This commit is contained in:
parent
2521db74a9
commit
012715ee81
@ -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) || '--:--';
|
||||
}
|
||||
}
|
@ -77,7 +77,7 @@ function translateSkycon(skycon: string) {
|
||||
"SAND": "沙尘",
|
||||
"WIND": "大风"
|
||||
};
|
||||
return skyconMap[skycon] || "未知天气";
|
||||
return skyconMap[skycon] || "未知";
|
||||
}
|
||||
|
||||
// 风向转换(根据官方文档说明)
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* 魔镜主页面
|
||||
* 魔镜主页面src\app\page.tsx
|
||||
* 集成所有子组件并管理主要状态
|
||||
*/
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
||||
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user