TyphoonVideo.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. <template>
  2. <div class="menu-content">
  3. <div class="container">
  4. <div class="gradient-text title">台风视频</div>
  5. </div>
  6. <!-- <div class="section-box">-->
  7. <div class="title2">路径列表</div>
  8. <div style="display: flex; align-content: center; justify-content: flex-start">
  9. <span style="margin-right: 15px; white-space: nowrap; font-size: 38px; height: 100px">选择台风:</span>
  10. <el-select
  11. v-model="selectedYear"
  12. placeholder="请选择年份"
  13. class="custom-select"
  14. popper-class="custom-select-popper"
  15. :teleported="false"
  16. style="width: 1200px; margin-left: 30px"
  17. @change="handleYearChange"
  18. >
  19. <el-option v-for="year in yearList" :key="year" :label="year" :value="year"></el-option>
  20. </el-select>
  21. <el-select
  22. v-model="selectedTyphoon"
  23. placeholder="请选择台风名称"
  24. class="custom-select"
  25. popper-class="custom-select-popper"
  26. :teleported="false"
  27. style="width: 1200px; margin-left: 30px"
  28. @change="handleTyphoonChange"
  29. >
  30. <el-option v-for="typhoon in TyphoonList" :key="typhoon.id" :label="typhoon.typhoon_name" :value="typhoon.typhoon_name"></el-option>
  31. </el-select>
  32. </div>
  33. <div class="custom-table">
  34. <div class="th">
  35. <div class="td">过去时间</div>
  36. <div class="td">经纬度</div>
  37. <div class="td">位置</div>
  38. <div class="td">操作</div>
  39. </div>
  40. <div class="table-content">
  41. <div v-for="(item, index) in dataList" :key="item.id" class="tr">
  42. <div class="td">{{ parseTime(item.record_time) }}</div>
  43. <div class="td">{{ item.longitude }},{{ item.latitude }}</div>
  44. <div class="td">{{ item.record_address }}</div>
  45. <div class="td">
  46. <div class="text" @click="handleConnect(index, item)">周边视频</div>
  47. </div>
  48. </div>
  49. </div>
  50. </div>
  51. <Dialog v-if="showDialog" v-model="showDialog" draggable type="md" title="台风视频" hide-footer>
  52. <div style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center">
  53. <HKVideo :dot_data="videoMonitorData" />
  54. </div>
  55. </Dialog>
  56. </div>
  57. </template>
  58. <script setup lang="ts">
  59. import { ref, reactive, onMounted } from 'vue';
  60. import { getTyphoonList, getTyphoonTrajectory, getTyphoonYearList } from '@/api/globalMap/TyphoonVideo';
  61. const selectedYear = ref(''); // selectedYear 用于存储用户选择的年份
  62. const yearList = ref([]); // yearList 用于存储从 getTyphoonYearList 获取的年份列表
  63. const TyphoonList = ref([]);
  64. const selectedTyphoon = ref('');
  65. const dataList = reactive([]); // 数据列表
  66. const queryParams = reactive({
  67. typhoon_code: '',
  68. year_n: ''
  69. });
  70. let showDialog = ref(false);
  71. let videoMonitorData = ref({});
  72. const handleConnect = (index: number, item: any) => {
  73. videoMonitorData.value = item; // 将当前行数据赋值给videoMonitorData
  74. showDialog.value = true; // 显示弹窗
  75. };
  76. // 调用函数
  77. onMounted(() => {
  78. initData();
  79. });
  80. const handleYearChange = async () => {
  81. if (selectedYear.value) {
  82. try {
  83. const typhoons = await getTyphoonList({ query: { year_n: selectedYear.value } });
  84. if (typhoons.code === 0) {
  85. TyphoonList.value = typhoons.rows;
  86. if (TyphoonList.value.length > 0) {
  87. selectedTyphoon.value = TyphoonList.value[0].typhoon_name; // 默认选择第一个台风
  88. initDataByTyphoon();
  89. } else {
  90. console.error('No typhoons found for the selected year');
  91. }
  92. } else {
  93. console.error('Failed to fetch typhoon list:', typhoons);
  94. }
  95. } catch (error) {
  96. console.error('Error fetching typhoon list:', error);
  97. }
  98. }
  99. };
  100. const handleTyphoonChange = async () => {
  101. if (selectedTyphoon.value && selectedYear.value) {
  102. initDataByTyphoon();
  103. }
  104. };
  105. const initDataByTyphoon = async () => {
  106. const typhoon = TyphoonList.value.find((t) => t.typhoon_name === selectedTyphoon.value);
  107. if (typhoon && typhoon.typhoon_code) {
  108. try {
  109. const trajectory = await getTyphoonTrajectory({ query: { typhoon_code: typhoon.typhoon_code } });
  110. if (trajectory.code === 0 && Array.isArray(trajectory.rows)) {
  111. dataList.splice(0, dataList.length, ...trajectory.rows);
  112. } else {
  113. console.error('Invalid response from server:', trajectory);
  114. dataList.splice(0, dataList.length); // 清空数据列表
  115. }
  116. } catch (error) {
  117. console.error('Error fetching typhoon trajectory:', error);
  118. }
  119. } else {
  120. console.error('No typhoon code found for selected typhoon name');
  121. }
  122. };
  123. const fetchData = async (apiFunction, params) => {
  124. try {
  125. const response = await apiFunction(params);
  126. if (response.code !== 0) {
  127. throw new Error('Invalid response from server');
  128. }
  129. return response.rows;
  130. } catch (error) {
  131. console.error('Error fetching data:', error);
  132. return [];
  133. }
  134. };
  135. const initData = async () => {
  136. try {
  137. const years = await fetchData(getTyphoonYearList, {});
  138. // 对获取到的年份进行降序排序,并选取第一个(即最新年份)
  139. yearList.value = years.map((item) => item.year_n).sort((a, b) => b - a);
  140. const latestYear = yearList.value[0]; // 获取最新年份
  141. if (latestYear) {
  142. selectedYear.value = latestYear; // 设置最新年份为默认选中值
  143. const typhoons = await fetchData(getTyphoonList, { query: { year_n: latestYear } });
  144. TyphoonList.value = typhoons;
  145. if (TyphoonList.value.length > 0) {
  146. selectedTyphoon.value = TyphoonList.value[0].typhoon_name; // 默认选择第一个台风
  147. const typhoonCode = TyphoonList.value[0].typhoon_code;
  148. if (typhoonCode) {
  149. const trajectory = await fetchData(getTyphoonTrajectory, { query: { typhoon_code: typhoonCode } });
  150. dataList.splice(0, dataList.length, ...trajectory);
  151. } else {
  152. console.error('No typhoon code found in the list');
  153. }
  154. } else {
  155. console.error('No typhoons found for the selected year');
  156. }
  157. } else {
  158. console.error('No year found in the list');
  159. }
  160. } catch (error) {
  161. console.error('Error initializing data:', error);
  162. }
  163. };
  164. </script>
  165. <style lang="scss" scoped>
  166. .menu-content {
  167. width: 1579px;
  168. height: 1394px;
  169. background: url('@/assets/images/map/rightMenu/content.png') no-repeat;
  170. padding: 130px 20px 20px 20px;
  171. font-size: 36px;
  172. position: relative;
  173. color: #ffffff;
  174. }
  175. .custom-input {
  176. height: 60px;
  177. line-height: 40px;
  178. }
  179. .box-left {
  180. display: flex;
  181. margin-top: 30px;
  182. margin-bottom: 20px;
  183. .btn {
  184. width: 140px;
  185. min-width: 140px;
  186. height: 60px;
  187. background: url('@/assets/images/map/rightMenu/potentialFloodHazard/btn.png') no-repeat;
  188. display: flex;
  189. justify-content: center;
  190. align-items: center;
  191. cursor: pointer;
  192. margin-left: 20px;
  193. color: #ffffff;
  194. font-size: 32px;
  195. }
  196. }
  197. .custom-table {
  198. width: 100%;
  199. height: 1030px;
  200. overflow-y: auto;
  201. overflow-x: hidden;
  202. .table-content {
  203. height: 880px;
  204. overflow-y: auto;
  205. overflow-x: hidden;
  206. }
  207. .th {
  208. width: 100%;
  209. height: 151px;
  210. background: url('@/assets/images/map/rightMenu/th.png') no-repeat;
  211. background-size: 100% 100%;
  212. display: flex;
  213. }
  214. .tr {
  215. width: 100%;
  216. height: 139px;
  217. background: url('@/assets/images/map/rightMenu/td.png') no-repeat;
  218. background-size: 100% 100%;
  219. display: flex;
  220. padding-right: 20px;
  221. &:hover {
  222. background: url('@/assets/images/map/rightMenu/td_checked.png') no-repeat;
  223. background-size: 100% 100%;
  224. }
  225. }
  226. .td {
  227. flex: 1;
  228. color: #edfaff;
  229. font-size: 38px;
  230. display: flex;
  231. justify-content: center;
  232. align-items: center;
  233. cursor: pointer;
  234. }
  235. .td-text {
  236. /* 设置字体透明 */
  237. color: transparent;
  238. /* 使用 -webkit-background-clip 属性将背景剪裁至文本形状 */
  239. -webkit-background-clip: text;
  240. /* 非Webkit内核浏览器需要使用标准前缀 */
  241. background-clip: text;
  242. font-family: 'YouSheBiaoTiHei';
  243. /* 设置线性渐变,从红色渐变到蓝色 */
  244. background-image: linear-gradient(to bottom, #ffffff 50%, #3075d3 100%);
  245. font-size: 48px;
  246. }
  247. .text-green {
  248. background-image: linear-gradient(to bottom, #ffffff 50%, #40c75f 100%);
  249. }
  250. .text-danger {
  251. background-image: linear-gradient(to bottom, #ffffff 50%, #ff2f3c 100%);
  252. }
  253. }
  254. .title2 {
  255. font-size: 36px;
  256. height: 80px;
  257. }
  258. .title {
  259. font-size: 60px;
  260. position: absolute;
  261. top: 30px;
  262. left: 160px;
  263. }
  264. .text {
  265. cursor: pointer;
  266. font-size: 38px;
  267. color: #00e8ff;
  268. margin-right: 20px;
  269. &:last-child {
  270. margin-right: 0;
  271. }
  272. }
  273. </style>