StartPlan.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. <template>
  2. <div class="dialog">
  3. <div class="title gradient-text">启动预案</div>
  4. <div class="close-icon" @click="onClose"></div>
  5. <!-- 响应等级选择和操作按钮 -->
  6. <div class="section-box">
  7. <div class="title2">响应等级</div>
  8. <el-select v-model="selectedLevel" placeholder="请选择响应等级" class="custom-select" style="width: 1200px;margin-left: 30px">
  9. <el-option v-for="level in responseLevels" :key="level.value" :label="level.name" :value="level.value"></el-option>
  10. </el-select>
  11. <div class="btn" @click="onStartPlan">启动预案</div>
  12. <div class="btn" @click="onTaskDelivery">预案任务下发</div>
  13. </div>
  14. <div class="title-box">{{ planTitle }}</div>
  15. <div class="plan-content">
  16. <!-- 预案内容的标签页 -->
  17. <div class="tabs">
  18. <div v-for="(item, index) in tabs" :key="index" :class="index === activeTab ? 'tab tabActive' : 'tab'" @click="handleClickTab(index)">{{ item.label }}</div>
  19. </div>
  20. <div class="tab-content">
  21. <div class="tabs2">
  22. <div
  23. v-for="(item, index) in tabs[activeTab]?.children"
  24. :key="index"
  25. :class="index === activeTab2 ? 'tab tab-active2' : 'tab'"
  26. @click="handleClickTab2(index)"
  27. >
  28. <div class="text">{{ item.label }}</div>
  29. </div>
  30. </div>
  31. <div class="tab-content2">
  32. {{
  33. tabs[activeTab] && tabs[activeTab].children && tabs[activeTab].children[activeTab2] ? tabs[activeTab].children[activeTab2].content : ''
  34. }}
  35. </div>
  36. </div>
  37. </div>
  38. <TaskDelivery v-model="taskDeliveryState.show" :title="taskDeliveryState.title" :planId="planData.plan_id" :eventId="props.eventId" />
  39. </div>
  40. </template>
  41. <script lang="ts" setup>
  42. import { ref, watch, defineProps, defineEmits, reactive } from 'vue';
  43. import { ElMessage } from 'element-plus';
  44. import TaskDelivery from './TaskDelivery.vue';
  45. import { matchingPlan, launchPlan } from '@/api/duty/eventing';
  46. // 内部状态
  47. const planTitle = ref(''); // 预案名称
  48. const selectedLevel = ref(''); // 默认响应等级为空
  49. // 定义组件接收的属性类型
  50. interface Props {
  51. show: boolean;
  52. eventId: string;
  53. title: string;
  54. }
  55. // 初始化组件属性
  56. const props = defineProps<Props>();
  57. const responseLevels = [
  58. // 响应等级选项
  59. { value: '1', name: '特重大(I级)' },
  60. { value: '2', name: '重大(II级)' },
  61. { value: '3', name: '较大(III级)' },
  62. { value: '4', name: '一般(IV级)' }
  63. ];
  64. const planData = reactive({
  65. purpose: '',
  66. basis: '',
  67. scope: '',
  68. principles: '',
  69. plan_id: ''
  70. });
  71. let plan_id = '';
  72. const tabs = ref([
  73. {
  74. label: '总则',
  75. children: [
  76. { label: '编制目的', content: '编制目的内容' },
  77. { label: '编制依据', content: '编制依据内容' },
  78. { label: '适用范围', content: '适用范围内容' },
  79. { label: '工作原则', content: '工作原则内容' }
  80. ]
  81. },
  82. { label: '组织体系', children: [] },
  83. { label: '运行机制', children: [] },
  84. { label: '应急保障', children: [] },
  85. { label: '附则', children: [] },
  86. { label: '附件', children: [] }
  87. ]);
  88. const activeTab = ref(0); // 当前激活的标签页
  89. const activeTab2 = ref(0); // 当前激活的标签页
  90. const handleClickTab = (key) => {
  91. activeTab.value = key;
  92. };
  93. const handleClickTab2 = (key) => {
  94. activeTab2.value = key;
  95. };
  96. // 定义事件发射器
  97. const emit = defineEmits(['update:show']);
  98. // 对话框关闭时执行的操作
  99. const onClose = () => {
  100. emit('update:show', false);
  101. };
  102. // 处理启动预案的函数
  103. const onStartPlan = async () => {
  104. // 检查是否选择了响应等级
  105. if (!selectedLevel.value) {
  106. ElMessage.error('请先选择响应等级!');
  107. return;
  108. }
  109. // 收集数据
  110. const data = {
  111. response_level: selectedLevel.value,
  112. eventId: props.eventId,
  113. plan_id: planData.plan_id // 确保 planData 中包含正确的 plan_id
  114. };
  115. try {
  116. // 调用后端API启动预案
  117. const response = await launchPlan(data);
  118. if (response && response.code === 200) {
  119. ElMessage.success('预案启动成功!');
  120. } else if (response && response.code !== 200) {
  121. ElMessage.error(`预案启动失败,后端返回错误码:${response.code}`);
  122. }
  123. } catch (error) {
  124. console.error('启动预案时发生的错误:', error);
  125. if (error.response && error.response.data && error.response.data.message) {
  126. ElMessage.error(error.response.data.message);
  127. } else if (error.response && error.response.status) {
  128. ElMessage.error(`启动预案时发生错误,HTTP 状态码:${error.response.status}`);
  129. } else {
  130. ElMessage.error('启动预案时发生未知错误,请稍后再试。');
  131. }
  132. }
  133. };
  134. // 处理标签页点击事件
  135. const onTabClick = (tab: any, event: Event) => {
  136. // 实现标签页点击的逻辑
  137. };
  138. // 控制 taskDelivery 弹窗的显示状态
  139. const taskDeliveryState = reactive({
  140. show: ref(false),
  141. title: '' as string
  142. });
  143. // 处理预案任务下发内容的函数
  144. const onTaskDelivery = () => {
  145. taskDeliveryState.title = '预案任务下发';
  146. taskDeliveryState.show = true;
  147. };
  148. // 调用接口获取预案数据
  149. const fetchPlanData = async () => {
  150. try {
  151. if (!props.eventId) {
  152. ElMessage.warning('eventId 为空,无法获取预案数据');
  153. return;
  154. }
  155. const response = await matchingPlan({ eventId: props.eventId });
  156. if (response && response.code === 200) {
  157. const data = response.data;
  158. planTitle.value = `预案名称: ${data.plan_name}`;
  159. if (data.response_level) {
  160. const isValidLevel = responseLevels.some((level) => level.value === data.response_level);
  161. if (isValidLevel) {
  162. selectedLevel.value = data.response_level;
  163. } else {
  164. ElMessage.error('无效的响应等级,请检查数据');
  165. selectedLevel.value = '';
  166. }
  167. } else {
  168. selectedLevel.value = '';
  169. }
  170. planData.purpose = data.purpose || '这是内容...';
  171. planData.basis = data.basis || '这是内容...';
  172. planData.scope = data.scope || '这是内容...';
  173. planData.principles = data.principles || '这是内容...';
  174. planData.plan_id = data.plan_id; // 确保 planData 中包含 plan_id
  175. } else {
  176. ElMessage.error('未能从服务器获取有效的预案数据');
  177. }
  178. } catch (error) {
  179. ElMessage.error('获取预案数据失败');
  180. }
  181. };
  182. // 在组件挂载时尝试获取预案数据
  183. onMounted(() => {
  184. if (props.eventId) {
  185. fetchPlanData();
  186. }
  187. });
  188. // 监听 eventId 变化
  189. watch(
  190. () => props.eventId,
  191. (newEventId) => {
  192. if (newEventId) {
  193. fetchPlanData();
  194. }
  195. }
  196. );
  197. </script>
  198. <style scoped>
  199. .dialog {
  200. position: absolute;
  201. top: 478px;
  202. left: 50%;
  203. transform: translateX(-50%);
  204. width: 2839px;
  205. height: 1263px;
  206. background: url('@/assets/images/plan/dialog.png') no-repeat;
  207. padding-top: 200px;
  208. color: #fff;
  209. font-size: 36px;
  210. .title {
  211. position: absolute;
  212. top: 30px;
  213. left: 55px;
  214. font-size: 80px;
  215. }
  216. .close-icon {
  217. position: absolute;
  218. top: 0px;
  219. right: 0px;
  220. width: 75px;
  221. height: 70px;
  222. background: url('@/assets/images/map/rightMenu/close.png') no-repeat;
  223. background-size: 100% 100%;
  224. cursor: pointer;
  225. }
  226. .section-box {
  227. display: flex;
  228. align-items: center;
  229. padding: 15px 60px;
  230. border: 1px solid #16448c;
  231. .btn {
  232. width: 440px;
  233. height: 120px;
  234. background: url('@/assets/images/plan/btn.png') no-repeat;
  235. cursor: pointer;
  236. display: flex;
  237. align-items: center;
  238. justify-content: center;
  239. }
  240. }
  241. .title-box {
  242. background-image: url('@/assets/images/plan/titleBox.png');
  243. background-repeat: no-repeat;
  244. background-size: 354px 28px;
  245. background-position: bottom left;
  246. margin: 40px 60px 0;
  247. font-family: 'YouSheBiaoTiHei';
  248. font-size: 56px;
  249. padding-left: 50px;
  250. }
  251. }
  252. h2 {
  253. text-align: center;
  254. }
  255. /* 样式 */
  256. .plan-content {
  257. display: flex;
  258. flex-direction: column;
  259. margin: 40px 60px;
  260. }
  261. .button-container {
  262. display: flex;
  263. justify-content: flex-end;
  264. }
  265. .content-row {
  266. margin-top: 20px;
  267. }
  268. .tabs {
  269. display: flex;
  270. width: 100%;
  271. overflow-x: auto;
  272. .tab {
  273. width: 350px;
  274. height: 78px;
  275. background: url('@/assets/images/plan/tab.png') no-repeat;
  276. display: flex;
  277. justify-content: center;
  278. align-items: flex-end;
  279. margin-left: 50px;
  280. font-family: 'YouSheBiaoTiHei';
  281. font-size: 38px;
  282. cursor: pointer;
  283. &:hover {
  284. background: url('@/assets/images/plan/tabActive.png') no-repeat;
  285. }
  286. &:first-child {
  287. margin-left: 0;
  288. }
  289. }
  290. .tabActive {
  291. background: url('@/assets/images/plan/tabActive.png') no-repeat;
  292. }
  293. }
  294. .tab-content {
  295. display: flex;
  296. margin-top: 30px;
  297. .tabs2 {
  298. .tab {
  299. width: 399px;
  300. height: 70px;
  301. background: url('@/assets/images/plan/tab2.png') no-repeat;
  302. display: flex;
  303. align-items: center;
  304. padding-left: 76px;
  305. color: #9badc4;
  306. font-size: 36px;
  307. font-weight: bold;
  308. margin-top: 60px;
  309. position: relative;
  310. cursor: pointer;
  311. &::before {
  312. content: '';
  313. width: 2px;
  314. height: 68px;
  315. background: #2b88bd;
  316. position: absolute;
  317. top: -63px;
  318. left: 42px;
  319. }
  320. &:first-child {
  321. margin-top: 0;
  322. &::before {
  323. display: none;
  324. }
  325. }
  326. &:hover {
  327. width: 439px;
  328. background: url('@/assets/images/plan/tab2Active.png') no-repeat;
  329. .text {
  330. /* 设置字体透明 */
  331. color: transparent;
  332. /* 设置线性渐变,从红色渐变到蓝色 */
  333. background-image: linear-gradient(to bottom, #fff 40%, #5CC4FA 50%, #40A2E7 100%);
  334. /* 使用 -webkit-background-clip 属性将背景剪裁至文本形状 */
  335. -webkit-background-clip: text;
  336. /* 非Webkit内核浏览器需要使用标准前缀 */
  337. background-clip: text;
  338. /* 把当前元素设置为行内块,以便能够应用背景 */
  339. display: inline-block;
  340. }
  341. }
  342. }
  343. .tab-active2 {
  344. width: 439px;
  345. background: url('@/assets/images/plan/tab2Active.png') no-repeat;
  346. .text {
  347. /* 设置字体透明 */
  348. color: transparent;
  349. /* 设置线性渐变,从红色渐变到蓝色 */
  350. background-image: linear-gradient(to bottom, #fff 40%, #5CC4FA 50%, #40A2E7 100%);
  351. /* 使用 -webkit-background-clip 属性将背景剪裁至文本形状 */
  352. -webkit-background-clip: text;
  353. /* 非Webkit内核浏览器需要使用标准前缀 */
  354. background-clip: text;
  355. /* 把当前元素设置为行内块,以便能够应用背景 */
  356. display: inline-block;
  357. }
  358. }
  359. }
  360. .tab-content2 {
  361. height: 620px;
  362. overflow-y: auto;
  363. }
  364. }
  365. </style>