0228.6
Some checks failed
部署 Next.js 站点到 Gitea / deploy (push) Has been cancelled

This commit is contained in:
298977887 2025-02-28 23:49:57 +08:00
parent 2521db74a9
commit 012715ee81
4 changed files with 86 additions and 60 deletions

View File

@ -1,33 +1,48 @@
// src/app/api/sun/route.ts
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
export async function GET() { export async function GET() {
const apiKey = process.env.QWEATHER_API_KEY; const apiKey = process.env.QWEATHER_API_KEY;
// 使用北京坐标(示例)
const [longitude, latitude] = [116.3974, 39.9093]; const [longitude, latitude] = [116.3974, 39.9093];
const today = new Date(); const today = new Date();
try { try {
// 获取未来6天日出日落数据 // 生成包含今天在内的未来6天日期
const datePromises = Array.from({ length: 6 }).map((_, i) => { const datePromises = Array.from({ length: 6 }).map((_, i) => {
const date = new Date(today); const date = new Date(today);
date.setDate(today.getDate() + i); date.setDate(today.getDate() + i);
return date.toISOString().split('T')[0].replace(/-/g, ''); // 格式化为yyyyMMdd return date;
}); });
const responses = await Promise.all( const responses = await Promise.all(
datePromises.map(date => datePromises.map(date => {
fetch(`https://devapi.qweather.com/v7/astronomy/sun?location=${longitude},${latitude}&date=${date}&key=${apiKey}`) const formattedDate = date.toISOString().split('T')[0].replace(/-/g, '');
) return fetch(
`https://devapi.qweather.com/v7/astronomy/sun?` +
`location=${longitude},${latitude}&date=${formattedDate}&key=${apiKey}`
);
})
); );
// 增强的错误处理
const errors = responses.filter(res => !res.ok);
if (errors.length > 0) {
throw new Error(`API请求失败: ${errors.map(e => e.statusText).join(', ')}`);
}
const sunData = await Promise.all(responses.map(res => res.json())); const sunData = await Promise.all(responses.map(res => res.json()));
// 格式化数据 // 数据格式化和验证
const formattedData = sunData.map((item, index) => ({ const formattedData = sunData.map((item, index) => {
date: new Date(today.getTime() + index * 86400000).toISOString().split('T')[0], const targetDate = new Date(today);
sunrise: formatSunTime(item.sunrise), targetDate.setDate(today.getDate() + index);
sunset: formatSunTime(item.sunset)
})); return {
date: targetDate.toISOString().split('T')[0],
sunrise: safeFormatTime(item.sunrise),
sunset: safeFormatTime(item.sunset)
};
});
return NextResponse.json(formattedData, { return NextResponse.json(formattedData, {
headers: { headers: {
@ -39,22 +54,28 @@ export async function GET() {
} catch (error) { } catch (error) {
console.error('日出日落数据获取失败:', error); console.error('日出日落数据获取失败:', error);
return NextResponse.json( return NextResponse.json(
{ error: "日出日落数据获取失败" }, { error: "数据获取失败,请稍后重试" },
{ status: 500 } { status: 500 }
); );
} }
} }
// 时间格式化函数 // 健壮的时间格式化方法
function formatSunTime(isoTime: string) { function safeFormatTime(timeStr: string) {
// 直接匹配HH:mm格式
const directMatch = timeStr.match(/(\d{2}:\d{2})/);
if (directMatch) return directMatch[1];
// 处理ISO格式
try { try {
const date = new Date(isoTime); const date = new Date(timeStr);
return date.toLocaleTimeString('zh-CN', { return date.toLocaleTimeString('zh-CN', {
hour: '2-digit', hour: '2-digit',
minute: '2-digit', minute: '2-digit',
hour12: false hour12: false
}); });
} catch { } catch {
return isoTime.split('T')[1]?.substring(0, 5) || '--:--'; // 最终处理非常规格式
return timeStr.split('T')[1]?.substring(0, 5) || '--:--';
} }
} }

View File

@ -77,7 +77,7 @@ function translateSkycon(skycon: string) {
"SAND": "沙尘", "SAND": "沙尘",
"WIND": "大风" "WIND": "大风"
}; };
return skyconMap[skycon] || "未知天气"; return skyconMap[skycon] || "未知";
} }
// 风向转换(根据官方文档说明) // 风向转换(根据官方文档说明)

View File

@ -1,7 +1,7 @@
"use client"; "use client";
/** /**
* * src\app\page.tsx
* *
*/ */

View File

@ -1,11 +1,4 @@
"use client"; // src/components/WeatherSection.tsx
/**
*
*
* API数据格式转换
*/
import { FC } from "react"; import { FC } from "react";
import { WeatherData } from "../types/magic-mirror"; import { WeatherData } from "../types/magic-mirror";
import CircularProgress from "@mui/material/CircularProgress"; import CircularProgress from "@mui/material/CircularProgress";
@ -15,7 +8,6 @@ interface WeatherSectionProps {
} }
const WeatherSection: FC<WeatherSectionProps> = ({ data }) => { const WeatherSection: FC<WeatherSectionProps> = ({ data }) => {
// 处理加载状态
if (!data) { if (!data) {
return ( return (
<div className="absolute top-8 right-0 w-64 flex items-center justify-center h-32"> <div className="absolute top-8 right-0 w-64 flex items-center justify-center h-32">
@ -25,21 +17,7 @@ const WeatherSection: FC<WeatherSectionProps> = ({ data }) => {
); );
} }
// 日出日落时间格式化(安全访问) // 获取天气图标
const formatTime = (isoTime?: string) => {
if (!isoTime) return "--:--";
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) || "--:--";
}
};
// 获取天气图标(带默认值)
const getWeatherIcon = (condition?: string) => { const getWeatherIcon = (condition?: string) => {
const iconMap: Record<string, string> = { const iconMap: Record<string, string> = {
: "☀️", : "☀️",
@ -53,12 +31,8 @@ const WeatherSection: FC<WeatherSectionProps> = ({ data }) => {
return condition ? iconMap[condition] || "🌤️" : "🌤️"; return condition ? iconMap[condition] || "🌤️" : "🌤️";
}; };
// 安全获取天气预报数据
const forecastData = data.forecast?.slice(0, 5) || [];
return ( return (
<> <>
{/* 当前天气模块 */}
<div className="absolute top-8 right-0 w-64 space-y-4"> <div className="absolute top-8 right-0 w-64 space-y-4">
<div className="grid grid-cols-2 gap-4 text-sm"> <div className="grid grid-cols-2 gap-4 text-sm">
<div className="space-y-1"> <div className="space-y-1">
@ -70,10 +44,11 @@ const WeatherSection: FC<WeatherSectionProps> = ({ data }) => {
<div className="space-y-1"> <div className="space-y-1">
<div className="text-gray-400">/</div> <div className="text-gray-400">/</div>
<div className="text-gray-300"> <div className="text-gray-300">
{formatTime(data.sunrise)} / {formatTime(data.sunset)} {data.sunrise || "--:--"} / {data.sunset || "--:--"}
</div> </div>
</div> </div>
</div> </div>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="text-4xl">{getWeatherIcon(data.condition)}</div> <div className="text-4xl">{getWeatherIcon(data.condition)}</div>
<div> <div>
@ -83,33 +58,63 @@ const WeatherSection: FC<WeatherSectionProps> = ({ data }) => {
</div> </div>
</div> </div>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<div className="text-gray-400 text-sm">线</div> <div className="text-gray-400 text-sm">线</div>
<div className="text-gray-300">{data.uvIndex || "--"}</div> <div className="text-gray-300">{data.uvIndex || "--"}</div>
</div> </div>
</div> </div>
{/* 天气预报模块(安全渲染) */} {/* 天气预报模块 */}
{forecastData.length > 0 && ( <div className="absolute top-64 right-8 w-72 bg-black/50 backdrop-blur-sm rounded-lg p-4 shadow-lg">
<div className="absolute top-64 right-8 w-64 space-y-2"> <div className="text-gray-300 text-sm font-medium mb-3">
{forecastData.map((day, index) => ( 5
</div>
<div className="space-y-2">
{data.forecast.slice(0, 5).map((day, index) => (
<div <div
key={day.day || index} key={day.day || index}
className="flex justify-between items-center text-sm group" className="flex justify-between items-center group transition-all duration-200 hover:bg-white/10 px-2 py-1 rounded-md"
style={{ opacity: 1 - index * 0.15 }} style={{ opacity: 1 - index * 0.15 }}
> >
<div className="text-gray-300 w-12">{day.day || "未知"}</div> {/* 日期 */}
<div className="text-gray-400 transition-opacity opacity-70 group-hover:opacity-100"> <div className="text-gray-300 text-sm font-light w-16">
{getWeatherIcon(day.condition)} {day.day || "未知"}
</div> </div>
<div className="flex gap-2">
<span className="text-blue-300">{day.low ?? "--"}°</span> {/*
<span className="text-red-300">{day.high ?? "--"}°</span> <div className="text-gray-400 text-lg">
{getWeatherIcon(day.condition)}
</div> */}
{/* 天气图标和名称 */}
<div className="flex items-center gap-2 w-24">
<div className="text-gray-400 text-lg">
{getWeatherIcon(day.condition)}
</div>
<div className="text-gray-300 text-sm font-light">
{day.condition || "未知"}
</div>
</div>
{/* 温度范围 */}
<div className="flex items-center gap-2">
<span className="text-blue-300 text-sm">
{day.low ?? "--"}°
</span>
<div className="w-12 h-1 bg-gradient-to-r from-blue-400 to-red-400 rounded-full" />
<span className="text-red-300 text-sm">
{day.high ?? "--"}°
</span>
</div> </div>
</div> </div>
))} ))}
</div> </div>
)} </div>
</> </>
); );
}; };