RightTop.vue 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. <template>
  2. <div class="duty-card">
  3. <div class="common-title gradient-text">指挥动态</div>
  4. <ul class="tabs">
  5. <li v-for="(tab, index) in tabs" :key="index" :class="{ active: tab.id === activeTab }" @click="setActiveTab(tab.id)">
  6. {{ tab.label }}
  7. </li>
  8. </ul>
  9. <div class="card-content">
  10. <!-- 任务跟踪部分 -->
  11. <RenWuGenZong v-if="activeTab === '任务跟踪'" :event-id="eventId" />
  12. <!-- 预案通知部分修改为卡片式布局 -->
  13. <div v-else-if="activeTab === '预案通知'" class="preplan-notification">
  14. <div v-if="notifications.length > 0" class="table-content">
  15. <div v-for="(notification, index) in notifications" :key="index" class="box1">
  16. <div :class="notification.status === '发送失败' ? 'error-icon' : 'success-icon'"></div>
  17. <div class="box2">
  18. <div class="box-header">
  19. <div class="header-left">
  20. <div class="box-title">{{ notification.unit }}</div>
  21. <div class="time">{{ notification.date }}</div>
  22. <div :class="notification.status === '发送失败' ? 'status error' : 'status success'">
  23. {{ notification.status === '发送失败' ? '发送失败' : `接收人:${notification.receiver}` }}
  24. </div>
  25. </div>
  26. <!-- 如果需要更新按钮,可以取消注释以下代码 -->
  27. <!--
  28. <div class="btn" @click="openUpdateDialog(notification)">
  29. 更新
  30. </div>
  31. -->
  32. </div>
  33. <div class="box-content">{{ notification.content }}</div>
  34. <template v-if="notification.leaders && notification.leaders.length">
  35. <div class="box-content">赶赴现场人员:{{ notification.name1 }} ({{ notification.phone1 }})</div>
  36. <div class="box-content">指挥部值守人员:{{ notification.name2 }} ({{ notification.phone2 }})</div>
  37. </template>
  38. <div v-if="!!notification.comment" class="box-content2">领导批示:{{ notification.comment }}</div>
  39. </div>
  40. </div>
  41. </div>
  42. <div v-else class="tip">暂无数据</div>
  43. </div>
  44. <!-- 资源调度部分保持不变 -->
  45. <div v-else-if="activeTab === '资源调度'" class="resource-scheduling">
  46. <div class="tip">暂无数据</div>
  47. </div>
  48. <!-- 事件简报部分 -->
  49. <ShiJianJieBao v-if="activeTab === '事件简报'" :event-id="eventId" />
  50. </div>
  51. </div>
  52. </template>
  53. <script lang="ts" setup>
  54. import { ref, reactive } from 'vue';
  55. import { taskList } from '@/api/duty/eventing';
  56. import RenWuGenZong from '@/views/emergencyCommandMap/RightSection/RenWuGenZong.vue'; // 确保 eventing.ts 的路径正确
  57. import ShiJianJieBao from '@/views/emergencyCommandMap/RightSection/ShiJianJieBao.vue';
  58. const props = defineProps<{
  59. eventId?: string; // 使用可选属性
  60. }>();
  61. // 定义 tabs
  62. const tabs = reactive([
  63. { id: '任务跟踪', label: '任务跟踪' },
  64. { id: '预案通知', label: '预案通知' },
  65. { id: '资源调度', label: '资源调度' },
  66. { id: '事件简报', label: '事件简报' }
  67. ]);
  68. const activeTab = ref('任务跟踪');
  69. const setActiveTab = (id) => {
  70. activeTab.value = id;
  71. if (id === '预案通知') {
  72. fetchData();
  73. }
  74. };
  75. // 定义 notifications
  76. const notifications = ref([]);
  77. // 请求数据
  78. const fetchData = async () => {
  79. try {
  80. if (!props.eventId) {
  81. console.error('eventId 未找到');
  82. return;
  83. }
  84. const response = await taskList({ eventId: props.eventId });
  85. // const response = await taskList({ eventId: "YJSJ3295457527" });
  86. if (response.code === 200) {
  87. console.log('查询成功预案:', response.data);
  88. updateTaskList(response.data);
  89. } else {
  90. console.error('预案查询失败:', response.msg);
  91. }
  92. } catch (error) {
  93. console.error('请求失败:', error);
  94. }
  95. };
  96. // 更新任务列表
  97. const updateTaskList = (tasks) => {
  98. notifications.value = tasks.map((task) => ({
  99. unit: task.dept_name,
  100. date: task.sent_time,
  101. status: task.sent_status === 0 ? '暂未发送' : '已发送',
  102. content: task.yzy_content,
  103. comment: task.comment,
  104. receiver: task.nick_name, // 假设所有的任务都有接收者
  105. name1: getLeaderNames(task.leaders, '赶赴现场人员'), // 赶赴现场人员姓名
  106. phone1: getLeaderPhones(task.leaders, '赶赴现场人员'), // 赶赴现场人员电话
  107. name2: getLeaderNames(task.leaders, '指挥部值守人员'), // 指挥部值守人员姓名
  108. phone2: getLeaderPhones(task.leaders, '指挥部值守人员'), // 指挥部值守人员电话
  109. leaders: task.leaders // 保留原始 leaders 数据用于模板中的条件判断
  110. }));
  111. nextFetchData();
  112. };
  113. // 获取特定类型的领导人姓名
  114. const getLeaderNames = (leaders, userType) => {
  115. return (
  116. leaders
  117. .filter((leader) => leader.user_type === userType)
  118. .map((leader) => leader.user_name)
  119. .join('、') || '-'
  120. );
  121. };
  122. // 获取特定类型的领导人电话
  123. const getLeaderPhones = (leaders, userType) => {
  124. return (
  125. leaders
  126. .filter((leader) => leader.user_type === userType)
  127. .map((leader) => leader.mobile)
  128. .join('、') || '-'
  129. );
  130. };
  131. // 设置定时器
  132. const fetchInterval = process.env.NODE_ENV === 'development' ? 60000 : 1500; // 每60秒刷新一次(刷新太频繁影响调试)
  133. const nextFetchData = () => {
  134. setTimeout(() => {
  135. if (notifications.value.length === 0) {
  136. fetchData();
  137. }
  138. }, fetchInterval);
  139. };
  140. watch(
  141. () => props.eventId,
  142. () => {
  143. if (!!props.eventId) {
  144. fetchData();
  145. }
  146. },
  147. {
  148. immediate: true
  149. }
  150. );
  151. </script>
  152. <style lang="scss" scoped>
  153. .tabs {
  154. display: flex;
  155. justify-content: flex-start; /* 选项卡靠左对齐 */
  156. padding: 0;
  157. margin-left: 80px;
  158. margin-top: 75px;
  159. .active {
  160. background: url('@/assets/images/emergencyCommandMap/tabActive.png') no-repeat;
  161. }
  162. li {
  163. cursor: pointer;
  164. padding: 20px;
  165. font-size: 44px;
  166. color: #fff;
  167. width: 349px;
  168. height: 118px;
  169. background: url('@/assets/images/emergencyCommandMap/tab.png') no-repeat;
  170. display: flex;
  171. justify-content: center;
  172. align-items: flex-end;
  173. font-family: YouSheBiaoTiHei;
  174. &:hover {
  175. background: url('@/assets/images/emergencyCommandMap/tabActive.png') no-repeat;
  176. }
  177. }
  178. }
  179. .preplan-notification {
  180. width: 100%;
  181. .table-content {
  182. height: 550px;
  183. overflow-y: auto;
  184. .box1 {
  185. width: 100%;
  186. display: flex;
  187. align-items: center;
  188. &:last-child {
  189. margin-bottom: 0;
  190. }
  191. .success-icon,
  192. .error-icon {
  193. margin-right: -50px;
  194. width: 123px;
  195. height: 79px;
  196. background-repeat: no-repeat;
  197. background-size: contain;
  198. }
  199. .success-icon {
  200. background-image: url('@/assets/images/taskTracking/success.png');
  201. }
  202. .error-icon {
  203. background-image: url('@/assets/images/taskTracking/processing.png');
  204. }
  205. .box2 {
  206. flex: 1;
  207. background-image: url('@/assets/images/taskTracking/box1.png');
  208. background-repeat: no-repeat;
  209. background-size: 1642px 377px;
  210. background-position: bottom left;
  211. padding: 20px 20px 20px 60px;
  212. margin-top: 20px;
  213. position: relative;
  214. &:first-child {
  215. margin-top: 0;
  216. }
  217. .box-header {
  218. display: flex;
  219. justify-content: space-between;
  220. align-items: center;
  221. .header-left {
  222. display: flex;
  223. align-items: center; // 垂直居中对齐
  224. }
  225. .box-title {
  226. font-size: 44px;
  227. font-family: YouSheBiaoTiHei;
  228. color: #ffffff;
  229. background-image: url('@/assets/images/taskTracking/titleBox.png');
  230. background-repeat: no-repeat;
  231. background-size: 311px 56px;
  232. background-position: bottom left;
  233. padding-left: 50px;
  234. }
  235. .time {
  236. font-size: 32px;
  237. color: #00e8ff;
  238. margin-left: 70px;
  239. }
  240. .status {
  241. width: 354px;
  242. height: 56px;
  243. line-height: 56px;
  244. font-size: 32px;
  245. color: #ffffff;
  246. border-radius: 10px;
  247. text-align: center;
  248. margin-left: 30px;
  249. }
  250. .success {
  251. background-color: #38c95a;
  252. }
  253. .error {
  254. background-color: #ff4d4f;
  255. }
  256. }
  257. .box-content {
  258. color: #fff;
  259. width: 100%;
  260. font-size: 38px;
  261. line-height: 1.5;
  262. margin-top: 10px;
  263. }
  264. .box-content2 {
  265. color: #ff4d4f;
  266. width: 100%;
  267. font-size: 38px;
  268. line-height: 1.5;
  269. margin-top: 10px;
  270. }
  271. }
  272. }
  273. }
  274. }
  275. .duty-card {
  276. width: 1968px;
  277. height: 812px;
  278. background: url('@/assets/images/commandDynamic/dialog.png') no-repeat;
  279. position: relative;
  280. color: #fff;
  281. animation-name: slideRight;
  282. animation-duration: 1s;
  283. .card-content {
  284. display: flex;
  285. flex-wrap: wrap;
  286. padding: 0 0 0 80px;
  287. width: 100%;
  288. }
  289. }
  290. .resource-scheduling {
  291. width: 100%;
  292. display: flex;
  293. justify-content: center;
  294. align-items: center;
  295. font-size: 36px;
  296. color: #fff;
  297. }
  298. .tip {
  299. font-size: 44px;
  300. text-align: center;
  301. }
  302. </style>