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';
export async function GET() {
const apiKey = process.env.QWEATHER_API_KEY;
// 使用北京坐标(示例)
const [longitude, latitude] = [116.3974, 39.9093];
const today = new Date();
try {
// 获取未来6天日出日落数据
// 生成包含今天在内的未来6天日期
const datePromises = Array.from({ length: 6 }).map((_, i) => {
const date = new Date(today);
date.setDate(today.getDate() + i);
return date.toISOString().split('T')[0].replace(/-/g, ''); // 格式化为yyyyMMdd
return date;
});
const responses = await Promise.all(
datePromises.map(date =>
fetch(`https://devapi.qweather.com/v7/astronomy/sun?location=${longitude},${latitude}&date=${date}&key=${apiKey}`)
)
datePromises.map(date => {
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 formattedData = sunData.map((item, index) => ({
date: new Date(today.getTime() + index * 86400000).toISOString().split('T')[0],
sunrise: formatSunTime(item.sunrise),
sunset: formatSunTime(item.sunset)
}));
// 数据格式化和验证
const formattedData = sunData.map((item, index) => {
const targetDate = new Date(today);
targetDate.setDate(today.getDate() + index);
return {
date: targetDate.toISOString().split('T')[0],
sunrise: safeFormatTime(item.sunrise),
sunset: safeFormatTime(item.sunset)
};
});
return NextResponse.json(formattedData, {
headers: {
@ -39,22 +54,28 @@ export async function GET() {
} catch (error) {
console.error('日出日落数据获取失败:', error);
return NextResponse.json(
{ error: "日出日落数据获取失败" },
{ error: "数据获取失败,请稍后重试" },
{ 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 {
const date = new Date(isoTime);
const date = new Date(timeStr);
return date.toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit',
hour12: false
});
} 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": "沙尘",
"WIND": "大风"
};
return skyconMap[skycon] || "未知天气";
return skyconMap[skycon] || "未知";
}
// 风向转换(根据官方文档说明)

View File

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

View File

@ -1,11 +1,4 @@
"use client";
/**
*
*
* API数据格式转换
*/
// src/components/WeatherSection.tsx
import { FC } from "react";
import { WeatherData } from "../types/magic-mirror";
import CircularProgress from "@mui/material/CircularProgress";
@ -15,7 +8,6 @@ interface WeatherSectionProps {
}
const WeatherSection: FC<WeatherSectionProps> = ({ data }) => {
// 处理加载状态
if (!data) {
return (
<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 iconMap: Record<string, string> = {
: "☀️",
@ -53,12 +31,8 @@ const WeatherSection: FC<WeatherSectionProps> = ({ data }) => {
return condition ? iconMap[condition] || "🌤️" : "🌤️";
};
// 安全获取天气预报数据
const forecastData = data.forecast?.slice(0, 5) || [];
return (
<>
{/* 当前天气模块 */}
<div className="absolute top-8 right-0 w-64 space-y-4">
<div className="grid grid-cols-2 gap-4 text-sm">
<div className="space-y-1">
@ -70,10 +44,11 @@ const WeatherSection: FC<WeatherSectionProps> = ({ data }) => {
<div className="space-y-1">
<div className="text-gray-400">/</div>
<div className="text-gray-300">
{formatTime(data.sunrise)} / {formatTime(data.sunset)}
{data.sunrise || "--:--"} / {data.sunset || "--:--"}
</div>
</div>
</div>
<div className="flex items-center gap-4">
<div className="text-4xl">{getWeatherIcon(data.condition)}</div>
<div>
@ -83,33 +58,63 @@ const WeatherSection: FC<WeatherSectionProps> = ({ data }) => {
</div>
</div>
</div>
<div className="space-y-2">
<div className="text-gray-400 text-sm">线</div>
<div className="text-gray-300">{data.uvIndex || "--"}</div>
</div>
</div>
{/* 天气预报模块(安全渲染) */}
{forecastData.length > 0 && (
<div className="absolute top-64 right-8 w-64 space-y-2">
{forecastData.map((day, index) => (
{/* 天气预报模块 */}
<div className="absolute top-64 right-8 w-72 bg-black/50 backdrop-blur-sm rounded-lg p-4 shadow-lg">
<div className="text-gray-300 text-sm font-medium mb-3">
5
</div>
<div className="space-y-2">
{data.forecast.slice(0, 5).map((day, index) => (
<div
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 }}
>
<div className="text-gray-300 w-12">{day.day || "未知"}</div>
<div className="text-gray-400 transition-opacity opacity-70 group-hover:opacity-100">
{getWeatherIcon(day.condition)}
{/* 日期 */}
<div className="text-gray-300 text-sm font-light w-16">
{day.day || "未知"}
</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>
</>
);
};