From 959faaf2a85a9bac7d78df2f41fcb685b53cc16f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9C=B1?= <10714957+xiao-zhu245@user.noreply.gitee.com> Date: Fri, 11 Jul 2025 14:47:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=A9=E6=B0=94=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pages/HomePage.tsx | 161 +++++++++++++++++++++++++++++- client/src/pages/SettingsPage.tsx | 76 +++++++++++++- client/src/types/index.ts | 45 +++++++++ server/src/index.ts | 4 + server/src/routes/weather.ts | 79 +++++++++++++++ 5 files changed, 360 insertions(+), 5 deletions(-) create mode 100644 server/src/routes/weather.ts diff --git a/client/src/pages/HomePage.tsx b/client/src/pages/HomePage.tsx index 1dda0de..2769209 100644 --- a/client/src/pages/HomePage.tsx +++ b/client/src/pages/HomePage.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import { useAuthStore } from '@/stores/authStore' -import { SystemStats, SystemInfo, ProcessInfo } from '@/types' +import { SystemStats, SystemInfo, ProcessInfo, WeatherData } from '@/types' import socketClient from '@/utils/socket' import apiClient from '@/utils/api' import { @@ -15,6 +15,44 @@ import { ArrowRight } from 'lucide-react' +// 城市代码映射 +const cityOptions = [ + { value: '101010100', label: '北京市' }, + { value: '101020100', label: '上海市' }, + { value: '101280101', label: '广州市' }, + { value: '101280601', label: '深圳市' }, + { value: '101210101', label: '杭州市' }, + { value: '101030100', label: '天津市' }, + { value: '101200101', label: '武汉市' }, + { value: '101270101', label: '成都市' }, + { value: '101110101', label: '西安市' }, + { value: '101190401', label: '苏州市' }, + { value: '101230101', label: '福州市' }, + { value: '101040100', label: '重庆市' }, + { value: '101250101', label: '长沙市' }, + { value: '101230201', label: '厦门市' }, + { value: '101180101', label: '郑州市' }, + { value: '101120101', label: '济南市' }, + { value: '101190101', label: '南京市' }, + { value: '101260101', label: '合肥市' }, + { value: '101300101', label: '南宁市' }, + { value: '101310101', label: '海口市' }, + { value: '101320101', label: '石家庄市' }, + { value: '101330101', label: '太原市' }, + { value: '101340101', label: '沈阳市' }, + { value: '101050101', label: '哈尔滨市' }, + { value: '101060101', label: '长春市' }, + { value: '101070101', label: '呼和浩特市' }, + { value: '101080101', label: '乌鲁木齐市' }, + { value: '101090101', label: '银川市' }, + { value: '101100101', label: '西宁市' }, + { value: '101150101', label: '兰州市' }, + { value: '101160101', label: '拉萨市' }, + { value: '101240101', label: '南昌市' }, + { value: '101290101', label: '昆明市' }, + { value: '101170101', label: '贵阳市' } +] + const HomePage: React.FC = () => { const { user } = useAuthStore() const navigate = useNavigate() @@ -22,6 +60,9 @@ const HomePage: React.FC = () => { const [systemInfo, setSystemInfo] = useState(null) const [processList, setProcessList] = useState([]) const [connected, setConnected] = useState(socketClient.isConnected()) + const [currentDateTime, setCurrentDateTime] = useState(new Date()) + const [weatherData, setWeatherData] = useState(null) + const [weatherLoading, setWeatherLoading] = useState(false) useEffect(() => { // 获取系统信息 @@ -54,6 +95,58 @@ const HomePage: React.FC = () => { // 设置定时刷新终端进程列表 const processInterval = setInterval(fetchTerminalProcesses, 5000) // 每5秒刷新一次 + // 设置定时更新日期时间 + const dateTimeInterval = setInterval(() => { + setCurrentDateTime(new Date()) + }, 1000) // 每秒更新一次 + + + + // 获取天气信息 + const fetchWeatherData = async () => { + try { + setWeatherLoading(true) + + // 从localStorage获取天气城市设置 + let weatherCity = '101010100' // 默认北京 + try { + const webSettings = localStorage.getItem('webSettings') + if (webSettings) { + const settings = JSON.parse(webSettings) + if (settings.weatherCity) { + weatherCity = settings.weatherCity + } + } + } catch (error) { + console.warn('读取天气城市设置失败:', error) + } + + const response = await fetch(`/api/weather/current?city=${weatherCity}`) + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`) + } + + const result = await response.json() + + if (result.success && result.data) { + // 添加选择的城市代码到天气数据中 + setWeatherData({ ...result.data, selectedCityCode: weatherCity }) + } else { + throw new Error('天气API返回错误数据') + } + } catch (error) { + console.error('获取天气信息失败:', error) + setWeatherData(null) + } finally { + setWeatherLoading(false) + } + } + + fetchWeatherData() + // 每30分钟更新一次天气信息 + const weatherInterval = setInterval(fetchWeatherData, 30 * 60 * 1000) + // 设置初始连接状态 setConnected(socketClient.isConnected()) @@ -75,6 +168,8 @@ const HomePage: React.FC = () => { socketClient.off('system-stats') socketClient.emit('unsubscribe-system-stats') clearInterval(processInterval) + clearInterval(dateTimeInterval) + clearInterval(weatherInterval) } }, []) @@ -100,8 +195,72 @@ const HomePage: React.FC = () => { return 'bg-green-500' } + const formatDateTime = (date: Date) => { + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const hours = String(date.getHours()).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + const seconds = String(date.getSeconds()).padStart(2, '0') + + return { + date: `${year}年${month}月${day}日`, + time: `${hours}:${minutes}:${seconds}` + } + } + return (
+ {/* 日期时间和天气显示 */} +
+
+ {/* 日期时间 */} +
+
+ {formatDateTime(currentDateTime).date} +
+
+ {formatDateTime(currentDateTime).time} +
+
+ + {/* 分隔线 */} +
+ + {/* 天气信息 */} +
+ {weatherLoading ? ( +
加载中...
+ ) : weatherData ? ( +
+
+ {cityOptions.find(city => city.value === weatherData.selectedCityCode)?.label || weatherData.cityInfo?.city || '北京'} +
+
+ + {weatherData.wendu}°C + + + {weatherData.forecast?.[0]?.type || '晴'} + +
+
+ {weatherData.forecast?.[0]?.low?.replace('低温 ', '')} ~ {weatherData.forecast?.[0]?.high?.replace('高温 ', '')} +
+
+ {weatherData.forecast?.[0]?.fx} {weatherData.forecast?.[0]?.fl} +
+
+ 湿度: {weatherData.shidu} | 空气质量: {weatherData.quality} +
+
+ ) : ( +
天气信息获取失败
+ )} +
+
+
+ {/* 欢迎信息 */}
diff --git a/client/src/pages/SettingsPage.tsx b/client/src/pages/SettingsPage.tsx index fb16650..53b7fe1 100644 --- a/client/src/pages/SettingsPage.tsx +++ b/client/src/pages/SettingsPage.tsx @@ -18,8 +18,9 @@ import { CheckCircle, XCircle, Loader2, - Battery, - Moon + Battery, + Moon, + MapPin } from 'lucide-react' const SettingsPage: React.FC = () => { @@ -27,6 +28,44 @@ const SettingsPage: React.FC = () => { const { user, changePassword, changeUsername } = useAuthStore() const { addNotification } = useNotificationStore() + // 城市选项数据 + const cityOptions = [ + { value: '101010100', label: '北京市' }, + { value: '101020100', label: '上海市' }, + { value: '101280101', label: '广州市' }, + { value: '101280601', label: '深圳市' }, + { value: '101210101', label: '杭州市' }, + { value: '101030100', label: '天津市' }, + { value: '101200101', label: '武汉市' }, + { value: '101270101', label: '成都市' }, + { value: '101110101', label: '西安市' }, + { value: '101190401', label: '苏州市' }, + { value: '101230101', label: '福州市' }, + { value: '101040100', label: '重庆市' }, + { value: '101250101', label: '长沙市' }, + { value: '101230201', label: '厦门市' }, + { value: '101180101', label: '郑州市' }, + { value: '101120101', label: '济南市' }, + { value: '101190101', label: '南京市' }, + { value: '101260101', label: '合肥市' }, + { value: '101300101', label: '南宁市' }, + { value: '101310101', label: '海口市' }, + { value: '101320101', label: '石家庄市' }, + { value: '101330101', label: '太原市' }, + { value: '101340101', label: '沈阳市' }, + { value: '101050101', label: '哈尔滨市' }, + { value: '101060101', label: '长春市' }, + { value: '101070101', label: '呼和浩特市' }, + { value: '101080101', label: '乌鲁木齐市' }, + { value: '101090101', label: '银川市' }, + { value: '101100101', label: '西宁市' }, + { value: '101150101', label: '兰州市' }, + { value: '101160101', label: '拉萨市' }, + { value: '101240101', label: '南昌市' }, + { value: '101290101', label: '昆明市' }, + { value: '101170101', label: '贵阳市' } + ] + // 密码修改状态 const [passwordForm, setPasswordForm] = useState({ oldPassword: '', @@ -50,7 +89,8 @@ const SettingsPage: React.FC = () => { enableLowPowerMode: true, lowPowerModeTimeout: 60, // 秒 enableDeepSleep: true, - deepSleepTimeout: 10 // 秒 + deepSleepTimeout: 10, // 秒 + weatherCity: '101010100' // 默认北京 }) // SteamCMD设置状态 @@ -447,7 +487,8 @@ const SettingsPage: React.FC = () => { enableLowPowerMode: true, lowPowerModeTimeout: 60, enableDeepSleep: true, - deepSleepTimeout: 10 + deepSleepTimeout: 10, + weatherCity: '101010100' } setWebSettings(defaultWebSettings) @@ -606,6 +647,33 @@ const SettingsPage: React.FC = () => { )}
+ {/* 天气地理位置 */} +
+
+ +
+ +

选择首页显示的天气城市

+
+
+ + + +

+ 当前选择: {cityOptions.find(city => city.value === webSettings.weatherCity)?.label || '未知城市'} +

+
+

当前主题: {theme === 'dark' ? '深色模式' : '浅色模式'} diff --git a/client/src/types/index.ts b/client/src/types/index.ts index 63253af..6e34b86 100644 --- a/client/src/types/index.ts +++ b/client/src/types/index.ts @@ -176,6 +176,51 @@ export interface NotificationState { clearNotifications: () => void } +// 天气相关类型 +export interface WeatherData { + cityInfo: { + city: string + cityId: string + parent: string + updateTime: string + } + wendu: string + shidu: string + pm25: number + pm10: number + quality: string + ganmao: string + forecast: Array<{ + date: string + ymd: string + week: string + sunrise: string + high: string + low: string + sunset: string + aqi: number + fx: string + fl: string + type: string + notice: string + }> + yesterday: { + date: string + ymd: string + week: string + sunrise: string + high: string + low: string + sunset: string + aqi: number + fx: string + fl: string + type: string + notice: string + } + selectedCityCode?: string // 用户选择的城市代码 +} + // 设置相关类型 export interface AppSettings { theme: Theme diff --git a/server/src/index.ts b/server/src/index.ts index 8af42f3..5621c87 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -30,6 +30,7 @@ import steamcmdRouter, { setSteamCMDManager } from './routes/steamcmd.js' import gameDeploymentRouter, { setGameDeploymentManagers } from './routes/gameDeployment.js' import minecraftRouter from './routes/minecraft.js' import moreGamesRouter from './routes/moreGames.js' +import weatherRouter from './routes/weather.js' // 获取当前文件目录 const __filename = fileURLToPath(import.meta.url) @@ -485,6 +486,9 @@ async function startServer() { const { setMoreGamesDependencies } = await import('./routes/moreGames.js') setMoreGamesDependencies(io) app.use('/api/more-games', moreGamesRouter) + + // 设置天气路由 + app.use('/api/weather', weatherRouter) // 前端路由处理(SPA支持) app.get('*', (req, res) => { diff --git a/server/src/routes/weather.ts b/server/src/routes/weather.ts new file mode 100644 index 0000000..78fc60d --- /dev/null +++ b/server/src/routes/weather.ts @@ -0,0 +1,79 @@ +import { Router } from 'express' +import axios from 'axios' + +const router = Router() + +// 获取天气信息 +router.get('/current', async (req, res) => { + try { + const { city = '101010100' } = req.query // 默认北京,支持前端传递城市代码 + + // 使用备用的天气API + const response = await axios.get(`http://t.weather.sojson.com/api/weather/city/${city}`, { + timeout: 10000, + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' + } + }) + + if (response.data && response.data.status === 200) { + res.json({ + success: true, + data: response.data.data + }) + } else { + throw new Error('天气API返回错误') + } + } catch (error: any) { + console.error('获取天气信息失败:', error.message) + + // 返回模拟数据作为备用 + res.json({ + success: true, + data: { + cityInfo: { + city: '北京市', + cityId: '101010100', + parent: '北京', + updateTime: new Date().toLocaleTimeString('zh-CN', { hour12: false, hour: '2-digit', minute: '2-digit' }) + }, + wendu: '20', + shidu: '45%', + pm25: 35, + pm10: 50, + quality: '良', + ganmao: '各类人群可自由活动', + forecast: [{ + date: new Date().getDate().toString(), + ymd: new Date().toISOString().split('T')[0], + week: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'][new Date().getDay()], + sunrise: '06:30', + high: '高温 25°C', + low: '低温 15°C', + sunset: '18:30', + aqi: 45, + fx: '西北风', + fl: '3-4级', + type: '晴', + notice: '愿你拥有比阳光明媚的心情' + }], + yesterday: { + date: (new Date().getDate() - 1).toString(), + ymd: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString().split('T')[0], + week: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'][new Date(Date.now() - 24 * 60 * 60 * 1000).getDay()], + sunrise: '06:31', + high: '高温 23°C', + low: '低温 13°C', + sunset: '18:29', + aqi: 42, + fx: '北风', + fl: '2-3级', + type: '多云', + notice: '阴晴之间,谨防紫外线侵扰' + } + } + }) + } +}) + +export default router \ No newline at end of file