20251225:设备上电触发任务修改
This commit is contained in:
parent
97da219ff1
commit
1e70af6110
@ -92,6 +92,12 @@ public class DeviceTask {
|
||||
return builder(FastBeeConstant.TASK.DEVICE_TEST_TASK);
|
||||
}
|
||||
|
||||
@Bean(FastBeeConstant.TASK.TRIGGER_POWER_ON_TASK_ASYNC)
|
||||
public Executor triggerPowerOnTaskExecutor() {
|
||||
return builder(FastBeeConstant.TASK.TRIGGER_POWER_ON_TASK_ASYNC);
|
||||
}
|
||||
|
||||
|
||||
/*组装线程池*/
|
||||
private ThreadPoolTaskExecutor builder(String threadNamePrefix) {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
|
||||
@ -96,6 +96,8 @@ public interface FastBeeConstant {
|
||||
/*OTA升级线程池*/
|
||||
String OTA_THREAD_POOL = "otaThreadPoolTaskExecutor";
|
||||
|
||||
// 设备上电即触发异步任务 TriggerPowerOnTaskAsync
|
||||
String TRIGGER_POWER_ON_TASK_ASYNC = "triggerPowerOnTaskAsync";
|
||||
}
|
||||
|
||||
interface MQTT {
|
||||
@ -280,6 +282,11 @@ public interface FastBeeConstant {
|
||||
*/
|
||||
String POWER_ON_TRIGGER_COMMAND = "power:on:trigger:command:";
|
||||
|
||||
/**
|
||||
* 设备上电触发任务待触发标记
|
||||
*/
|
||||
String POWER_ON_TRIGGER_PENDING = "power:on:trigger:pending:";
|
||||
|
||||
}
|
||||
|
||||
interface PROTOCOL {
|
||||
|
||||
@ -166,5 +166,13 @@ public class RedisKeyBuilder {
|
||||
return FastBeeConstant.REDIS.POWER_ON_TRIGGER_COMMAND + serialNumber.toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备上电触发任务待触发标记key
|
||||
* 用于标记设备是否需要在上电后收到第一个数据包时触发任务
|
||||
*/
|
||||
public static String buildPowerOnTriggerPendingKey(String serialNumber) {
|
||||
return FastBeeConstant.REDIS.POWER_ON_TRIGGER_PENDING + serialNumber.toUpperCase();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -47,4 +47,10 @@ public interface IDeviceReportMessageService {
|
||||
*/
|
||||
IProtocol selectedProtocol(Long productId);
|
||||
|
||||
/**
|
||||
* 异步检查并触发设备上电任务(在收到第一个数据包时)
|
||||
* @param serialNumber 设备编号
|
||||
*/
|
||||
void checkAndTriggerPowerOnTaskAsync(String serialNumber);
|
||||
|
||||
}
|
||||
|
||||
@ -16,11 +16,13 @@ import com.fastbee.common.core.redis.RedisKeyBuilder;
|
||||
import com.fastbee.common.enums.*;
|
||||
import com.fastbee.common.exception.ServiceException;
|
||||
import com.fastbee.common.utils.DateUtils;
|
||||
import com.fastbee.common.utils.StringUtils;
|
||||
import com.fastbee.common.utils.gateway.mq.TopicsUtils;
|
||||
import com.fastbee.iot.cache.IOtaTaskCache;
|
||||
import com.fastbee.iot.data.service.IDataHandler;
|
||||
import com.fastbee.iot.data.service.IDeviceReportMessageService;
|
||||
import com.fastbee.iot.data.service.IFirmwareTaskDetailService;
|
||||
import com.fastbee.iot.domain.Device;
|
||||
import com.fastbee.iot.domain.FunctionLog;
|
||||
import com.fastbee.iot.domain.Product;
|
||||
import com.fastbee.iot.domain.ProductAuthorize;
|
||||
@ -40,6 +42,8 @@ import io.netty.buffer.Unpooled;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@ -80,12 +84,20 @@ public class DeviceReportMessageServiceImpl implements IDeviceReportMessageServi
|
||||
@Resource
|
||||
private MqttRemoteManager remoteManager;
|
||||
|
||||
@Resource
|
||||
private ApplicationContext applicationContext; // self注入,用于异步调用
|
||||
|
||||
/**
|
||||
* 处理设备主动上报数据
|
||||
*/
|
||||
@Override
|
||||
public void parseReportMsg(DeviceReportBo bo) {
|
||||
String serialNumber = bo.getSerialNumber();
|
||||
|
||||
// 异步检查是否需要触发上电任务(收到第一个数据包时触发)
|
||||
DeviceReportMessageServiceImpl bean = applicationContext.getBean(DeviceReportMessageServiceImpl.class);
|
||||
bean.checkAndTriggerPowerOnTaskAsync(serialNumber);
|
||||
|
||||
switch (bo.getServerType()) {
|
||||
case MQTT:
|
||||
log.debug("=>MQ*收到设备主题[{}],消息:[{}]", bo.getTopicName(), bo.getData());
|
||||
@ -233,6 +245,43 @@ public class DeviceReportMessageServiceImpl implements IDeviceReportMessageServi
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步检查并触发设备上电任务(在收到第一个数据包时)
|
||||
* @param serialNumber 设备编号
|
||||
*/
|
||||
@Async(FastBeeConstant.TASK.TRIGGER_POWER_ON_TASK_ASYNC)
|
||||
public void checkAndTriggerPowerOnTaskAsync(String serialNumber) {
|
||||
try {
|
||||
if (StringUtils.isEmpty(serialNumber)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String cacheKey = RedisKeyBuilder.buildPowerOnTriggerPendingKey(serialNumber.toUpperCase());
|
||||
// 检查是否有待触发的上电任务标记
|
||||
Object pendingFlag = redisCache.getCacheObject(cacheKey);
|
||||
if (pendingFlag == null) {
|
||||
// 没有待触发的任务,直接返回
|
||||
return;
|
||||
}
|
||||
|
||||
// 有待触发的任务,删除标记(确保只触发一次)
|
||||
redisCache.deleteObject(cacheKey);
|
||||
|
||||
// 获取设备信息
|
||||
Device device = deviceService.selectDeviceBySerialNumber(serialNumber);
|
||||
if (device == null) {
|
||||
log.warn("触发上电任务时设备不存在,设备编号:{}", serialNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
// 触发上电任务(异步执行)
|
||||
log.info("收到设备第一个数据包,触发上电任务,设备编号:{}", serialNumber);
|
||||
deviceService.triggerPowerOnTasks(device);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("检查并触发设备上电任务异常,设备编号:{}", serialNumber, e);
|
||||
} }
|
||||
|
||||
/**
|
||||
* 构建消息
|
||||
*
|
||||
|
||||
@ -7,6 +7,7 @@ import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -84,6 +85,6 @@ public class ModbusJob extends BaseEntity {
|
||||
* 执行时间(仅设备上电触发任务使用,记录任务执行的时间)
|
||||
*/
|
||||
@ApiModelProperty("执行时间")
|
||||
private java.util.Date executeTime;
|
||||
private Date executeTime;
|
||||
|
||||
}
|
||||
|
||||
@ -185,6 +185,18 @@ public interface IDeviceService {
|
||||
*/
|
||||
public int updateDeviceStatusAndLocation(Device device, String ipAddress);
|
||||
|
||||
/**
|
||||
* 标记设备上电任务待处理
|
||||
* @param device 设备信息
|
||||
*/
|
||||
void markPowerOnTriggerPending(Device device);
|
||||
|
||||
/**
|
||||
* 触发设备上电任务(供外部调用,如收到第一个数据包时)
|
||||
* @param device 设备信息
|
||||
*/
|
||||
public void triggerPowerOnTasks(Device device);
|
||||
|
||||
/**
|
||||
* 更新设备状态
|
||||
*
|
||||
|
||||
@ -57,6 +57,7 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.Caching;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -124,6 +125,9 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
@Resource
|
||||
private IThingsModelService thingsModelService;
|
||||
|
||||
@Resource
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Resource
|
||||
private EventLogMapper eventLogMapper;
|
||||
@Resource
|
||||
@ -1367,24 +1371,74 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
}
|
||||
eventLogMapper.insert(event);
|
||||
|
||||
// 设备上线时,检查并执行设备上电触发任务
|
||||
// 设备上线时,标记需要触发上电任务(等待第一个数据包)
|
||||
if (device.getStatus() == 3) {
|
||||
handlePowerOnTriggerTasks(device);
|
||||
DeviceServiceImpl bean = applicationContext.getBean(DeviceServiceImpl.class);
|
||||
bean.markPowerOnTriggerPending(device);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理设备上电触发任务
|
||||
*
|
||||
* 标记设备需要触发上电任务(等待第一个数据包)
|
||||
* @param device 设备信息
|
||||
*/
|
||||
@Async
|
||||
private void handlePowerOnTriggerTasks(Device device) {
|
||||
@Async(FastBeeConstant.TASK.TRIGGER_POWER_ON_TASK_ASYNC)
|
||||
@Override
|
||||
public void markPowerOnTriggerPending(Device device) {
|
||||
try {
|
||||
// 延迟2秒执行,确保设备完全上线
|
||||
//Thread.sleep(2000);
|
||||
// 查询该设备的所有设备上电触发任务
|
||||
DeviceJob queryJob = new DeviceJob();
|
||||
queryJob.setDeviceId(device.getDeviceId());
|
||||
queryJob.setJobType(5); // modbus轮询任务
|
||||
queryJob.setStatus("0"); // 正常状态
|
||||
|
||||
List<DeviceJob> jobList = deviceJobService.selectJobList(queryJob);
|
||||
if (CollectionUtils.isEmpty(jobList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否有上电触发任务
|
||||
boolean hasPowerOnTrigger = false;
|
||||
for (DeviceJob job : jobList) {
|
||||
if (isPowerOnTrigger(job.getRemark())) {
|
||||
hasPowerOnTrigger = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有上电触发任务,则在Redis中标记,等待第一个数据包
|
||||
// 无论标记是否存在,都重新设置标记,确保恢复任务后能重新触发
|
||||
if (hasPowerOnTrigger) {
|
||||
String serialNumber = device.getSerialNumber().toUpperCase();
|
||||
String cacheKey = RedisKeyBuilder.buildPowerOnTriggerPendingKey(serialNumber);
|
||||
// 设置过期时间为10分钟,防止长时间未收到数据包导致标记一直存在
|
||||
// 无论之前是否有标记,都重新设置,确保恢复任务后能重新触发
|
||||
redisCache.setCacheObject(cacheKey, "1", 600, TimeUnit.SECONDS);
|
||||
log.info("标记设备需要触发上电任务,等待第一个数据包,设备编号:{}", serialNumber);
|
||||
} else {
|
||||
// 如果没有上电触发任务,清除可能存在的旧标记
|
||||
String serialNumber = device.getSerialNumber().toUpperCase();
|
||||
String cacheKey = RedisKeyBuilder.buildPowerOnTriggerPendingKey(serialNumber);
|
||||
if (redisCache.hasKey(cacheKey)) {
|
||||
redisCache.deleteObject(cacheKey);
|
||||
log.debug("清除设备上电触发任务标记,设备编号:{}", serialNumber);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("标记设备上电触发任务异常,设备编号:{}", device.getSerialNumber(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发设备上电任务(收到第一个数据包时)
|
||||
* @param device 设备信息
|
||||
*/
|
||||
public void triggerPowerOnTasks(Device device) {
|
||||
try {
|
||||
// 延迟1秒执行,确保设备完全上线并准备好接收指令
|
||||
//Thread.sleep(1000);
|
||||
|
||||
// 查询该设备的所有设备上电触发任务
|
||||
DeviceJob queryJob = new DeviceJob();
|
||||
@ -1712,7 +1766,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
*/
|
||||
private void setLatitudeAndLongitude(String city, Device device) throws UnsupportedEncodingException {
|
||||
String BAIDU_URL = "http://api.map.baidu.com/geocoding/v3/";
|
||||
city = StrUtil.isEmpty(city) ? "江苏省海安市城东镇" : city;
|
||||
city = StrUtil.isEmpty(city) ? "湖南省长沙市岳麓区" : city;
|
||||
String encodedAddress = URLEncoder.encode(city, StandardCharsets.UTF_8.toString());
|
||||
String baiduResponse = HttpUtils.sendGet(BAIDU_URL, "address=" + encodedAddress + "&output=json&ak=" + baiduAK, Constants.UTF8);
|
||||
if (!StringUtils.isEmpty(baiduResponse)) {
|
||||
|
||||
@ -15,7 +15,7 @@ VUE_APP_SERVER_API_URL = http://localhost:8080
|
||||
VUE_APP_MQTT_SERVER_URL = ws://localhost:8083/mqtt
|
||||
|
||||
# 百度地图 API Key
|
||||
VUE_APP_BAI_DU_AK = hxAMWdceoYQ2laSOzL1OEddHj4fx3Ah9
|
||||
VUE_APP_BAI_DU_AK = s9mUXN9a7bKgVu6U6Z3bXhKSxtz97YPI
|
||||
|
||||
# OSS API 地址(生产环境使用)
|
||||
VUE_APP_OSS_API =
|
||||
|
||||
@ -14,7 +14,10 @@
|
||||
>
|
||||
<!-- 头部:城市名称、日期与星期 -->
|
||||
<div class="weather-header">
|
||||
<h2>{{ weatherData.city }}</h2>
|
||||
<h2 style="cursor: pointer; display: inline-flex; align-items: center; gap: 8px;" @click="openCityDialog">
|
||||
{{ weatherData.city }}
|
||||
<i class="el-icon-edit" style="font-size: 16px; opacity: 0.7;"></i>
|
||||
</h2>
|
||||
<div class="date-week">
|
||||
<span>{{ weatherData.data.date }}</span>
|
||||
<span>{{ weatherData.data.week }}</span>
|
||||
@ -242,6 +245,36 @@
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 城市选择对话框 -->
|
||||
<el-dialog title="选择城市" :visible.sync="showCityDialog" width="400px" append-to-body>
|
||||
<el-input
|
||||
v-model="cityInput"
|
||||
placeholder="请输入城市名称,如:北京市、上海市、广州市等"
|
||||
style="margin-bottom: 20px;"
|
||||
@keyup.enter.native="confirmCity"
|
||||
>
|
||||
<template slot="prepend">城市</template>
|
||||
</el-input>
|
||||
<div style="margin-bottom: 20px; color: #909399; font-size: 12px;">
|
||||
<p>常用城市:</p>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px;">
|
||||
<el-tag
|
||||
v-for="city in commonCities"
|
||||
:key="city"
|
||||
@click="selectCity(city)"
|
||||
style="cursor: pointer;"
|
||||
effect="plain"
|
||||
>
|
||||
{{ city }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="showCityDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmCity">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-card style="text-align: center; font-size: 14px; color: #666; line-height: 32px; margin: 10px 10px 0px 10px; background-color: #ffffff; border-radius: 15px">
|
||||
<span>
|
||||
Copyright © 2021-2024
|
||||
@ -276,20 +309,40 @@ import { searchUserCount } from '@/api/monitor/jobLog';
|
||||
import { getNettyMqttStats, statisticNettyMqtt } from '@/api/iot/netty';
|
||||
// import { count } from 'echarts/types/src/component/dataZoom/history.js';
|
||||
|
||||
// 获取当前日期和星期
|
||||
function getCurrentDateAndWeek() {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
const date = `${year}-${month}-${day}`;
|
||||
|
||||
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||||
const week = weekDays[now.getDay()];
|
||||
|
||||
return { date, week };
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'Index',
|
||||
components: {
|
||||
CountTo,
|
||||
},
|
||||
data() {
|
||||
// 获取当前日期和星期
|
||||
const { date, week } = getCurrentDateAndWeek();
|
||||
|
||||
// 从localStorage读取保存的城市,如果没有则使用默认值
|
||||
const savedCity = localStorage.getItem('weatherCity') || '北京市';
|
||||
|
||||
return {
|
||||
//天气信息
|
||||
weatherData: {
|
||||
success: true,
|
||||
city: '北京市',
|
||||
city: savedCity,
|
||||
data: {
|
||||
date: '2024-03-14',
|
||||
week: '星期四',
|
||||
date: date,
|
||||
week: week,
|
||||
type: '晴',
|
||||
typeIcon: require('../../src/assets/icons/qing.png'), // 确保路径正确
|
||||
low: '5°C',
|
||||
@ -304,6 +357,11 @@ export default {
|
||||
},
|
||||
tip: '天有点冷,注意保暖~',
|
||||
},
|
||||
// 城市选择对话框
|
||||
showCityDialog: false,
|
||||
cityInput: '',
|
||||
// 常用城市列表
|
||||
commonCities: ['北京市', '上海市', '广州市', '深圳市', '杭州市', '南京市', '成都市', '武汉市', '西安市', '重庆市', '天津市', '苏州市', '郑州市', '长沙市', '东莞市', '佛山市', '宁波市', '无锡市', '青岛市', '大连市'],
|
||||
// mqtt状态数据
|
||||
stats: {},
|
||||
// mqtt统计信息
|
||||
@ -407,7 +465,16 @@ export default {
|
||||
getChartData() {
|
||||
searchUserCount().then((response) => {
|
||||
console.log('折线图信息', response);
|
||||
response.data.reverse().forEach((item) => {
|
||||
// 清空之前的数据
|
||||
this.linechart.date = [];
|
||||
this.linechart.counts = [];
|
||||
|
||||
// 按日期排序(从早到晚)
|
||||
const sortedData = [...response.data].sort((a, b) => {
|
||||
return new Date(a.datetime) - new Date(b.datetime);
|
||||
});
|
||||
|
||||
sortedData.forEach((item) => {
|
||||
this.linechart.date.push(item.datetime);
|
||||
this.linechart.counts.push(item.user_count);
|
||||
});
|
||||
@ -561,58 +628,134 @@ export default {
|
||||
},
|
||||
|
||||
// 天气
|
||||
async fetchWeather() {
|
||||
async fetchWeather(cityName) {
|
||||
this.isLoading = true;
|
||||
this.error = null;
|
||||
// 获取当前日期和星期作为默认值
|
||||
const { date, week } = getCurrentDateAndWeek();
|
||||
|
||||
// 使用传入的城市名或当前保存的城市
|
||||
const city = cityName || this.weatherData.city;
|
||||
|
||||
try {
|
||||
const response = await axios.get('https://api.vvhan.com/api/weather');
|
||||
// 尝试使用城市参数调用API,如果API不支持则使用默认调用
|
||||
let apiUrl = 'https://api.vvhan.com/api/weather';
|
||||
// 某些天气API支持城市参数,这里尝试添加
|
||||
// 如果API不支持,可以尝试其他天气API或使用IP定位
|
||||
try {
|
||||
// 尝试带城市参数的API调用
|
||||
const response = await axios.get(apiUrl, {
|
||||
params: { city: city }
|
||||
});
|
||||
if (response.data && response.data.success) {
|
||||
this.updateWeatherData(response.data, date, week, city);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// 如果带参数的调用失败,尝试不带参数的调用
|
||||
console.log('带城市参数的API调用失败,尝试默认调用');
|
||||
}
|
||||
|
||||
// 默认API调用(可能不支持城市参数,使用IP定位)
|
||||
const response = await axios.get(apiUrl);
|
||||
if (response.data && response.data.success) {
|
||||
console.log(response.data);
|
||||
this.weatherData = {
|
||||
success: true,
|
||||
city: response.data.city,
|
||||
data: {
|
||||
date: response.data.data.date,
|
||||
week: response.data.data.week,
|
||||
type: response.data.data.type,
|
||||
typeIcon: this.getTypeIcon(response.data.data.type),
|
||||
low: response.data.data.low,
|
||||
high: response.data.data.high,
|
||||
fengxiang: response.data.data.fengxiang,
|
||||
fengli: response.data.data.fengli,
|
||||
night: {
|
||||
type: response.data.data.night.type,
|
||||
fengxiang: response.data.data.night.fengxiang,
|
||||
fengli: response.data.data.night.fengli,
|
||||
},
|
||||
},
|
||||
air: {
|
||||
aqi: response.data.air.aqi,
|
||||
aqi_level: response.data.air.aqi_level,
|
||||
aqi_name: response.data.air.aqi_name,
|
||||
co: response.data.air.co,
|
||||
no2: response.data.air.no2,
|
||||
o3: response.data.air.o3,
|
||||
pm10: response.data.air.pm10,
|
||||
pm2_5: response.data.air.pm2_5,
|
||||
so2: response.data.air.so2,
|
||||
},
|
||||
tip: response.data.tip,
|
||||
};
|
||||
// 动态设置背景颜色
|
||||
const background = this.getBackgroundColor(response.data.data.type);
|
||||
document.documentElement.style.setProperty('--background-start', background.start);
|
||||
document.documentElement.style.setProperty('--background-end', background.end);
|
||||
// 如果API返回的城市与设置的城市不同,使用设置的城市名称
|
||||
const apiCity = response.data.city || city;
|
||||
this.updateWeatherData(response.data, date, week, city);
|
||||
} else {
|
||||
// API返回失败,使用当前日期更新
|
||||
this.weatherData.data.date = date;
|
||||
this.weatherData.data.week = week;
|
||||
this.weatherData.city = city; // 保持用户设置的城市
|
||||
this.error = '获取天气数据失败,请稍后重试。';
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
// API调用失败,使用当前日期更新
|
||||
this.weatherData.data.date = date;
|
||||
this.weatherData.data.week = week;
|
||||
this.weatherData.city = city; // 保持用户设置的城市
|
||||
this.error = '无法连接到天气服务,请检查网络或稍后再试。';
|
||||
} finally {
|
||||
this.isLoading = false;
|
||||
}
|
||||
},
|
||||
// 更新天气数据
|
||||
updateWeatherData(responseData, date, week, city) {
|
||||
this.weatherData = {
|
||||
success: true,
|
||||
city: city, // 使用用户设置的城市
|
||||
data: {
|
||||
date: responseData.data?.date || date,
|
||||
week: responseData.data?.week || week,
|
||||
type: responseData.data.type,
|
||||
typeIcon: this.getTypeIcon(responseData.data.type),
|
||||
low: responseData.data.low,
|
||||
high: responseData.data.high,
|
||||
fengxiang: responseData.data.fengxiang,
|
||||
fengli: responseData.data.fengli,
|
||||
night: {
|
||||
type: responseData.data.night.type,
|
||||
fengxiang: responseData.data.night.fengxiang,
|
||||
fengli: responseData.data.night.fengli,
|
||||
},
|
||||
},
|
||||
air: {
|
||||
aqi: responseData.air.aqi,
|
||||
aqi_level: responseData.air.aqi_level,
|
||||
aqi_name: responseData.air.aqi_name,
|
||||
co: responseData.air.co,
|
||||
no2: responseData.air.no2,
|
||||
o3: responseData.air.o3,
|
||||
pm10: responseData.air.pm10,
|
||||
pm2_5: responseData.air.pm2_5,
|
||||
so2: responseData.air.so2,
|
||||
},
|
||||
tip: responseData.tip,
|
||||
};
|
||||
// 动态设置背景颜色
|
||||
const background = this.getBackgroundColor(responseData.data.type);
|
||||
document.documentElement.style.setProperty('--background-start', background.start);
|
||||
document.documentElement.style.setProperty('--background-end', background.end);
|
||||
},
|
||||
// 打开城市选择对话框
|
||||
openCityDialog() {
|
||||
this.cityInput = this.weatherData.city;
|
||||
this.showCityDialog = true;
|
||||
},
|
||||
// 选择城市(点击常用城市标签)
|
||||
selectCity(city) {
|
||||
this.cityInput = city;
|
||||
},
|
||||
// 确认城市选择
|
||||
confirmCity() {
|
||||
const city = this.cityInput.trim();
|
||||
if (!city) {
|
||||
this.$message.warning('请输入城市名称');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果城市没有变化,直接关闭对话框
|
||||
if (city === this.weatherData.city) {
|
||||
this.showCityDialog = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存城市到localStorage
|
||||
localStorage.setItem('weatherCity', city);
|
||||
|
||||
// 更新当前城市
|
||||
this.weatherData.city = city;
|
||||
|
||||
// 关闭对话框
|
||||
this.showCityDialog = false;
|
||||
this.cityInput = '';
|
||||
|
||||
// 重新获取天气数据
|
||||
this.fetchWeather(city);
|
||||
|
||||
this.$message.success(`已切换到 ${city} 的天气信息`);
|
||||
},
|
||||
getBackgroundColor(type) {
|
||||
const backgrounds = {
|
||||
晴: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user