WarningInfo.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. <template>
  2. <div class="menu-content">
  3. <div class="warning-info">
  4. <div class="gradient-text common-dialog-title2">预警信号</div>
  5. <!-- 当前时间显示 -->
  6. <div class="current-time">
  7. <div>日期</div>
  8. <div class="line" />
  9. <div class="gradient-text3 text1">{{ formattedTime.month }}</div>
  10. <div>月</div>
  11. <div class="gradient-text3 text1">{{ formattedTime.day }}</div>
  12. <div>日</div>
  13. <div class="gradient-text3 text2" style="margin-left: 12px">{{ formattedTime.hour }}</div>
  14. <div class="gradient-text3 text2">:</div>
  15. <div class="gradient-text3 text2">{{ formattedTime.minute }}</div>
  16. </div>
  17. <!-- 生效预警列表 -->
  18. <div class="alert-boxes">
  19. <div class="common-title-box">生效预警</div>
  20. <div class="alert-items">
  21. <div v-for="(item, index) in activeAlerts" :key="index" class="data-box1" @click="handleSelect(item)">
  22. <img v-if="item.type === '台风'" src="@/assets/images/map/warningInfo/by_1.png" class="alert-icon" />
  23. <img v-else-if="item.type === '暴雨预警'" src="@/assets/images/map/warningInfo/by_2.png" class="alert-icon" />
  24. <img v-else-if="item.type === '雷雨大风'" src="@/assets/images/map/warningInfo/by_3.png" class="alert-icon" />
  25. <img v-else-if="item.type === '高温'" src="@/assets/images/map/warningInfo/by_4.png" class="alert-icon" />
  26. <div class="box-text1">{{ item.type }}</div>
  27. <div :class="item.type === selectedType ? 'checked1' : 'checked2'" />
  28. </div>
  29. </div>
  30. </div>
  31. <!-- 发布数量统计 -->
  32. <div class="alert-count">
  33. <h3 class="common-title-box">发布数量统计</h3>
  34. <div class="alert-count-items">
  35. <img src="../../../assets/images/map/warningInfo/ic_number_releases.png" alt="发布数量统计" class="count-icon" />
  36. <div v-for="(count, index) in alertCounts" :key="index" class="count-item">
  37. <span :class="getTextClass(count.num)">{{ count.num }}</span>
  38. <img :src="getLevelImage(count.level)" :alt="`${getLevelName(count.level)}预警图标`" class="count-icon" />
  39. <span
  40. :class="['count-level', `level-${getLevelName(count.level).toLowerCase()}`]"
  41. :style="{ color: index === alertCounts.length - 1 ? 'blue' : '#fdfcfc' }"
  42. >
  43. {{ getLevelName(count.level) }}预警
  44. </span>
  45. </div>
  46. </div>
  47. </div>
  48. <!-- 预警明细列表 -->
  49. <div class="alert-details">
  50. <div class="common-table">
  51. <div class="table-header">
  52. <div class="td">
  53. <el-select
  54. v-model="selectedType"
  55. placeholder="类型"
  56. class="custom-select2"
  57. popper-class="custom-select-popper2"
  58. :teleported="false"
  59. style="width: 100px"
  60. @change="fetchAlertDetails"
  61. >
  62. <el-option label="全部" value="" />
  63. <el-option v-for="(item, index) in activeAlerts" :key="index" :label="item.type" :value="item.type" />
  64. </el-select>
  65. </div>
  66. <div class="td">
  67. <el-select
  68. v-model="selectedLevel"
  69. placeholder="等级"
  70. size="large"
  71. class="custom-select2"
  72. popper-class="custom-select-popper2"
  73. :teleported="false"
  74. style="width: 80px"
  75. @change="fetchAlertDetails"
  76. >
  77. <el-option label="全部" value="" />
  78. <el-option v-for="item in alertLevels" :key="item.value" :label="item.name" :value="item.value" />
  79. </el-select>
  80. </div>
  81. <div class="td">区域</div>
  82. <div class="td">发布时间</div>
  83. </div>
  84. <div v-for="(item, index) in alertDetails" :key="index">
  85. <div class="tr">
  86. <div class="td">{{ item.type }}</div>
  87. <div :class="['level-icon', 'td', item.level.toLowerCase()]">
  88. <img :src="getWarningImage(item.type, item.level)" style="height: 60px"/>
  89. </div>
  90. <div class="td">{{ item.area }}</div>
  91. <div class="td">{{ item.release_time }}</div>
  92. </div>
  93. </div>
  94. </div>
  95. </div>
  96. </div>
  97. </div>
  98. </template>
  99. <script lang="ts" setup name="WarningInfo">
  100. import { getWarningInfoList, getWarningInfoCount, getWarningInfoDetail } from '@/api/globalMap/warningInfo';
  101. import redWarningImg from '@/assets/images/map/warningInfo/bg_1_warning.png';
  102. import orangeWarningImg from '@/assets/images/map/warningInfo/bg_2_warning.png';
  103. import yellowWarningImg from '@/assets/images/map/warningInfo/bg_3_warning.png';
  104. import blueWarningImg from '@/assets/images/map/warningInfo/bg_4_warning.png';
  105. import whiteWarningImg from '@/assets/images/map/warningInfo/bg_5_warning.png';
  106. import rainstormYellowImg from '@/assets/images/map/warningInfo/ic_rainstorm_yellow.png';
  107. import rainstormWhiteImg from '@/assets/images/map/warningInfo/ic_rainstorm_white.png';
  108. import rainstormRedImg from '@/assets/images/map/warningInfo/ic_rainstorm_red.png';
  109. import rainstormOrangeImg from '@/assets/images/map/warningInfo/ic_rainstorm_orange.png';
  110. import rainstormBlueImg from '@/assets/images/map/warningInfo/ic_rainstorm_blue.png';
  111. import temperatureBlue from '@/assets/images/map/warningInfo/ic_temperature_blue.png';
  112. import temperatureOrange from '@/assets/images/map/warningInfo/ic_temperature_orange.png';
  113. import temperatureRed from '@/assets/images/map/warningInfo/ic_temperature_red.png';
  114. import temperatureWhite from '@/assets/images/map/warningInfo/ic_temperature_white.png';
  115. import temperatureYellow from '@/assets/images/map/warningInfo/ic_temperature_yellow.png';
  116. import thunderStormBlue from '@/assets/images/map/warningInfo/ic_thunderstorm_blue.png';
  117. import thunderStormOrange from '@/assets/images/map/warningInfo/ic_thunderstorm_orange.png';
  118. import thunderStormRed from '@/assets/images/map/warningInfo/ic_thunderstorm_red.png';
  119. import thunderStormWhite from '@/assets/images/map/warningInfo/ic_thunderstorm_white.png';
  120. import thunderStormYellow from '@/assets/images/map/warningInfo/ic_thunderstorm_yellow.png';
  121. import typhoonBlue from '@/assets/images/map/warningInfo/ic_typhoon_blue.png';
  122. import typhoonOrange from '@/assets/images/map/warningInfo/ic_typhoon_orange.png';
  123. import typhoonRed from '@/assets/images/map/warningInfo/ic_typhoon_red.png';
  124. import typhoonWhite from '@/assets/images/map/warningInfo/ic_typhoon_white.png';
  125. import typhoonYellow from '@/assets/images/map/warningInfo/ic_typhoon_yellow.png';
  126. const alertCounts = ref<any[]>([]);
  127. const activeAlerts = ref<any[]>([]);
  128. const alertDetails = ref<any[]>([]);
  129. const alertLevels = ref([
  130. { name: '红色', value: '5' },
  131. { name: '橙色', value: '4' },
  132. { name: '黄色', value: '3' },
  133. { name: '蓝色', value: '2' },
  134. { name: '白色', value: '1' },
  135. ]);
  136. const selectedType = ref<string>(''); // 用户选择的预警类型
  137. const selectedLevel = ref<string>(''); // 用户选择的预警等级
  138. const showTypeDropdown = ref<boolean>(false); // 控制类型下拉菜单的显示
  139. const showLevelDropdown = ref<boolean>(false); // 控制等级下拉菜单的显示
  140. const loading = ref(false);
  141. const errorMessage = ref('');
  142. // 预警等级与图片的映射
  143. const levelImages: { [key: string]: string } = {
  144. '5': redWarningImg,
  145. '4': orangeWarningImg,
  146. '3': yellowWarningImg,
  147. '2': blueWarningImg,
  148. '1': whiteWarningImg
  149. };
  150. // 预警等级与中文名称的映射
  151. const levelNames: { [key: string]: string } = {
  152. '5': '红色',
  153. '4': '橙色',
  154. '3': '黄色',
  155. '2': '蓝色',
  156. '1': '白色'
  157. };
  158. const getTextClass = (value) => {
  159. if (value === '5') {
  160. return 'count-num text-red';
  161. } else if (value === '4') {
  162. return 'count-num text-orange';
  163. } else if (value === '3') {
  164. return 'count-num text-yellow';
  165. } else if (value === '2') {
  166. return 'count-num text-blue';
  167. } else {
  168. return 'count-num';
  169. }
  170. };
  171. const getLevelImage = (level: string) => {
  172. return levelImages[level] || '';
  173. };
  174. const levelImages2 = {
  175. '暴雨预警': {
  176. '5': rainstormRedImg,
  177. '4': rainstormOrangeImg,
  178. '3': rainstormYellowImg,
  179. '2': rainstormBlueImg,
  180. '1': rainstormWhiteImg
  181. },
  182. '高温': {
  183. '5': temperatureRed,
  184. '4': temperatureOrange,
  185. '3': temperatureYellow,
  186. '2': temperatureBlue,
  187. '1': temperatureWhite
  188. },
  189. '雷雨大风': {
  190. '5': thunderStormRed,
  191. '4': thunderStormOrange,
  192. '3': thunderStormYellow,
  193. '2': thunderStormBlue,
  194. '1': thunderStormWhite
  195. },
  196. '台风': {
  197. '5': typhoonRed,
  198. '4': typhoonOrange,
  199. '3': typhoonYellow,
  200. '2': typhoonBlue,
  201. '1': typhoonWhite
  202. }
  203. };
  204. const getWarningImage = (type, level) => {
  205. const warningImage = levelImages2[type];
  206. return warningImage ? warningImage[level] : null;
  207. };
  208. // 获取预警等级的中文名称
  209. const getLevelName = (level: string) => {
  210. return levelNames[level] || '未知';
  211. };
  212. // 获取发布数量统计
  213. const fetchAlertCounts = async () => {
  214. try {
  215. const response = await getWarningInfoCount();
  216. alertCounts.value = response.rows.reverse() || [];
  217. console.log('alertCounts:', alertCounts.value);
  218. } catch (error) {
  219. console.error('获取发布数量统计失败:', error);
  220. errorMessage.value = '获取发布数量统计失败';
  221. }
  222. };
  223. // 获取生效预警列表
  224. const fetchActiveAlerts = async () => {
  225. getWarningInfoList().then((res) => {
  226. activeAlerts.value = res.rows;
  227. });
  228. };
  229. const handleSelect = (item) => {
  230. if (selectedType.value == item.type) {
  231. selectedType.value = '';
  232. } else {
  233. selectedType.value = item.type;
  234. }
  235. };
  236. // 获取预警明细列表
  237. const fetchAlertDetails = async () => {
  238. const query = {
  239. type: selectedType.value,
  240. level: selectedLevel.value
  241. };
  242. getWarningInfoDetail({ query }).then((response) => {
  243. alertDetails.value = response.rows || [];
  244. });
  245. };
  246. // 控制类型下拉菜单的显示
  247. const toggleTypeDropdown = () => {
  248. showTypeDropdown.value = !showTypeDropdown.value;
  249. showLevelDropdown.value = false; // 关闭等级下拉菜单
  250. };
  251. // 控制等级下拉菜单的显示
  252. const toggleLevelDropdown = () => {
  253. showLevelDropdown.value = !showLevelDropdown.value;
  254. showTypeDropdown.value = false; // 关闭类型下拉菜单
  255. };
  256. // 选择类型
  257. const selectType = (type: string) => {
  258. selectedType.value = type;
  259. showTypeDropdown.value = false;
  260. fetchAlertDetails();
  261. };
  262. // 选择等级
  263. const selectLevel = (level: string) => {
  264. selectedLevel.value = level;
  265. showLevelDropdown.value = false;
  266. fetchAlertDetails();
  267. };
  268. // 点击外部区域时关闭下拉菜单
  269. const wrapper = ref<HTMLElement | null>(null);
  270. onMounted(async () => {
  271. loading.value = true;
  272. await Promise.all([fetchAlertCounts(), fetchActiveAlerts(), fetchAlertDetails()]);
  273. loading.value = false;
  274. });
  275. // 监听点击事件以关闭下拉菜单
  276. const handleClickOutside = (event: MouseEvent) => {
  277. const target = event.target as HTMLElement;
  278. if (wrapper.value && !wrapper.value.contains(target)) {
  279. showTypeDropdown.value = false;
  280. showLevelDropdown.value = false;
  281. }
  282. };
  283. window.addEventListener('click', handleClickOutside);
  284. // 清理事件监听器
  285. onUnmounted(() => {
  286. window.removeEventListener('click', handleClickOutside);
  287. });
  288. const formattedTime = ref({
  289. month: '-',
  290. day: '-',
  291. hour: '-',
  292. minute: '-'
  293. });
  294. const getFormattedTime = () => {
  295. const date = new Date();
  296. formattedTime.value = {
  297. month: (date.getMonth() + 1).toString(),
  298. day: date.getDate().toString(),
  299. hour: date.getHours().toString().padStart(2, '0'),
  300. minute: date.getMinutes().toString().padStart(2, '0'),
  301. };
  302. };
  303. onMounted(() => {
  304. getFormattedTime();
  305. });
  306. </script>
  307. <style lang="scss" scoped>
  308. .menu-content {
  309. width: 574px;
  310. height: 581px;
  311. background: url('@/assets/images/map/rightMenu/content.png') no-repeat;
  312. background-size: 100% 100%;
  313. padding: 30px 10px 10px 15px;
  314. font-size: 14px;
  315. position: relative;
  316. color: #ffffff;
  317. }
  318. .warning-info {
  319. padding: 20px 10px;
  320. display: flex;
  321. flex-direction: column;
  322. height: 540px;
  323. overflow: auto;
  324. }
  325. .current-time {
  326. width: 254px;
  327. height: 65px;
  328. background: url('@/assets/images/map/warningInfo/timeBox.png') no-repeat;
  329. background-size: 100% 100%;
  330. color: #fff;
  331. font-size: 16px;
  332. display: flex;
  333. align-items: center;
  334. padding-left: 75px;
  335. padding-bottom: 9px;
  336. margin-bottom: -10px;
  337. margin-left: -10px;
  338. flex-shrink: 0;
  339. .line {
  340. margin: 0 3px 0 6px;
  341. }
  342. .text1 {
  343. font-weight: bold;
  344. background-image: linear-gradient(to bottom, #ffffff 30%, #aec9f3 50%, #2b72d6 100%);
  345. margin: 3px;
  346. }
  347. .text2 {
  348. background-image: linear-gradient(to bottom, #ffffff 30%, #aec9f3 50%, #2b72d6 100%);
  349. }
  350. }
  351. .level-white {
  352. color: blue;
  353. }
  354. .alert-items {
  355. display: flex;
  356. justify-content: space-between;
  357. align-items: flex-start;
  358. flex-wrap: wrap;
  359. margin-top: 11px;
  360. cursor: pointer;
  361. .checked1 {
  362. background: url('@/assets/images/map/warningInfo/checked1.png') no-repeat;
  363. }
  364. .checked2 {
  365. background: url('@/assets/images/map/warningInfo/checked2.png') no-repeat;
  366. }
  367. .checked1,
  368. .checked2 {
  369. width: 16px;
  370. height: 16px;
  371. background-size: 100% 100%;
  372. position: absolute;
  373. bottom: 15px;
  374. right: 10px;
  375. }
  376. }
  377. .data-box1 {
  378. width: 122px;
  379. height: 91px;
  380. background: url('@/assets/images/map/warningInfo/bgBox.png') no-repeat;
  381. background-size: 100% 100%;
  382. padding-left: 40px;
  383. display: flex;
  384. flex-direction: column;
  385. align-items: center;
  386. position: relative;
  387. cursor: pointer;
  388. &:hover {
  389. background: url('@/assets/images/map/warningInfo/bgBox2.png') no-repeat;
  390. background-size: 100% 100%;
  391. }
  392. }
  393. .alert-icon {
  394. height: 42px;
  395. // 向左偏移 50% 的宽度,使图标居中
  396. margin-left: -50px;
  397. margin-top: 15px; /* 根据需要调整位置 */
  398. }
  399. .box-text1 {
  400. color: #fff;
  401. font-size: 14px;
  402. // 向左偏移 50% 的宽度,使文字居中
  403. margin-left: -50px;
  404. margin-top: auto; /* 将文字推到图片下方 */
  405. margin-bottom: 50px; /* 根据需要调整文字与底部的间距 */
  406. }
  407. .alert-count-items {
  408. width: 552px;
  409. height: 80px;
  410. background: url('@/assets/images/map/warningInfo/bg_base.png') no-repeat;
  411. background-size: 552px 45px;
  412. background-position: bottom center;
  413. gap: 0; /* 可选:添加间距 */
  414. padding: 7px; /* 根据需要调整内边距 */
  415. display: flex; /* 水平排列发布数量统计 */
  416. position: relative; /* 用于设置子元素的位置 */
  417. }
  418. .count-item {
  419. display: flex;
  420. flex-direction: column;
  421. align-items: center;
  422. margin-left: 10px;
  423. }
  424. .count-icon {
  425. height: 52px;
  426. margin: 0; /* 移除外边距 */
  427. padding: 0; /* 移除内边距 */
  428. display: block; /* 确保图片以块级元素显示,避免因行内元素的空白字符产生间隔 */
  429. }
  430. .count-num {
  431. position: absolute;
  432. font-size: 16px;
  433. font-weight: bold;
  434. color: transparent;
  435. background-image: linear-gradient(to bottom, #ffffff 25%, #ffffff 100%);
  436. -webkit-background-clip: text;
  437. background-clip: text;
  438. display: inline-block;
  439. top: 10px;
  440. font-family: BEBAS-1;
  441. }
  442. .text-red {
  443. background-image: linear-gradient(to bottom, #ffffff 25%, #ff2b34 100%);
  444. }
  445. .text-orange {
  446. background-image: linear-gradient(to bottom, #ffffff 25%, #ff8400 100%);
  447. }
  448. .text-yellow {
  449. background-image: linear-gradient(to bottom, #ffffff 25%, #ffda00 100%);
  450. }
  451. .text-blue {
  452. background-image: linear-gradient(to bottom, #ffffff 25%, #2b72d6 100%);
  453. }
  454. .count-level {
  455. font-size: 14px;
  456. top: 38px;
  457. color: #fdfcfc;
  458. position: absolute;
  459. }
  460. .alert-details {
  461. margin-top: 10px;
  462. position: relative;
  463. }
  464. .alert-table {
  465. width: 100%;
  466. border-collapse: collapse;
  467. }
  468. .alert-table th {
  469. color: #fff; /* 确保表头文字颜色 */
  470. background: url('@/assets/images/map/warningInfo/bg_head.png') no-repeat center center; /* 表头背景图 */
  471. background-size: cover; /* 背景图覆盖整行 */
  472. padding: 16px; /* 根据需要调整内边距 */
  473. position: relative; /* 为下拉菜单的绝对定位做准备 */
  474. }
  475. .alert-table th,
  476. .alert-table td {
  477. padding: 8px;
  478. text-align: left;
  479. }
  480. .alert-table .alert-row {
  481. background: url('@/assets/images/map/warningInfo/bg_arrange.png') no-repeat center center; /* 行的背景图 */
  482. background-size: cover; /* 背景图覆盖整行 */
  483. transition: background 0.3s ease; /* 添加过渡效果 */
  484. }
  485. /* 悬停效果 */
  486. .alert-table .alert-row:hover {
  487. background: url('@/assets/images/map/warningInfo/bg_arrange_selected.png') no-repeat center center; /* 悬停效果 */
  488. background-size: cover; /* 背景图覆盖整行 */
  489. }
  490. /* 确保文字可读性 */
  491. .alert-table td {
  492. color: #fff; /* 确保文字颜色与背景图对比明显 */
  493. }
  494. .dropdown-icon {
  495. width: 12px; /* 根据需要调整图标大小 */
  496. height: 12px; /* 根据需要调整图标大小 */
  497. margin-left: 5px; /* 图标与文字之间的间距 */
  498. }
  499. .dropdown-trigger {
  500. cursor: pointer;
  501. position: relative;
  502. }
  503. .dropdown-menu {
  504. position: absolute;
  505. top: 100%; /* 下拉菜单显示在触发器下方 */
  506. left: 0;
  507. background-color: #0c0000; /* 背景颜色 */
  508. border: 1px solid #ccc; /* 边框 */
  509. border-radius: 4px;
  510. z-index: 20;
  511. width: 150px; /* 根据需要调整宽度 */
  512. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); /* 添加阴影 */
  513. }
  514. .dropdown-item {
  515. padding: 8px 12px;
  516. cursor: pointer;
  517. }
  518. .dropdown-item:hover,
  519. .dropdown-item.selected {
  520. background-color: #f0f0f0;
  521. }
  522. </style>