diff --git a/.env b/.env new file mode 100644 index 0000000..b8bb093 --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +# 聚合数据API +JUHE_NEWS_KEY=edbc3b96f022b59141961e2137f69b4a + +# 彩云天气API +CAIYUN_API_KEY=TAkhjf8d1nlSlspN \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5ef6a52..3c69b5d 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,8 @@ yarn-error.log* .pnpm-debug.log* # env files (can opt-in for committing if needed) -.env* +#.env* +.env.local # vercel .vercel diff --git a/Dockerfile b/Dockerfile index be597a5..83d0f35 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ COPY package.json ./ # 使用国内镜像源(如果网络连接较慢) #RUN pnpm config set registry https://registry.npm.taobao.org # 安装所有依赖(包括开发依赖) -RUN pnpm install --production=false +# RUN pnpm install --production=false # 安装项目依赖 #RUN pnpm install diff --git a/src/app/api/weather/route.ts b/src/app/api/weather/route.ts new file mode 100644 index 0000000..ecd6cd2 --- /dev/null +++ b/src/app/api/weather/route.ts @@ -0,0 +1,67 @@ +import { NextResponse } from 'next/server'; + +export async function GET() { + const apiKey = process.env.CAIYUN_API_KEY; + // 请替换为你的实际坐标(示例使用北京坐标) + const apiUrl = `https://api.caiyunapp.com/v2.6/${apiKey}/116.3974,39.9093/weather?alert=true&dailysteps=5`; + + try { + const response = await fetch(apiUrl, { next: { revalidate: 600 } }); + const data = await response.json(); + + // 数据格式转换 + const formattedData = { + temp: Math.round(data.result.realtime.temperature), + feelsLike: Math.round(data.result.realtime.apparent_temperature), + condition: translateSkycon(data.result.realtime.skycon), + sunrise: data.result.daily.astro[0].sunrise.time, + sunset: data.result.daily.astro[0].sunset.time, + windSpeed: (data.result.realtime.wind.speed * 3.6).toFixed(1), // 转换为km/h + windDirection: getWindDirection(data.result.realtime.wind.direction), + uvIndex: data.result.realtime.life_index.ultraviolet.desc, + forecast: data.result.daily.temperature.map((item: any, index: number) => ({ + day: formatDailyDate(item.date), + low: Math.round(item.min), + high: Math.round(item.max), + condition: translateSkycon(data.result.daily.skycon[index].value) + })) + }; + + return NextResponse.json(formattedData); + } catch (error) { + return NextResponse.json( + { error: "天气数据获取失败" }, + { status: 500 } + ); + } +} + +// 天气状况翻译 +function translateSkycon(skycon: string) { + const skyconMap: Record = { + "CLEAR_DAY": "晴", + "CLEAR_NIGHT": "晴", + "PARTLY_CLOUDY_DAY": "多云", + "PARTLY_CLOUDY_NIGHT": "多云", + "CLOUDY": "阴", + "LIGHT_RAIN": "小雨", + "MODERATE_RAIN": "中雨", + "HEAVY_RAIN": "大雨", + "STORM_RAIN": "暴雨", + // 其他天气代码可继续补充... + }; + return skyconMap[skycon] || skycon; +} + +// 风向转换 +function getWindDirection(degree: number) { + const directions = ["北风", "东北风", "东风", "东南风", "南风", "西南风", "西风", "西北风"]; + return directions[Math.round(degree % 360 / 45) % 8]; +} + +// 日期格式化 +function formatDailyDate(dateStr: string) { + const date = new Date(dateStr); + const weekdays = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"]; + return weekdays[date.getDay()]; +} \ No newline at end of file diff --git a/src/app/page.tsx b/src/app/page.tsx index 7b72f73..0a55cfc 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -20,23 +20,11 @@ const MagicMirror = () => { const calendarDays = useMemo(() => generateCalendarDays(), []); // 天气示例数据 - const weatherData: WeatherData = { - temp: 24, - feelsLike: 26, - condition: "晴", - sunrise: "06:12", - sunset: "18:34", - windSpeed: 5, - windDirection: "东南风", - uvIndex: "中等", - forecast: [ - { day: "周二", low: 18, high: 26, condition: "☀️" }, - { day: "周三", low: 20, high: 28, condition: "⛅" }, - { day: "周四", low: 19, high: 27, condition: "🌤️" }, - { day: "周五", low: 17, high: 25, condition: "☀️" }, - { day: "周六", low: 16, high: 24, condition: "🌧️" }, - ], - }; + // 替换原有的weatherData定义部分 + const { data: weatherData, error: weatherError } = useSWR( + "/api/weather", + (url: string) => fetch(url).then((res) => res.json()) + ); // 新闻数据 // 替换原有的newsItems定义部分: diff --git a/src/components/WeatherSection.tsx b/src/components/WeatherSection.tsx index 89881bb..8dab77b 100644 --- a/src/components/WeatherSection.tsx +++ b/src/components/WeatherSection.tsx @@ -3,21 +3,62 @@ /** * 天气信息组件 * 包含当前天气和天气预报展示 + * 支持加载状态处理和API数据格式转换 */ import { FC } from "react"; import { WeatherData } from "../types/magic-mirror"; import WbSunnyIcon from "@mui/icons-material/WbSunny"; +import CircularProgress from "@mui/material/CircularProgress"; interface WeatherSectionProps { - data: WeatherData; + data?: WeatherData; // 允许undefined状态(加载中) } const WeatherSection: FC = ({ data }) => { + // 处理加载状态 + if (!data) { + return ( +
+ + 天气加载中... +
+ ); + } + + // 日出日落时间格式化(去除日期部分) + const formatTime = (isoTime: string) => { + try { + return new Date(isoTime).toLocaleTimeString("zh-CN", { + hour: "2-digit", + minute: "2-digit", + hour12: false, + }); + } catch { + return isoTime.split("T")[1]?.slice(0, 5) || isoTime; // 回退处理 + } + }; + + // 获取天气图标 + const getWeatherIcon = (condition: string) => { + const iconMap: Record = { + 晴: "☀️", + 多云: "⛅", + 阴: "☁️", + 小雨: "🌧️", + 中雨: "🌧️", + 大雨: "⛈️", + 暴雨: "🌧️💦", + // 其他天气类型可继续扩展... + }; + return iconMap[condition] || "🌤️"; + }; + return ( <> {/* 当前天气模块 */}
+ {/* 风速和日出日落信息 */}
风速
@@ -28,19 +69,21 @@ const WeatherSection: FC = ({ data }) => {
日出/日落
- {data.sunrise} / {data.sunset} + {formatTime(data.sunrise)} / {formatTime(data.sunset)}
+ {/* 温度显示区域 */}
-
☀️
+
{getWeatherIcon(data.condition)}
{data.temp}°C
体感 {data.feelsLike}°C
+ {/* 紫外线指数 */}
紫外线指数
{data.uvIndex}
@@ -49,16 +92,19 @@ const WeatherSection: FC = ({ data }) => { {/* 天气预报模块 */}
- {data.forecast.map((day, index) => ( + {data.forecast.slice(0, 5).map((day, index) => (
-
{day.day}
-
{day.condition}
-
- {day.low}° {day.high}° +
{day.day}
+
+ {getWeatherIcon(day.condition)} +
+
+ {day.low}° + {day.high}°
))} diff --git a/src/types/magic-mirror.ts b/src/types/magic-mirror.ts index 857a7cc..153910f 100644 --- a/src/types/magic-mirror.ts +++ b/src/types/magic-mirror.ts @@ -3,13 +3,13 @@ * 包含天气、新闻、日历等数据结构的类型定义 */ -export type WeatherData = { +export interface WeatherData { temp: number; feelsLike: number; condition: string; sunrise: string; sunset: string; - windSpeed: number; + windSpeed: string; windDirection: string; uvIndex: string; forecast: { @@ -18,7 +18,7 @@ export type WeatherData = { high: number; condition: string; }[]; -}; +} export interface NewsItem { uniquekey: string;