2025-02-27 21:39:32 +08:00
|
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
/**
|
2025-02-28 23:49:57 +08:00
|
|
|
|
* 魔镜主页面src\app\page.tsx
|
2025-02-27 21:39:32 +08:00
|
|
|
|
* 集成所有子组件并管理主要状态
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
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";
|
2025-02-28 23:28:56 +08:00
|
|
|
|
import { WeatherData, NewsItem, SunData } from "@/types/magic-mirror";
|
2025-02-27 21:39:32 +08:00
|
|
|
|
import VoiceAssistant from "@/components/VoiceAssistant";
|
|
|
|
|
import useSWR from "swr";
|
2025-02-28 23:28:56 +08:00
|
|
|
|
// 定义通用数据获取器
|
|
|
|
|
const fetcher = (url: string) => fetch(url).then((res) => res.json());
|
2025-02-27 21:39:32 +08:00
|
|
|
|
|
|
|
|
|
const MagicMirror = () => {
|
|
|
|
|
const [time, setTime] = useState(new Date());
|
|
|
|
|
const calendarDays = useMemo(() => generateCalendarDays(), []);
|
|
|
|
|
|
|
|
|
|
// 天气示例数据
|
2025-02-28 22:04:13 +08:00
|
|
|
|
const { data: weatherData, error: weatherError } = useSWR<WeatherData>(
|
|
|
|
|
"/api/weather",
|
|
|
|
|
(url: string) => fetch(url).then((res) => res.json())
|
|
|
|
|
);
|
2025-02-27 21:39:32 +08:00
|
|
|
|
|
|
|
|
|
// 新闻数据
|
|
|
|
|
const { data: newsItems = [], error: newsError } = useSWR<NewsItem[]>(
|
|
|
|
|
"/api/news",
|
|
|
|
|
(url: string) => fetch(url).then((res) => res.json())
|
|
|
|
|
);
|
|
|
|
|
|
2025-02-28 23:28:56 +08:00
|
|
|
|
// 在组件顶部新增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]);
|
|
|
|
|
|
2025-02-27 21:39:32 +08:00
|
|
|
|
// 时间更新
|
|
|
|
|
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]);
|
2025-02-24 15:03:29 +08:00
|
|
|
|
|
|
|
|
|
return (
|
2025-02-27 21:39:32 +08:00
|
|
|
|
<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>
|
2025-02-24 15:03:29 +08:00
|
|
|
|
|
2025-02-27 21:39:32 +08:00
|
|
|
|
{/* 日历模块 */}
|
|
|
|
|
<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" })}
|
2025-02-24 15:03:29 +08:00
|
|
|
|
</div>
|
2025-02-27 21:39:32 +08:00
|
|
|
|
<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>
|
|
|
|
|
|
2025-02-28 23:28:56 +08:00
|
|
|
|
{/*<WeatherSection data={weatherData} />*/}
|
|
|
|
|
<WeatherSection data={mergedWeatherData} />
|
2025-02-27 21:39:32 +08:00
|
|
|
|
<NewsSection items={newsItems} />
|
|
|
|
|
<VoiceAssistant greeting={greeting} />
|
2025-02-24 15:03:29 +08:00
|
|
|
|
</div>
|
|
|
|
|
);
|
2025-02-27 21:39:32 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default MagicMirror;
|