130 lines
4.1 KiB
TypeScript
130 lines
4.1 KiB
TypeScript
"use client";
|
||
|
||
/**
|
||
* 魔镜主页面src\app\page.tsx
|
||
* 集成所有子组件并管理主要状态
|
||
*/
|
||
|
||
import { useState, useEffect, useMemo } from "react";
|
||
import AnalogClock from "../components/AnalogClock";
|
||
import CalendarGrid from "../components/CalendarGrid";
|
||
import WeatherSection from "../components/WeatherSection";
|
||
import NewsSection from "../components/NewsSection";
|
||
import { generateCalendarDays } from "@/utils/calendar";
|
||
import { WeatherData, NewsItem, SunData } from "@/types/magic-mirror";
|
||
import VoiceAssistant from "@/components/VoiceAssistant";
|
||
import useSWR from "swr";
|
||
// 定义通用数据获取器
|
||
const fetcher = (url: string) => fetch(url).then((res) => res.json());
|
||
|
||
const MagicMirror = () => {
|
||
const [time, setTime] = useState(new Date());
|
||
const calendarDays = useMemo(() => generateCalendarDays(), []);
|
||
|
||
// 天气示例数据
|
||
const { data: weatherData, error: weatherError } = useSWR<WeatherData>(
|
||
"/api/weather",
|
||
(url: string) => fetch(url).then((res) => res.json())
|
||
);
|
||
|
||
// 新闻数据
|
||
const { data: newsItems = [], error: newsError } = useSWR<NewsItem[]>(
|
||
"/api/news",
|
||
(url: string) => fetch(url).then((res) => res.json())
|
||
);
|
||
|
||
// 在组件顶部新增SWR请求
|
||
// 日出日落数据
|
||
const { data: sunData = [] } = useSWR<SunData[]>("/api/sun", fetcher);
|
||
|
||
// 合并天气数据和日出日落数据
|
||
const mergedWeatherData = useMemo(() => {
|
||
if (weatherData && sunData.length > 0) {
|
||
return {
|
||
...weatherData,
|
||
sunrise: sunData[0]?.sunrise || "06:00",
|
||
sunset: sunData[0]?.sunset || "18:00",
|
||
};
|
||
}
|
||
return weatherData;
|
||
}, [weatherData, sunData]);
|
||
|
||
// 时间更新
|
||
useEffect(() => {
|
||
const timer = setInterval(() => setTime(new Date()), 1000);
|
||
return () => clearInterval(timer);
|
||
}, []);
|
||
|
||
// 生成问候语
|
||
const greeting = useMemo(() => {
|
||
const hours = time.getHours();
|
||
if (hours < 5) return "夜深了";
|
||
if (hours < 12) return "早上好";
|
||
if (hours < 18) return "下午好";
|
||
return "晚上好";
|
||
}, [time]);
|
||
|
||
return (
|
||
<div className="min-h-screen bg-black text-gray-100 font-sans antialiased overflow-hidden">
|
||
{/* 左上角时间模块 */}
|
||
<div className="absolute top-8 left-8 flex items-start gap-8">
|
||
{/* 时间日期模块 */}
|
||
<div className="space-y-1">
|
||
<div className="text-2xl font-light">
|
||
{time.toLocaleDateString("zh-CN", { weekday: "long" })}
|
||
</div>
|
||
<div className="text-gray-400 text-sm">
|
||
{time.toLocaleDateString("zh-CN", {
|
||
year: "numeric",
|
||
month: "long",
|
||
day: "numeric",
|
||
})}
|
||
</div>
|
||
<div className="flex items-end gap-2">
|
||
<div className="text-5xl font-light">
|
||
{time.toLocaleTimeString("zh-CN", {
|
||
hour: "2-digit",
|
||
minute: "2-digit",
|
||
hour12: false,
|
||
})}
|
||
</div>
|
||
<div className="text-gray-400 mb-5">
|
||
{time.getSeconds().toString().padStart(2, "0")}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<AnalogClock time={time} />
|
||
</div>
|
||
|
||
{/* 日历模块 */}
|
||
<div className="absolute top-48 left-8 w-64">
|
||
<div className="mb-4 text-gray-300 text-sm">
|
||
{time.toLocaleDateString("zh-CN", { month: "long", year: "numeric" })}
|
||
</div>
|
||
<CalendarGrid days={calendarDays} />
|
||
</div>
|
||
|
||
{/* 分隔线 */}
|
||
<div className="absolute left-8 w-64 top-[420px] border-t border-white/10" />
|
||
|
||
{/* 待办事项 */}
|
||
<div className="absolute left-8 w-64 top-[460px] space-y-2 font-light">
|
||
<div className="text-gray-400 text-sm">待办事项</div>
|
||
<div className="text-gray-300 space-y-1">
|
||
<div>• 项目会议准备(10:00)</div>
|
||
<div>• 技术方案评审</div>
|
||
<div>• 客户需求确认</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/*<WeatherSection data={weatherData} />*/}
|
||
<WeatherSection data={mergedWeatherData} />
|
||
<NewsSection items={newsItems} />
|
||
<VoiceAssistant greeting={greeting} />
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default MagicMirror;
|