WarningInfo.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. <template>
  2. <div class="menu-content">
  3. <div class="warning-info">
  4. <div class="gradient-text title">预警信号</div>
  5. <!-- 当前时间显示 -->
  6. <div class="current-time" :style="{ backgroundImage: `url(${dateTimeBg})` }">日期 {{ formattedTime }}</div>
  7. <!-- 生效预警列表 -->
  8. <div class="alert-boxes">
  9. <div class="section-title">生效预警</div>
  10. <div class="alert-items">
  11. <div v-if="activeAlerts.some((alert) => alert.type === '台风')" class="data-box1">
  12. <img src="@/assets/images/map/warningInfo/by_1.png" alt="台风图标" class="alert-icon" />
  13. <div class="box-text1">台风</div>
  14. </div>
  15. <div v-if="activeAlerts.some((alert) => alert.type === '暴雨预警')" class="data-box2">
  16. <img src="@/assets/images/map/warningInfo/by_2.png" alt="暴雨图标" class="alert-icon" />
  17. <div class="box-text1">暴雨</div>
  18. </div>
  19. <div v-if="activeAlerts.some((alert) => alert.type === '雷雨大风')" class="data-box3">
  20. <img src="@/assets/images/map/warningInfo/by_3.png" alt="雷雨大风图标" class="alert-icon" />
  21. <div class="box-text1">雷雨大风</div>
  22. </div>
  23. <div v-if="activeAlerts.some((alert) => alert.type === '高温')" class="data-box4">
  24. <img src="@/assets/images/map/warningInfo/by_4.png" alt="高温图标" class="alert-icon" />
  25. <div class="box-text1">高温</div>
  26. </div>
  27. </div>
  28. </div>
  29. <!-- 发布数量统计 -->
  30. <div class="alert-count">
  31. <h3 class="section-title">发布数量统计</h3>
  32. <div class="alert-count-items">
  33. <img src="@/assets/images/map/warningInfo/ic_发布数量统计.png" alt="发布数量统计" class="count-icon" />
  34. <div v-for="(count, index) in alertCounts" :key="index" class="count-item">
  35. <span class="count-num">{{ count.num }}</span>
  36. <img :src="getLevelImage(count.level)" :alt="`${getLevelName(count.level)}预警图标`" class="count-icon" />
  37. <span
  38. :class="['count-level', `level-${getLevelName(count.level).toLowerCase()}`]"
  39. :style="{ color: index === alertCounts.length - 1 ? 'blue' : '#fdfcfc' }"
  40. >
  41. {{ getLevelName(count.level) }}预警
  42. </span>
  43. </div>
  44. </div>
  45. </div>
  46. <!-- 预警明细列表 -->
  47. <div class="alert-details">
  48. <table class="alert-table">
  49. <thead>
  50. <tr>
  51. <th class="dropdown-trigger">
  52. <el-select
  53. v-model="selectedType"
  54. placeholder="类型"
  55. size="large"
  56. class="custom-select2"
  57. popper-class="custom-select-popper2"
  58. :teleported="false"
  59. >
  60. <el-option label="全部" value="" />
  61. <el-option v-for="type in alertTypes" :key="type" :label="type" :value="type" />
  62. </el-select>
  63. </th>
  64. <th>
  65. <el-select
  66. v-model="selectedLevel"
  67. placeholder="等级"
  68. size="large"
  69. class="custom-select2"
  70. popper-class="custom-select-popper2"
  71. :teleported="false"
  72. >
  73. <el-option label="全部" value="" />
  74. <el-option v-for="level in alertLevels" :key="level" :label="level" :value="level" />
  75. </el-select>
  76. </th>
  77. <th>区域</th>
  78. <th>发布时间</th>
  79. </tr>
  80. </thead>
  81. <tbody>
  82. <tr v-for="(alert, index) in alertDetails" :key="index" class="alert-row">
  83. <td>{{ alert.type }}</td>
  84. <td>
  85. <div :class="['level-icon', alert.level.toLowerCase()]">{{ alert.level }}</div>
  86. </td>
  87. <td>{{ alert.area }}</td>
  88. <td>{{ alert.release_time }}</td>
  89. </tr>
  90. </tbody>
  91. </table>
  92. </div>
  93. </div>
  94. </div>
  95. </template>
  96. <script lang="ts">
  97. import { defineComponent, ref, onMounted } from 'vue';
  98. import { getWarningInfoList, getWarningInfoCount, getWarningInfoDetail } from '@/api/globalMap/warningInfo';
  99. import redWarningImg from '@/assets/images/map/warningInfo/bg_1预警.png';
  100. import orangeWarningImg from '@/assets/images/map/warningInfo/bg_2预警.png';
  101. import yellowWarningImg from '@/assets/images/map/warningInfo/bg_3预警.png';
  102. import blueWarningImg from '@/assets/images/map/warningInfo/bg_4预警.png';
  103. import whiteWarningImg from '@/assets/images/map/warningInfo/bg_5预警.png';
  104. import dateTimeBg from '@/assets/images/map/warningInfo/bg_日期时间.png';
  105. export default defineComponent({
  106. name: 'WarningInfo',
  107. setup() {
  108. const alertCounts = ref<any[]>([]);
  109. const activeAlerts = ref<any[]>([]);
  110. const alertDetails = ref<any[]>([]);
  111. const alertTypes = ref(['台风', '暴雨预警', '雷雨大风', '高温']);
  112. const alertLevels = ref(['红色', '橙色', '黄色', '蓝色', '白色']);
  113. const selectedType = ref<string>(''); // 用户选择的预警类型
  114. const selectedLevel = ref<string>(''); // 用户选择的预警等级
  115. const showTypeDropdown = ref<boolean>(false); // 控制类型下拉菜单的显示
  116. const showLevelDropdown = ref<boolean>(false); // 控制等级下拉菜单的显示
  117. const loading = ref(false);
  118. const errorMessage = ref('');
  119. // 预警等级与图片的映射
  120. const levelImages: { [key: string]: string } = {
  121. '1': redWarningImg,
  122. '2': orangeWarningImg,
  123. '3': yellowWarningImg,
  124. '4': blueWarningImg,
  125. '5': whiteWarningImg
  126. };
  127. // 预警等级与中文名称的映射
  128. const levelNames: { [key: string]: string } = {
  129. '1': '红色',
  130. '2': '橙色',
  131. '3': '黄色',
  132. '4': '蓝色',
  133. '5': '白色'
  134. };
  135. const getLevelImage = (level: string) => {
  136. return levelImages[level] || '';
  137. };
  138. // 获取预警等级的中文名称
  139. const getLevelName = (level: string) => {
  140. return levelNames[level] || '未知';
  141. };
  142. // 获取发布数量统计
  143. const fetchAlertCounts = async () => {
  144. try {
  145. const response = await getWarningInfoCount();
  146. alertCounts.value = response.rows || [];
  147. console.log('alertCounts:', alertCounts.value);
  148. } catch (error) {
  149. console.error('获取发布数量统计失败:', error);
  150. errorMessage.value = '获取发布数量统计失败';
  151. }
  152. };
  153. // 获取生效预警列表
  154. const fetchActiveAlerts = async () => {
  155. try {
  156. const response = await getWarningInfoList();
  157. activeAlerts.value = response.rows || [];
  158. } catch (error) {
  159. console.error('获取生效预警列表失败:', error);
  160. errorMessage.value = '获取生效预警列表失败';
  161. }
  162. };
  163. // 获取预警明细列表
  164. const fetchAlertDetails = async () => {
  165. try {
  166. const query = {
  167. type: selectedType.value,
  168. level: selectedLevel.value
  169. };
  170. const response = await getWarningInfoDetail({ query });
  171. alertDetails.value = response.rows || [];
  172. } catch (error) {
  173. console.error('获取预警明细列表失败:', error);
  174. errorMessage.value = '获取预警明细列表失败';
  175. }
  176. };
  177. // 控制类型下拉菜单的显示
  178. const toggleTypeDropdown = () => {
  179. showTypeDropdown.value = !showTypeDropdown.value;
  180. showLevelDropdown.value = false; // 关闭等级下拉菜单
  181. };
  182. // 控制等级下拉菜单的显示
  183. const toggleLevelDropdown = () => {
  184. showLevelDropdown.value = !showLevelDropdown.value;
  185. showTypeDropdown.value = false; // 关闭类型下拉菜单
  186. };
  187. // 选择类型
  188. const selectType = (type: string) => {
  189. selectedType.value = type;
  190. showTypeDropdown.value = false;
  191. fetchAlertDetails();
  192. };
  193. // 选择等级
  194. const selectLevel = (level: string) => {
  195. selectedLevel.value = level;
  196. showLevelDropdown.value = false;
  197. fetchAlertDetails();
  198. };
  199. // 点击外部区域时关闭下拉菜单
  200. const wrapper = ref<HTMLElement | null>(null);
  201. onMounted(async () => {
  202. loading.value = true;
  203. await Promise.all([fetchAlertCounts(), fetchActiveAlerts(), fetchAlertDetails()]);
  204. loading.value = false;
  205. });
  206. // 监听点击事件以关闭下拉菜单
  207. const handleClickOutside = (event: MouseEvent) => {
  208. const target = event.target as HTMLElement;
  209. if (wrapper.value && !wrapper.value.contains(target)) {
  210. showTypeDropdown.value = false;
  211. showLevelDropdown.value = false;
  212. }
  213. };
  214. window.addEventListener('click', handleClickOutside);
  215. // 清理事件监听器
  216. onUnmounted(() => {
  217. window.removeEventListener('click', handleClickOutside);
  218. });
  219. // 当前时间相关逻辑
  220. const currentTime = ref(new Date());
  221. const formattedTime = computed(() => {
  222. const date = currentTime.value;
  223. const month = date.getMonth() + 1;
  224. const day = date.getDate();
  225. const hour = date.getHours();
  226. const minute = date.getMinutes();
  227. // 格式化为 "MM月DD日 HH时MM分"
  228. return `${month}月${day}日 ${hour.toString().padStart(2, '0')}时${minute.toString().padStart(2, '0')}分`;
  229. });
  230. onMounted(() => {
  231. // 每分钟更新一次时间
  232. const interval = setInterval(() => {
  233. currentTime.value = new Date();
  234. }, 60000); // 60000 毫秒 = 1 分钟
  235. // 清除定时器
  236. onUnmounted(() => {
  237. clearInterval(interval);
  238. });
  239. });
  240. return {
  241. alertCounts,
  242. activeAlerts,
  243. alertDetails,
  244. alertTypes,
  245. alertLevels,
  246. selectedType,
  247. selectedLevel,
  248. loading,
  249. errorMessage,
  250. fetchAlertDetails,
  251. getLevelImage,
  252. getLevelName,
  253. toggleTypeDropdown,
  254. toggleLevelDropdown,
  255. selectType,
  256. selectLevel,
  257. showTypeDropdown,
  258. showLevelDropdown,
  259. wrapper,
  260. formattedTime,
  261. dateTimeBg
  262. };
  263. }
  264. });
  265. </script>
  266. <style lang="scss" scoped>
  267. .menu-content {
  268. width: 1579px;
  269. height: 1394px;
  270. background: url('@/assets/images/map/rightMenu/content.png') no-repeat;
  271. padding: 130px 20px 20px 20px;
  272. font-size: 36px;
  273. position: relative;
  274. color: #000000;
  275. }
  276. .title {
  277. font-size: 60px;
  278. position: absolute;
  279. top: 30px;
  280. left: 160px;
  281. }
  282. .warning-info {
  283. padding: 20px;
  284. display: flex;
  285. flex-direction: column; /* 垂直排列标题和预警图标 */
  286. }
  287. .current-time {
  288. width: 800px; /* 根据背景图的宽度调整 */
  289. height: 150px; /* 根据背景图的高度调整 */
  290. background-size: cover; /* 背景图覆盖整个盒子 */
  291. background-repeat: no-repeat; /* 禁止背景图重复 */
  292. background-position: center; /* 背景图居中 */
  293. color: #fff;
  294. font-size: 38px; /* 根据需要调整字体大小 */
  295. display: flex;
  296. align-items: center;
  297. justify-content: center;
  298. margin-bottom: 20px; /* 与下方内容的间距 */
  299. }
  300. .alert-boxes {
  301. display: flex;
  302. flex-direction: column; /* 纵向排列子元素 */
  303. }
  304. .section-title {
  305. font-size: 44px;
  306. color: #f4f7fa;
  307. background: url('@/assets/images/map/warningInfo/bg_2level_title.png') no-repeat;
  308. padding-left: 65px;
  309. height: 54px;
  310. display: flex;
  311. align-items: center; /* 垂直居中内容 */
  312. margin-bottom: 20px; /* 标题与图标之间的间距 */
  313. }
  314. .level-white {
  315. color: blue;
  316. }
  317. .alert-items {
  318. display: flex;
  319. justify-content: space-between; /* 水平排列 */
  320. align-items: flex-start; /* 图标与格子对齐顶部 */
  321. flex-wrap: wrap; /* 如果内容超出宽度,则换行 */
  322. margin-top: 20px;
  323. }
  324. .data-box1,
  325. .data-box2,
  326. .data-box3,
  327. .data-box4 {
  328. width: 350px; /* 每个盒子的宽度 */
  329. height: 260px; /* 每个盒子的高度 */
  330. background:
  331. url('@/assets/images/map/warningInfo/bg_未选中.png') no-repeat,
  332. url('@/assets/images/map/warningInfo/ic_未选中.png') no-repeat;
  333. /* 第一个背景自适应,第二个背景设置为特定大小 */
  334. background-size:
  335. contain,
  336. 40px 40px; /* 第一个背景自适应,第二个为40x40像素 */
  337. background-position:
  338. center,
  339. calc(100% - 20px) calc(100% - 20px); /* 第一个背景居中,第二个背景向左下偏移10px */
  340. display: flex;
  341. flex-direction: column;
  342. align-items: center;
  343. justify-content: flex-start; /* 图标与文字对齐顶部 */
  344. margin-right: 10px; /* 添加间距 */
  345. position: relative; /* 为内部定位做准备 */
  346. }
  347. .alert-icon {
  348. // 向左偏移 50% 的宽度,使图标居中
  349. margin-left: -50px;
  350. margin-top: 30px; /* 根据需要调整位置 */
  351. }
  352. .box-text1 {
  353. color: #fff;
  354. font-size: 36px;
  355. // 向左偏移 50% 的宽度,使文字居中
  356. margin-left: -50px;
  357. margin-top: auto; /* 将文字推到图片下方 */
  358. margin-bottom: 50px; /* 根据需要调整文字与底部的间距 */
  359. }
  360. .alert-count {
  361. margin-top: 20px;
  362. }
  363. .alert-count-items {
  364. background: url('@/assets/images/map/warningInfo/bg_底座.png') no-repeat;
  365. background-size: cover;
  366. gap: 0; /* 可选:添加间距 */
  367. padding: 20px; /* 根据需要调整内边距 */
  368. display: flex; /* 水平排列发布数量统计 */
  369. position: relative; /* 用于设置子元素的位置 */
  370. }
  371. .count-item {
  372. display: flex;
  373. flex-direction: column;
  374. align-items: center;
  375. width: 120px; /* 根据需要调整宽度 */
  376. margin: 0; /* 移除外边距 */
  377. padding: 0; /* 移除内边距 */
  378. }
  379. .count-icon {
  380. width: 120px;
  381. height: 120px;
  382. margin: 0; /* 移除外边距 */
  383. padding: 0; /* 移除内边距 */
  384. display: block; /* 确保图片以块级元素显示,避免因行内元素的空白字符产生间隔 */
  385. }
  386. .count-num {
  387. font-size: 40px; /* 根据需要调整字体大小 */
  388. color: #fdfcfc;
  389. position: absolute; /* 相对于父元素定位 */
  390. }
  391. .count-level {
  392. font-size: 26px; /* 根据需要调整字体大小 */
  393. top: 100px; /* 根据需要调整位置 */
  394. color: #fdfcfc;
  395. position: absolute;
  396. }
  397. .alert-details {
  398. margin-top: 20px;
  399. position: relative; /* 为了下拉菜单的绝对定位 */
  400. }
  401. .alert-table {
  402. width: 100%;
  403. border-collapse: collapse;
  404. }
  405. .alert-table th {
  406. color: #fff; /* 确保表头文字颜色 */
  407. background: url('@/assets/images/map/warningInfo/bg_表头.png') no-repeat center center; /* 表头背景图 */
  408. background-size: cover; /* 背景图覆盖整行 */
  409. padding: 16px; /* 根据需要调整内边距 */
  410. position: relative; /* 为下拉菜单的绝对定位做准备 */
  411. }
  412. .alert-table th,
  413. .alert-table td {
  414. padding: 8px;
  415. text-align: left;
  416. }
  417. .alert-table .alert-row {
  418. background: url('@/assets/images/map/warningInfo/bg_列.png') no-repeat center center; /* 行的背景图 */
  419. background-size: cover; /* 背景图覆盖整行 */
  420. transition: background 0.3s ease; /* 添加过渡效果 */
  421. }
  422. /* 悬停效果 */
  423. .alert-table .alert-row:hover {
  424. background: url('@/assets/images/map/warningInfo/bg_列_选中.png') no-repeat center center; /* 悬停效果 */
  425. background-size: cover; /* 背景图覆盖整行 */
  426. }
  427. /* 确保文字可读性 */
  428. .alert-table td {
  429. color: #fff; /* 确保文字颜色与背景图对比明显 */
  430. }
  431. .dropdown-icon {
  432. width: 12px; /* 根据需要调整图标大小 */
  433. height: 12px; /* 根据需要调整图标大小 */
  434. margin-left: 5px; /* 图标与文字之间的间距 */
  435. }
  436. .dropdown-trigger {
  437. cursor: pointer;
  438. position: relative;
  439. }
  440. .dropdown-menu {
  441. position: absolute;
  442. top: 100%; /* 下拉菜单显示在触发器下方 */
  443. left: 0;
  444. background-color: #0c0000; /* 背景颜色 */
  445. border: 1px solid #ccc; /* 边框 */
  446. border-radius: 4px;
  447. z-index: 20;
  448. width: 150px; /* 根据需要调整宽度 */
  449. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); /* 添加阴影 */
  450. }
  451. .dropdown-item {
  452. padding: 8px 12px;
  453. cursor: pointer;
  454. }
  455. .dropdown-item:hover,
  456. .dropdown-item.selected {
  457. background-color: #f0f0f0;
  458. }
  459. @media (max-width: 768px) {
  460. .alert-items {
  461. flex-direction: column;
  462. }
  463. .data-box1,
  464. .data-box2,
  465. .data-box3,
  466. .data-box4 {
  467. flex: none;
  468. width: 100%;
  469. margin-bottom: 10px;
  470. }
  471. .title {
  472. position: relative;
  473. left: 0;
  474. text-align: center;
  475. }
  476. .count-item {
  477. width: 100%;
  478. max-width: 200px;
  479. }
  480. .alert-table th,
  481. .alert-table td {
  482. background: none; /* 移除背景图以适应移动设备 */
  483. }
  484. .alert-table td:hover {
  485. background: none;
  486. }
  487. }
  488. </style>