cityEmergencyEvent.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. <template>
  2. <div class="container">
  3. <van-notice-bar
  4. v-show="noticeBarState.show"
  5. :left-icon="noticeImg"
  6. scrollable
  7. background="linear-gradient(180deg, #F3F7FF 0%, #FDFEFF 100%)"
  8. mode="closeable"
  9. :text="noticeBarState.event_title"
  10. class="notice"
  11. @click="handleNoticeBar"
  12. />
  13. <div ref="searchBoxRef" class="search-box">
  14. <van-search
  15. v-model="queryParams.keywords"
  16. class="common-search"
  17. :left-icon="searchImg"
  18. :right-icon="closeImg"
  19. :clearable="false"
  20. placeholder="请输入位置信息"
  21. @search="onSearchKeyword"
  22. @click-right-icon.stop="onSearchCancel"
  23. />
  24. <div v-show="showSearch" class="search-list">
  25. <van-list
  26. v-model:loading="loading"
  27. v-model:error="error"
  28. error-text="请求失败,点击重新加载"
  29. :finished="finished"
  30. finished-text="没有更多了"
  31. :immediate-check="false"
  32. @load="getSearchList"
  33. >
  34. <div
  35. v-for="(item, index) in searchList"
  36. :key="index"
  37. class="item"
  38. @click="handleClickItem(item)"
  39. >
  40. {{ item.name }}
  41. </div>
  42. </van-list>
  43. </div>
  44. </div>
  45. <Map
  46. ref="mapRef"
  47. class="map"
  48. active-map="satellite"
  49. :point-type="pointType"
  50. :event-details="eventDetails"
  51. />
  52. <div class="box">
  53. <div class="box-title">趋势统计</div>
  54. <Chart :option="option1" style="height: 200px" />
  55. </div>
  56. <div class="box">
  57. <div class="box-title">事件列表</div>
  58. <van-field
  59. v-model="yearLabel"
  60. is-link
  61. readonly
  62. label="年度"
  63. placeholder="请选择"
  64. @click="showPicker = true"
  65. />
  66. <el-table :data="detailsData.dataList" border table-layout="auto">
  67. <el-table-column label="类别" prop="data1" align="center" />
  68. <el-table-column label="位置" prop="data2" align="center" />
  69. <el-table-column label="发生时间" prop="data3" align="center" />
  70. <el-table-column label="受伤人数" prop="data4" align="center" />
  71. <el-table-column label="死亡人数" prop="data5" align="center" />
  72. </el-table>
  73. </div>
  74. <van-popup v-model:show="showPicker" round position="bottom">
  75. <van-picker
  76. :columns="columns"
  77. @cancel="showPicker = false"
  78. @confirm="onPickerConfirm"
  79. />
  80. </van-popup>
  81. </div>
  82. </template>
  83. <script lang="ts" setup name="cityEmergencyEvent">
  84. import noticeImg from "@/assets/images/notice.png";
  85. import { onMounted, reactive, ref } from "vue";
  86. import { useRouter } from "vue-router";
  87. import { getActiveEventList } from "@/api/event";
  88. import searchImg from "@/assets/images/search.png";
  89. import closeImg from "@/assets/images/close.png";
  90. import { getPointInfoComprehensiveSearch } from "@/api/globalMap";
  91. import { onClickOutside } from "@vueuse/core";
  92. import { chartOption1 } from "@/views/disasterRiskMonitor/chartOptions";
  93. import { ElTable, ElTableColumn } from "element-plus";
  94. const router = useRouter();
  95. const noticeBarState = reactive({
  96. show: false,
  97. event_id: "",
  98. event_title: ""
  99. });
  100. const handleNoticeBar = () => {};
  101. // 搜索
  102. const searchBoxRef = ref();
  103. let showSearch = ref();
  104. const total = ref(0);
  105. let loading = ref(false);
  106. let error = ref(false);
  107. let finished = ref(false);
  108. const queryParams = reactive({
  109. page: 0,
  110. page_size: 10,
  111. year: "",
  112. keywords: ""
  113. });
  114. let yearLabel = ref("");
  115. const searchList = ref([]);
  116. onClickOutside(searchBoxRef, event => {
  117. showSearch.value = false;
  118. });
  119. const getSearchList = () => {
  120. if (!queryParams.keywords) {
  121. return (loading.value = false);
  122. }
  123. queryParams.page++;
  124. getPointInfoComprehensiveSearch(queryParams)
  125. .then(res => {
  126. const items = res.data.list || [];
  127. total.value = res.data.total;
  128. if (queryParams.page == 1) {
  129. searchList.value = [];
  130. }
  131. items.forEach(val => {
  132. searchList.value.push(val);
  133. });
  134. if (queryParams.page_size * queryParams.page >= total.value) {
  135. finished.value = true;
  136. } else {
  137. finished.value = false;
  138. }
  139. showSearch.value = true;
  140. })
  141. .catch(() => {
  142. error.value = true;
  143. finished.value = false;
  144. })
  145. .finally(() => {
  146. loading.value = false;
  147. });
  148. };
  149. const onSearchKeyword = val => {
  150. queryParams.keywords = val;
  151. queryParams.page = 0;
  152. getSearchList();
  153. };
  154. const onSearchCancel = () => {
  155. showSearch.value = false;
  156. queryParams.keywords = "";
  157. queryParams.page = 0;
  158. finished.value = false;
  159. searchList.value = [];
  160. };
  161. const handleClickItem = item => {
  162. showSearch.value = false;
  163. queryParams.keywords = "";
  164. queryParams.page = 0;
  165. finished.value = false;
  166. searchList.value = [];
  167. };
  168. let pointType = ref([]);
  169. let eventDetails = ref({});
  170. let detailsData = ref({
  171. dataList: []
  172. });
  173. const option1 = ref(chartOption1);
  174. let showPicker = ref(false);
  175. let columns = reactive([
  176. { text: "2024年", value: "2024" },
  177. { text: "2023年", value: "2023" },
  178. { text: "2022年", value: "2022" },
  179. { text: "2021年", value: "2021" }
  180. ]);
  181. const onPickerConfirm = ({ selectedOptions }) => {
  182. showPicker.value = false;
  183. yearLabel.value = selectedOptions[0].text;
  184. queryParams.year = selectedOptions[0].value;
  185. };
  186. const initData = () => {
  187. // 通知栏数据
  188. getActiveEventList().then(res => {
  189. if (res.data.event_id != noticeBarState.event_id) {
  190. noticeBarState.show = true;
  191. noticeBarState.event_id = res.data.event_id;
  192. noticeBarState.event_title = res.data.event_title;
  193. }
  194. });
  195. // 趋势统计
  196. option1.value.xAxis[0].data = [
  197. "2024-01",
  198. "2024-02",
  199. "2024-03",
  200. "2024-04",
  201. "2024-05",
  202. "2024-06"
  203. ];
  204. option1.value.series[0].data = [
  205. 502.84, 205.97, 332.79, 281.55, 398.35, 214.02
  206. ];
  207. option1.value.series[1].data = [
  208. 281.55, 398.35, 214.02, 179.55, 289.57, 356.14
  209. ];
  210. // 事件列表
  211. detailsData.value.dataList = [
  212. {
  213. data1: "暴雨引发山体滑坡",
  214. data2: "茂名市电白区黄岭镇石头村",
  215. data3: "2024-12-14 12:12:13",
  216. data4: 1,
  217. data5: 0
  218. },
  219. {
  220. data1: "台风",
  221. data2: "茂名市茂南区羊角镇",
  222. data3: "2024-10-07 08:00:00",
  223. data4: 2,
  224. data5: 0
  225. },
  226. {
  227. data1: "洪水",
  228. data2: "茂名市高州市根子镇",
  229. data3: "2024-06-15 17:30:00",
  230. data4: 5,
  231. data5: 0
  232. },
  233. // 2023年的数据
  234. {
  235. data1: "地震",
  236. data2: "茂名市信宜市池洞镇",
  237. data3: "2023-11-22 21:45:00",
  238. data4: 3,
  239. data5: 0
  240. },
  241. {
  242. data1: "暴雨",
  243. data2: "茂名市化州市同庆镇",
  244. data3: "2023-08-03 14:20:00",
  245. data4: 4,
  246. data5: 0
  247. },
  248. {
  249. data1: "台风",
  250. data2: "茂名市电白区观珠镇",
  251. data3: "2023-05-12 10:10:00",
  252. data4: 2,
  253. data5: 0
  254. },
  255. // 新增2021年的数据
  256. {
  257. data1: "暴雨",
  258. data2: "茂名市茂南区公馆镇",
  259. data3: "2021-11-05 11:00:00",
  260. data4: 2,
  261. data5: 0
  262. },
  263. {
  264. data1: "台风",
  265. data2: "茂名市电白区麻岗镇",
  266. data3: "2021-09-15 15:00:00",
  267. data4: 1,
  268. data5: 0
  269. },
  270. {
  271. data1: "洪水",
  272. data2: "茂名市高州市石鼓镇",
  273. data3: "2021-07-25 17:00:00",
  274. data4: 3,
  275. data5: 0
  276. },
  277. {
  278. data1: "山体滑坡",
  279. data2: "茂名市化州市林尘镇",
  280. data3: "2021-05-20 09:00:00",
  281. data4: 0,
  282. data5: 0
  283. },
  284. {
  285. data1: "暴雨",
  286. data2: "茂名市电白区树仔镇",
  287. data3: "2021-03-10 14:00:00",
  288. data4: 1,
  289. data5: 0
  290. },
  291. // 原来的2021年数据
  292. {
  293. data1: "暴雨",
  294. data2: "茂名市茂南区袂花镇",
  295. data3: "2021-09-28 19:30:00",
  296. data4: 3,
  297. data5: 0
  298. }
  299. ];
  300. };
  301. onMounted(() => {
  302. initData();
  303. });
  304. </script>
  305. <style lang="scss" scoped>
  306. .container {
  307. .map {
  308. width: 100%;
  309. height: 250px;
  310. }
  311. }
  312. .search-box {
  313. margin-bottom: 10px;
  314. }
  315. .search-list {
  316. position: absolute;
  317. top: 50px;
  318. left: 0;
  319. z-index: 9;
  320. width: 100%;
  321. height: calc(100vh - 400px);
  322. overflow-y: auto;
  323. background-color: #ffffff;
  324. border-top: 1px solid #eeeeee;
  325. .item {
  326. padding: 8px 15px;
  327. border-bottom: 1px solid #eeeeee;
  328. }
  329. }
  330. .common-search {
  331. border: 1px solid #ededed;
  332. :deep(.van-field__left-icon) {
  333. .van-icon__image {
  334. width: 12px;
  335. height: 12px;
  336. }
  337. }
  338. :deep(.van-field__right-icon) {
  339. width: 30px;
  340. height: 30px;
  341. padding: 0;
  342. .van-icon__image {
  343. width: 30px;
  344. height: 30px;
  345. }
  346. }
  347. }
  348. .box {
  349. background-color: #fff;
  350. padding: 10px;
  351. .box-title {
  352. font-size: 18px;
  353. font-weight: bold;
  354. }
  355. }
  356. </style>