| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 |
- <template>
- <div class="weather-card">
- <div class="cloud-bg">
- <div class="basic-info">
- <div class="location">
- <span>上海市</span>
- <div class="date-time">
- <span>{{ currentDate }}</span>
- <span>{{ currentWeek }}</span>
- <span>{{ currentTime }}</span>
- </div>
- </div>
- <div class="weather-info">
- <div class="temperature">{{ curTemperature || '--' }}℃</div>
- <div class="wind">
- <div>
- <span class="wind-value"> 风力: {{ windSpeedLevel || '--' }}级</span>
- </div>
- <div>
- <span class="wind-value">风速: {{ curWindVelocity || '--' }} km/h</span>
- </div>
- </div>
- </div>
- <el-button type="text" class="history-temperature" @click="getHistoryTempDataAndOpenDialog">
- <img :src="historyTemperatureIcon" alt="empty" class="icon-history-temperature">
- <div class="history-temperature-text">历史温度</div>
- </el-button>
- <el-dialog v-model="isDialogOpen" title="历史温度统计" width="550px" class="weather-dialog"
- :close-on-click-modal="false" @close="handleCloseDialog">
- <!-- 加载状态 -->
- <el-loading v-if="isLoading" target=".dialog-content" text="正在加载数据...">
- <div class="dialog-content" style="min-height: 300px;"></div>
- </el-loading>
- <!-- 表格 -->
- <div v-else>
- <div class="highTemperatureWrapper">
- <img :src="fireIcon" alt="empty" class="icon-fire">
- <span>本年度高温天气: {{ countHighTemperatureDays }}天</span>
- </div>
- <el-table :data="historyTemperatureList" border stripe max-height="calc(80vh - 120px)">
- <el-table-column label="日期" align="center" width="170">
- <template #default="scope">
- {{ scope.row.dataTime ? scope.row.dataTime.slice(0, 10) : '--' }}
- </template>
- </el-table-column>
- <el-table-column label="最高温度(℃)" prop="maxTemperature" align="center" width="170">
- <template #default="scope">
- <span> {{ scope.row.maxTemperature ?? "--" }} </span>
- </template>
- </el-table-column>
- <el-table-column label="最低温度(℃)" prop="minTemperature" align="center">
- <template #default="scope">
- <span> {{ scope.row.minTemperature ?? "--" }} </span>
- </template>
- </el-table-column>
- </el-table>
- <!-- 无数据提示 -->
- <div v-if="historyTemperatureList.length === 0" class="no-data">
- <el-empty description="暂无历史温度数据" />
- </div>
- </div>
- </el-dialog>
- </div>
- <div class="disaster-emergency-tips">
- <WeatherTips :title="measureTitle" :measure="measureInfo" :disasterWarningList="todayWarningInfo" />
- </div>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { onMounted, onUnmounted, ref, watch } from 'vue';
- import dayjs from 'dayjs';
- import WeatherTips from './weather-info/WeatherTips.vue';
- import { SysDictDataDetail, queryDictTypeDetail } from '@/api/dict';
- import { getTodayDisasterWarnInfoList, getRealTimeWeatherData, getHighTemperatureDays, getHistoryTemperatureList, HistoryTemperatureInfo } from '@/api/disaster-overview';
- import fireIcon from '@/assets/images/weather-icons/fire.png'
- import historyTemperatureIcon from '@/assets/images/weather-icons/history-temperature.png'
- import {
- ElButton,
- ElDialog,
- ElMessage
- } from 'element-plus';
- import { Calendar, Download } from "@element-plus/icons-vue"
- import { Clock } from '@element-plus/icons-vue';
- export interface DisasterWarningListType {
- disasterType: string;
- disasterName: String;
- }
- const currentDate = ref('');
- const currentWeek = ref('');
- const currentTime = ref('');
- const countHighTemperatureDays = ref<number>(0);
- let timer: NodeJS.Timeout;
- const weatherDisasterDic = ref<SysDictDataDetail[]>([]); // 气象灾害预警字典
- const disasterMeasureDic = ref<SysDictDataDetail[]>([]); // 灾害应急措施字典
- const todayWarningInfo = ref<DisasterWarningListType[]>([]); // 今日灾害预警信息
- const measureTitle = ref<string | undefined>('');
- const measureInfo = ref<string | undefined>('');
- const curTemperature = ref('0');
- const curWindVelocity = ref(0);
- const windSpeedLevel = ref(0);
- const isDialogOpen = ref(false);
- const isLoading = ref(false);
- const historyTemperatureList = ref<HistoryTemperatureInfo[]>([]);
- // 风速km/h等级换算表
- const windLevels = [
- { max: 1, level: 0 },
- { max: 5, level: 1 },
- { max: 11, level: 2 },
- { max: 19, level: 3 },
- { max: 28, level: 4 },
- { max: 38, level: 5 },
- { max: 49, level: 6 },
- { max: 61, level: 7 },
- { max: 74, level: 8 },
- { max: 88, level: 9 },
- { max: 102, level: 10 },
- { max: 117, level: 11 },
- { max: 133, level: 12 },
- { max: 149, level: 13 },
- { max: 166, level: 14 },
- { max: 183, level: 15 },
- { max: 201, level: 16 },
- { max: 220, level: 17 },
- { max: Infinity, level: 18 },
- ];
- const getWindLevel = (windSpeedKmh: number): number => {
- const matched = windLevels.find(({ max }) => windSpeedKmh <= max);
- return matched ? matched.level : 18;
- };
- watch(
- () => curWindVelocity.value,
- (newVal) => {
- windSpeedLevel.value = getWindLevel(newVal);
- },
- );
- // 更新时间函数
- const updateDateTime = () => {
- const now = dayjs();
- currentDate.value = now.format('MM月DD日');
- currentWeek.value = `星期${now.format('dd').slice(-1)}`;
- currentTime.value = now.format('HH:mm:ss');
- };
- // 获取实时天气数据
- const getRealTimeWeatherDataInfo = async () => {
- const res = await getRealTimeWeatherData();
- curTemperature.value = res?.temperature || '0';
- curWindVelocity.value = (res?.windVelocity || 0) * 3.6;
- };
- //获取历史温度数据
- const getHistoryTempDataAndOpenDialog = async () => {
- try {
- isLoading.value = true;
- const res = await getHistoryTemperatureList();
- historyTemperatureList.value = res || [];
- isDialogOpen.value = true;
- } catch (error) {
- ElMessage.error({
- message: "请求后端数据失败",
- showClose: true,
- duration: 2000
- });
- console.error("获取历史温度数据失败", error);
- isLoading.value = false;
- historyTemperatureList.value = [];
- } finally {
- isLoading.value = false;
- }
- }
- const handleCloseDialog = () => {
- historyTemperatureList.value = [];
- isDialogOpen.value = false;
- }
- // 获取高温天气天数
- const getHighTemperatureDaysRes = async () => {
- const res = await getHighTemperatureDays();
- countHighTemperatureDays.value = res > 0 && res <= 366 ? res : 0;
- };
- function getWeatherWarningType(val: string) {
- const match = val.match(/^[a-zA-Z0-9]+/);
- if (!match) return;
- return match[0];
- };
- // 获取今日灾害预警信息
- const getTodayWarningInfo = async () => {
- todayWarningInfo.value = (await getTodayDisasterWarnInfoList())?.map((item) => ({
- disasterType: item.disasterType,
- disasterName:
- weatherDisasterDic.value.find((dic) => dic.itemCode === item.disasterType)?.itemValue || '未知预警信息',
- }));
- const normalMeasure = disasterMeasureDic.value.find((item) => item.itemCode === 'normal_measure');
- if (todayWarningInfo.value.length === 0) {
- measureTitle.value = '安全提示';
- measureInfo.value = normalMeasure?.itemValue || '暂无提示';
- } else {
- const todayWarningValue = todayWarningInfo.value.find(
- (item) => item.disasterType === todayWarningInfo.value[0].disasterType,
- )?.disasterType;
- if (todayWarningValue) {
- const weatherWarningType = getWeatherWarningType(todayWarningValue);
- if (weatherWarningType) {
- const targetMeasure = disasterMeasureDic.value.find((item) => item.itemCode.includes(weatherWarningType));
- measureTitle.value = targetMeasure?.itemValue ? '应急提示' : '安全提示';
- measureInfo.value = targetMeasure?.itemValue || normalMeasure?.itemValue;
- }
- }
- }
- };
- onMounted(async () => {
- await queryDictTypeDetail('weather_warning').then((res) => {
- weatherDisasterDic.value = res.sysDictDataList;
- });
- await queryDictTypeDetail('disaster_emergency_measure').then((res) => {
- disasterMeasureDic.value = res.sysDictDataList;
- });
- updateDateTime();
- timer = setInterval(updateDateTime, 1000);
- getRealTimeWeatherDataInfo();
- getHighTemperatureDaysRes();
- getTodayWarningInfo();
- });
- onUnmounted(() => {
- clearInterval(timer);
- });
- </script>
- <style lang="scss" scoped>
- .weather-card {
- width: 100%;
- height: 267px;
- background: linear-gradient(90deg, #b4ccff 0%, #e4fbf9 100%);
- border-radius: 4px;
- }
- .cloud-bg {
- width: 100%;
- height: 100%;
- display: flex;
- padding: 16px;
- background-image: url('@/assets/images/disaster-overview/cloud-bg.png');
- background-repeat: no-repeat;
- background-position: left top;
- background-size: auto 100%;
- }
- .basic-info {
- width: 305px;
- margin: 4px 35px 32px 12px;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- .location {
- gap: 20px;
- font-weight: 600;
- font-size: 16px;
- color: #062B5D;
- line-height: 22px;
- text-align: left;
- font-style: normal;
- display: flex;
- text-shadow: 1px 1px 1px rgba(15, 61, 125, 0.2);
- }
- .date-time {
- gap: 6px;
- text-align: left;
- font-style: normal;
- display: flex;
- }
- .weather-info {
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- padding: 0;
- margin-top: 80px;
- }
- .temperature {
- font-family: Helvetica, Helvetica;
- font-weight: bold;
- font-size: 64px;
- color: #0f3d7d;
- text-align: left;
- font-style: normal;
- line-height: 1;
- margin: 0;
- padding: 0;
- }
- .wind-value {
- font-weight: 500;
- }
- .wind {
- display: flex;
- flex-direction: column;
- gap: 20px;
- font-weight: 400;
- font-size: 16px;
- color: #0f3d7d;
- padding-top: 0;
- margin: 0;
- text-align: left;
- line-height: 1;
- }
- .history-temperature {
- width: 108px;
- height: 32px;
- background: rgba(255, 255, 255, 0.4);
- border-radius: 16px;
- backdrop-filter: blur(10px);
- display: flex;
- align-items: center;
- justify-content: center;
- margin-top: 15px
- }
- .icon-history-temperature {
- width: 12px;
- height: 12px;
- }
- .history-temperature-text {
- font-weight: 500;
- font-size: 14px;
- color: #0F3D7D;
- line-height: 20px;
- text-align: center;
- font-style: normal;
- margin-left: 6px;
- }
- }
- :deep(.el-dialog) {
- background-image: url('@/assets/images/weather-icons/dialoag-title-bg.png');
- background-size: cover;
- background-repeat: no-repeat;
- background-position: center;
- border-radius: 8px;
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
- .highTemperatureWrapper {
- background: rgba(255, 77, 77, 0.12);
- border-radius: 4px;
- backdrop-filter: blur(5.42253521126761px);
- height: 40px;
- margin: 10px 0;
- font-weight: 800;
- font-size: 16px;
- color: #FF4D4F;
- text-align: left;
- font-style: normal;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 6px;
- }
- .icon-fire {
- width: 20px;
- height: 20px;
- }
- .el-dialog__header {
- padding:5px 0;
- border: none;
- display: flex;
- position: relative;
- align-items: center;
- justify-content: flex-start;
- }
- .el-dialog__headerbtn {
- width: 16px;
- height: 16px;
- right: 0px;
- top: 5px;
- }
- .el-dialog__title {
- font-family: PingFangSC, PingFang SC;
- font-weight: 800;
- font-size: 16px;
- color: rgba(0, 0, 0, 0.88);
- line-height:24px;
- text-align: left;
- font-style: normal;
- }
- .el-dialog__close {
- font-size: 16px;
- }
- }
- :deep(.el-table) {
- border-radius: 8px;
- .el-table__header {
- .el-table__cell {
- font-family: 'PingFang SC';
- font-weight: bold;
- font-size: 14px;
- color: #000;
- line-height: 22px;
- text-align: left;
- font-style: normal;
- }
- }
- .el-table__body {
- overflow: auto;
- scrollbar-width: none;
- -ms-overflow-style: none;
- .el-table__cell {
- font-family: 'PingFang SC';
- font-weight: 400;
- font-size: 14px;
- color: #303133;
- line-height: 22px;
- text-align: left;
- font-style: normal;
- }
- }
- }
- .no-data {
- width: 100%;
- padding: 40px 0;
- text-align: center;
- }
- .disaster-emergency-tips {
- flex: 1;
- }
- </style>
|