20251225:设备上电触发任务修改

This commit is contained in:
George 2025-12-25 08:56:24 +08:00
parent 97da219ff1
commit 1e70af6110
10 changed files with 340 additions and 54 deletions

View File

@ -92,6 +92,12 @@ public class DeviceTask {
return builder(FastBeeConstant.TASK.DEVICE_TEST_TASK); 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) { private ThreadPoolTaskExecutor builder(String threadNamePrefix) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

View File

@ -96,6 +96,8 @@ public interface FastBeeConstant {
/*OTA升级线程池*/ /*OTA升级线程池*/
String OTA_THREAD_POOL = "otaThreadPoolTaskExecutor"; String OTA_THREAD_POOL = "otaThreadPoolTaskExecutor";
// 设备上电即触发异步任务 TriggerPowerOnTaskAsync
String TRIGGER_POWER_ON_TASK_ASYNC = "triggerPowerOnTaskAsync";
} }
interface MQTT { interface MQTT {
@ -280,6 +282,11 @@ public interface FastBeeConstant {
*/ */
String POWER_ON_TRIGGER_COMMAND = "power:on:trigger:command:"; String POWER_ON_TRIGGER_COMMAND = "power:on:trigger:command:";
/**
* 设备上电触发任务待触发标记
*/
String POWER_ON_TRIGGER_PENDING = "power:on:trigger:pending:";
} }
interface PROTOCOL { interface PROTOCOL {

View File

@ -166,5 +166,13 @@ public class RedisKeyBuilder {
return FastBeeConstant.REDIS.POWER_ON_TRIGGER_COMMAND + serialNumber.toUpperCase(); 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();
}
} }

View File

@ -47,4 +47,10 @@ public interface IDeviceReportMessageService {
*/ */
IProtocol selectedProtocol(Long productId); IProtocol selectedProtocol(Long productId);
/**
* 异步检查并触发设备上电任务(在收到第一个数据包时)
* @param serialNumber 设备编号
*/
void checkAndTriggerPowerOnTaskAsync(String serialNumber);
} }

View File

@ -16,11 +16,13 @@ import com.fastbee.common.core.redis.RedisKeyBuilder;
import com.fastbee.common.enums.*; import com.fastbee.common.enums.*;
import com.fastbee.common.exception.ServiceException; import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.utils.DateUtils; import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.gateway.mq.TopicsUtils; import com.fastbee.common.utils.gateway.mq.TopicsUtils;
import com.fastbee.iot.cache.IOtaTaskCache; import com.fastbee.iot.cache.IOtaTaskCache;
import com.fastbee.iot.data.service.IDataHandler; import com.fastbee.iot.data.service.IDataHandler;
import com.fastbee.iot.data.service.IDeviceReportMessageService; import com.fastbee.iot.data.service.IDeviceReportMessageService;
import com.fastbee.iot.data.service.IFirmwareTaskDetailService; import com.fastbee.iot.data.service.IFirmwareTaskDetailService;
import com.fastbee.iot.domain.Device;
import com.fastbee.iot.domain.FunctionLog; import com.fastbee.iot.domain.FunctionLog;
import com.fastbee.iot.domain.Product; import com.fastbee.iot.domain.Product;
import com.fastbee.iot.domain.ProductAuthorize; import com.fastbee.iot.domain.ProductAuthorize;
@ -40,6 +42,8 @@ import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -79,6 +83,9 @@ public class DeviceReportMessageServiceImpl implements IDeviceReportMessageServi
private TopicsUtils topicsUtils; private TopicsUtils topicsUtils;
@Resource @Resource
private MqttRemoteManager remoteManager; private MqttRemoteManager remoteManager;
@Resource
private ApplicationContext applicationContext; // self注入,用于异步调用
/** /**
* 处理设备主动上报数据 * 处理设备主动上报数据
@ -86,6 +93,11 @@ public class DeviceReportMessageServiceImpl implements IDeviceReportMessageServi
@Override @Override
public void parseReportMsg(DeviceReportBo bo) { public void parseReportMsg(DeviceReportBo bo) {
String serialNumber = bo.getSerialNumber(); String serialNumber = bo.getSerialNumber();
// 异步检查是否需要触发上电任务(收到第一个数据包时触发)
DeviceReportMessageServiceImpl bean = applicationContext.getBean(DeviceReportMessageServiceImpl.class);
bean.checkAndTriggerPowerOnTaskAsync(serialNumber);
switch (bo.getServerType()) { switch (bo.getServerType()) {
case MQTT: case MQTT:
log.debug("=>MQ*收到设备主题[{}],消息:[{}]", bo.getTopicName(), bo.getData()); 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);
} }
/** /**
* 构建消息 * 构建消息
* *

View File

@ -7,6 +7,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.util.Date;
import java.util.List; import java.util.List;
/** /**
@ -84,6 +85,6 @@ public class ModbusJob extends BaseEntity {
* 执行时间(仅设备上电触发任务使用,记录任务执行的时间) * 执行时间(仅设备上电触发任务使用,记录任务执行的时间)
*/ */
@ApiModelProperty("执行时间") @ApiModelProperty("执行时间")
private java.util.Date executeTime; private Date executeTime;
} }

View File

@ -185,6 +185,18 @@ public interface IDeviceService {
*/ */
public int updateDeviceStatusAndLocation(Device device, String ipAddress); public int updateDeviceStatusAndLocation(Device device, String ipAddress);
/**
* 标记设备上电任务待处理
* @param device 设备信息
*/
void markPowerOnTriggerPending(Device device);
/**
* 触发设备上电任务(供外部调用,如收到第一个数据包时)
* @param device 设备信息
*/
public void triggerPowerOnTasks(Device device);
/** /**
* 更新设备状态 * 更新设备状态
* *

View File

@ -57,6 +57,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching; import org.springframework.cache.annotation.Caching;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -124,6 +125,9 @@ public class DeviceServiceImpl implements IDeviceService {
@Resource @Resource
private IThingsModelService thingsModelService; private IThingsModelService thingsModelService;
@Resource
private ApplicationContext applicationContext;
@Resource @Resource
private EventLogMapper eventLogMapper; private EventLogMapper eventLogMapper;
@Resource @Resource
@ -1367,24 +1371,74 @@ public class DeviceServiceImpl implements IDeviceService {
} }
eventLogMapper.insert(event); eventLogMapper.insert(event);
// 设备上线时,检查并执行设备上电触发任务 // 设备上线时,标记需要触发上电任务(等待第一个数据包)
if (device.getStatus() == 3) { if (device.getStatus() == 3) {
handlePowerOnTriggerTasks(device); DeviceServiceImpl bean = applicationContext.getBean(DeviceServiceImpl.class);
bean.markPowerOnTriggerPending(device);
} }
return result; return result;
} }
/** /**
* 处理设备上电触发任务 * 标记设备需要触发上电任务(等待第一个数据包)
*
* @param device 设备信息 * @param device 设备信息
*/ */
@Async @Async(FastBeeConstant.TASK.TRIGGER_POWER_ON_TASK_ASYNC)
private void handlePowerOnTriggerTasks(Device device) { @Override
public void markPowerOnTriggerPending(Device device) {
try { 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(); DeviceJob queryJob = new DeviceJob();
@ -1712,7 +1766,7 @@ public class DeviceServiceImpl implements IDeviceService {
*/ */
private void setLatitudeAndLongitude(String city, Device device) throws UnsupportedEncodingException { private void setLatitudeAndLongitude(String city, Device device) throws UnsupportedEncodingException {
String BAIDU_URL = "http://api.map.baidu.com/geocoding/v3/"; 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 encodedAddress = URLEncoder.encode(city, StandardCharsets.UTF_8.toString());
String baiduResponse = HttpUtils.sendGet(BAIDU_URL, "address=" + encodedAddress + "&output=json&ak=" + baiduAK, Constants.UTF8); String baiduResponse = HttpUtils.sendGet(BAIDU_URL, "address=" + encodedAddress + "&output=json&ak=" + baiduAK, Constants.UTF8);
if (!StringUtils.isEmpty(baiduResponse)) { if (!StringUtils.isEmpty(baiduResponse)) {

View File

@ -15,7 +15,7 @@ VUE_APP_SERVER_API_URL = http://localhost:8080
VUE_APP_MQTT_SERVER_URL = ws://localhost:8083/mqtt VUE_APP_MQTT_SERVER_URL = ws://localhost:8083/mqtt
# 百度地图 API Key # 百度地图 API Key
VUE_APP_BAI_DU_AK = hxAMWdceoYQ2laSOzL1OEddHj4fx3Ah9 VUE_APP_BAI_DU_AK = s9mUXN9a7bKgVu6U6Z3bXhKSxtz97YPI
# OSS API 地址(生产环境使用) # OSS API 地址(生产环境使用)
VUE_APP_OSS_API = VUE_APP_OSS_API =

View File

@ -14,7 +14,10 @@
> >
<!-- 头部:城市名称、日期与星期 --> <!-- 头部:城市名称、日期与星期 -->
<div class="weather-header"> <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"> <div class="date-week">
<span>{{ weatherData.data.date }}</span> <span>{{ weatherData.data.date }}</span>
<span>{{ weatherData.data.week }}</span> <span>{{ weatherData.data.week }}</span>
@ -242,6 +245,36 @@
</div> </div>
</el-dialog> </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"> <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> <span>
Copyright © 2021-2024 Copyright © 2021-2024
@ -276,20 +309,40 @@ import { searchUserCount } from '@/api/monitor/jobLog';
import { getNettyMqttStats, statisticNettyMqtt } from '@/api/iot/netty'; import { getNettyMqttStats, statisticNettyMqtt } from '@/api/iot/netty';
// import { count } from 'echarts/types/src/component/dataZoom/history.js'; // 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 { export default {
name: 'Index', name: 'Index',
components: { components: {
CountTo, CountTo,
}, },
data() { data() {
// 获取当前日期和星期
const { date, week } = getCurrentDateAndWeek();
// 从localStorage读取保存的城市,如果没有则使用默认值
const savedCity = localStorage.getItem('weatherCity') || '北京市';
return { return {
//天气信息 //天气信息
weatherData: { weatherData: {
success: true, success: true,
city: '北京市', city: savedCity,
data: { data: {
date: '2024-03-14', date: date,
week: '星期四', week: week,
type: '晴', type: '晴',
typeIcon: require('../../src/assets/icons/qing.png'), // 确保路径正确 typeIcon: require('../../src/assets/icons/qing.png'), // 确保路径正确
low: '5°C', low: '5°C',
@ -304,6 +357,11 @@ export default {
}, },
tip: '天有点冷,注意保暖~', tip: '天有点冷,注意保暖~',
}, },
// 城市选择对话框
showCityDialog: false,
cityInput: '',
// 常用城市列表
commonCities: ['北京市', '上海市', '广州市', '深圳市', '杭州市', '南京市', '成都市', '武汉市', '西安市', '重庆市', '天津市', '苏州市', '郑州市', '长沙市', '东莞市', '佛山市', '宁波市', '无锡市', '青岛市', '大连市'],
// mqtt状态数据 // mqtt状态数据
stats: {}, stats: {},
// mqtt统计信息 // mqtt统计信息
@ -407,7 +465,16 @@ export default {
getChartData() { getChartData() {
searchUserCount().then((response) => { searchUserCount().then((response) => {
console.log('折线图信息', 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.date.push(item.datetime);
this.linechart.counts.push(item.user_count); this.linechart.counts.push(item.user_count);
}); });
@ -561,58 +628,134 @@ export default {
}, },
// 天气 // 天气
async fetchWeather() { async fetchWeather(cityName) {
this.isLoading = true; this.isLoading = true;
this.error = null; this.error = null;
// 获取当前日期和星期作为默认值
const { date, week } = getCurrentDateAndWeek();
// 使用传入的城市名或当前保存的城市
const city = cityName || this.weatherData.city;
try { 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) { if (response.data && response.data.success) {
console.log(response.data); // 如果API返回的城市与设置的城市不同,使用设置的城市名称
this.weatherData = { const apiCity = response.data.city || city;
success: true, this.updateWeatherData(response.data, date, week, city);
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);
} else { } else {
// API返回失败,使用当前日期更新
this.weatherData.data.date = date;
this.weatherData.data.week = week;
this.weatherData.city = city; // 保持用户设置的城市
this.error = '获取天气数据失败,请稍后重试。'; this.error = '获取天气数据失败,请稍后重试。';
} }
} catch (err) { } catch (err) {
console.error(err); console.error(err);
// API调用失败,使用当前日期更新
this.weatherData.data.date = date;
this.weatherData.data.week = week;
this.weatherData.city = city; // 保持用户设置的城市
this.error = '无法连接到天气服务,请检查网络或稍后再试。'; this.error = '无法连接到天气服务,请检查网络或稍后再试。';
} finally { } finally {
this.isLoading = false; 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) { getBackgroundColor(type) {
const backgrounds = { const backgrounds = {
晴: { 晴: {