WeatherCard.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. <template>
  2. <div class="weather-card">
  3. <div class="cloud-bg">
  4. <div class="basic-info">
  5. <div>
  6. <div class="location">上海市</div>
  7. <div class="date-time">
  8. <span>{{ currentDate }}</span>
  9. <span>{{ currentWeek }}</span>
  10. <span>{{ currentTime }}</span>
  11. </div>
  12. </div>
  13. <div class="weather-info">
  14. <div class="temperature">{{ curTemperature || '--' }}℃</div>
  15. <div class="wind">
  16. <div>
  17. 风速:
  18. <span class="wind-value">{{ curWindVelocity || '--' }} km/h</span>
  19. </div>
  20. <div>
  21. 风力:
  22. <span class="wind-value">{{ windSpeedLevel || '--' }}级</span>
  23. </div>
  24. </div>
  25. </div>
  26. </div>
  27. <div class="disaster-emergency-tips">
  28. <WeatherTips :title="measureTitle" :measure="measureInfo" :disasterWarningList="todayWarningInfo" />
  29. </div>
  30. </div>
  31. </div>
  32. </template>
  33. <script setup lang="ts">
  34. import { onMounted, onUnmounted, ref, watch } from 'vue';
  35. import dayjs from 'dayjs';
  36. import WeatherTips from './weather-info/WeatherTips.vue';
  37. import { SysDictDataDetail, queryDictTypeDetail } from '@/api/dict';
  38. import { getTodayDisasterWarnInfoList, getRealTimeWeatherData } from '@/api/disaster-overview';
  39. export interface DisasterWarningListType {
  40. disasterType: string;
  41. disasterName: string;
  42. }
  43. const currentDate = ref('');
  44. const currentWeek = ref('');
  45. const currentTime = ref('');
  46. let timer: NodeJS.Timeout;
  47. const weatherDisasterDic = ref<SysDictDataDetail[]>([]); // 气象灾害预警字典
  48. const disasterMeasureDic = ref<SysDictDataDetail[]>([]); // 灾害应急措施字典
  49. const todayWarningInfo = ref<DisasterWarningListType[]>([]); // 今日灾害预警信息
  50. const measureTitle = ref<string | undefined>('');
  51. const measureInfo = ref<string | undefined>('');
  52. const curTemperature = ref('0');
  53. const curWindVelocity = ref(0);
  54. const windSpeedLevel = ref(0);
  55. // 风速km/h等级换算表
  56. const windLevels = [
  57. { max: 1, level: 0 },
  58. { max: 5, level: 1 },
  59. { max: 11, level: 2 },
  60. { max: 19, level: 3 },
  61. { max: 28, level: 4 },
  62. { max: 38, level: 5 },
  63. { max: 49, level: 6 },
  64. { max: 61, level: 7 },
  65. { max: 74, level: 8 },
  66. { max: 88, level: 9 },
  67. { max: 102, level: 10 },
  68. { max: 117, level: 11 },
  69. { max: 133, level: 12 },
  70. { max: 149, level: 13 },
  71. { max: 166, level: 14 },
  72. { max: 183, level: 15 },
  73. { max: 201, level: 16 },
  74. { max: 220, level: 17 },
  75. { max: Infinity, level: 18 },
  76. ];
  77. const getWindLevel = (windSpeedKmh: number): number => {
  78. const matched = windLevels.find(({ max }) => windSpeedKmh <= max);
  79. return matched ? matched.level : 18;
  80. };
  81. watch(
  82. () => curWindVelocity.value,
  83. (newVal) => {
  84. windSpeedLevel.value = getWindLevel(newVal);
  85. },
  86. );
  87. // 更新时间函数
  88. const updateDateTime = () => {
  89. const now = dayjs();
  90. currentDate.value = now.format('MM月DD日');
  91. currentWeek.value = `星期${now.format('dd').slice(-1)}`;
  92. currentTime.value = now.format('HH:mm:ss');
  93. };
  94. // 获取实时天气数据
  95. const getRealTimeWeatherDataInfo = async () => {
  96. const res = await getRealTimeWeatherData();
  97. curTemperature.value = res?.temperature || '0';
  98. curWindVelocity.value = (res?.windVelocity || 0) * 3.6;
  99. };
  100. function getWeatherWarningType(val: string) {
  101. const match = val.match(/^[a-zA-Z0-9]+/);
  102. if (!match) return;
  103. return match[0];
  104. }
  105. // 获取今日灾害预警信息
  106. const getTodayWarningInfo = async () => {
  107. todayWarningInfo.value = (await getTodayDisasterWarnInfoList())?.map((item) => ({
  108. disasterType: item.disasterType,
  109. disasterName:
  110. weatherDisasterDic.value.find((dic) => dic.itemCode === item.disasterType)?.itemValue || '未知预警信息',
  111. }));
  112. const normalMeasure = disasterMeasureDic.value.find((item) => item.itemCode === 'normal_measure');
  113. if (todayWarningInfo.value.length === 0) {
  114. measureTitle.value = '安全提示';
  115. measureInfo.value = normalMeasure?.itemValue || '暂无提示';
  116. } else {
  117. const todayWarningValue = todayWarningInfo.value.find(
  118. (item) => item.disasterType === todayWarningInfo.value[0].disasterType,
  119. )?.disasterType;
  120. if (todayWarningValue) {
  121. const weatherWarningType = getWeatherWarningType(todayWarningValue);
  122. if (weatherWarningType) {
  123. const targetMeasure = disasterMeasureDic.value.find((item) => item.itemCode.includes(weatherWarningType));
  124. measureTitle.value = targetMeasure?.itemValue ? '应急提示' : '安全提示';
  125. measureInfo.value = targetMeasure?.itemValue || normalMeasure?.itemValue;
  126. }
  127. }
  128. }
  129. };
  130. onMounted(async () => {
  131. await queryDictTypeDetail('weather_warning').then((res) => {
  132. weatherDisasterDic.value = res.sysDictDataList;
  133. });
  134. await queryDictTypeDetail('disaster_emergency_measure').then((res) => {
  135. disasterMeasureDic.value = res.sysDictDataList;
  136. });
  137. updateDateTime();
  138. timer = setInterval(updateDateTime, 1000);
  139. getRealTimeWeatherDataInfo();
  140. getTodayWarningInfo();
  141. });
  142. onUnmounted(() => {
  143. clearInterval(timer);
  144. });
  145. </script>
  146. <style lang="scss" scoped>
  147. .weather-card {
  148. width: 100%;
  149. height: 267px;
  150. background: linear-gradient(90deg, #b4ccff 0%, #e4fbf9 100%);
  151. border-radius: 4px;
  152. }
  153. .cloud-bg {
  154. width: 100%;
  155. height: 100%;
  156. display: flex;
  157. padding: 16px;
  158. background-image: url('@/assets/images/disaster-overview/cloud-bg.png');
  159. background-repeat: no-repeat;
  160. background-position: left top;
  161. background-size: auto 100%;
  162. }
  163. .basic-info {
  164. width: 305px;
  165. margin: 4px 35px 32px 12px;
  166. display: flex;
  167. flex-direction: column;
  168. justify-content: space-between;
  169. .location {
  170. font-weight: 500;
  171. font-size: 20px;
  172. color: #0f3d7d;
  173. margin-bottom: 8px;
  174. }
  175. .date-time {
  176. font-weight: 400;
  177. font-size: 16px;
  178. color: #0f3d7d;
  179. display: flex;
  180. gap: 6px;
  181. }
  182. .weather-info {
  183. display: flex;
  184. align-items: center;
  185. justify-content: space-between;
  186. .temperature {
  187. font-weight: bold;
  188. font-size: 63px;
  189. color: #0f3d7d;
  190. }
  191. .wind {
  192. display: flex;
  193. flex-direction: column;
  194. gap: 7px;
  195. font-weight: 400;
  196. font-size: 16px;
  197. color: #0f3d7d;
  198. }
  199. .wind-value {
  200. font-weight: 500;
  201. }
  202. }
  203. }
  204. .disaster-emergency-tips {
  205. flex: 1;
  206. }
  207. </style>