112 lines
4.6 KiB
TypeScript
112 lines
4.6 KiB
TypeScript
"use client";
|
||
|
||
import { useMemo, useState } from "react";
|
||
import type { Destination } from "@/lib/types";
|
||
|
||
const TZ_OFFSET: Record<string, number> = {
|
||
bali: 8, lisbon: 0, chiangmai: 7, mexico: -6, barcelona: 1, tokyo: 9,
|
||
};
|
||
|
||
const HOME_OFFSETS = [
|
||
{ key: "cn", label: "🇨🇳 中国 (UTC+8)", offset: 8 },
|
||
{ key: "us_east", label: "🇺🇸 美东 (UTC-5)", offset: -5 },
|
||
{ key: "us_west", label: "🇺🇸 美西 (UTC-8)", offset: -8 },
|
||
{ key: "uk", label: "🇬🇧 英国 (UTC+0)", offset: 0 },
|
||
{ key: "eu", label: "🇪🇺 中欧 (UTC+1)", offset: 1 },
|
||
];
|
||
|
||
function tipsForDiff(absDiff: number): string[] {
|
||
if (absDiff <= 2) return ["时差很小,几乎无感", "抵达当天可正常作息", "保持常规睡眠即可"];
|
||
if (absDiff <= 5) return ["建议提前 2–3 天微调作息", "抵达后当天晒太阳帮助校准", "避免落地立刻开长会"];
|
||
if (absDiff <= 8) return ["东飞更伤,预留 2–3 天恢复", "第一晚尽量睡满 7 小时", "褪黑素/镁片可辅助(遵医嘱)"];
|
||
return ["跨半球长途,预留 4–5 天缓冲", "分段飞行比直达更易恢复", "前两天安排轻量工作即可"];
|
||
}
|
||
|
||
interface Props {
|
||
destinations: Destination[];
|
||
}
|
||
|
||
export default function JetLagEstimator({ destinations }: Props) {
|
||
const [home, setHome] = useState("cn");
|
||
const [slug, setSlug] = useState(destinations[0]?.slug || "bali");
|
||
const [direction, setDirection] = useState<"east" | "west">("east");
|
||
|
||
const result = useMemo(() => {
|
||
const homeOff = HOME_OFFSETS.find((h) => h.key === home)?.offset ?? 8;
|
||
const destOff = TZ_OFFSET[slug] ?? 8;
|
||
let diff = destOff - homeOff;
|
||
if (diff > 12) diff -= 24;
|
||
if (diff < -12) diff += 24;
|
||
const abs = Math.abs(diff);
|
||
const dir = diff >= 0 ? "east" : "west";
|
||
const recovery = Math.max(1, Math.ceil(abs / 2));
|
||
return { diff, abs, dir, recovery, tips: tipsForDiff(abs) };
|
||
}, [home, slug]);
|
||
|
||
const dest = destinations.find((d) => d.slug === slug);
|
||
|
||
return (
|
||
<section className="section jetlag-section" id="jetlag">
|
||
<div className="container">
|
||
<div className="section-header reveal">
|
||
<span className="section-tag">😴 JET LAG</span>
|
||
<h2>时差恢复估算</h2>
|
||
<p>算出时差跨度,提前安排恢复期与工作节奏</p>
|
||
</div>
|
||
|
||
<div className="jetlag-card reveal">
|
||
<div className="jetlag-form">
|
||
<div className="calc-field">
|
||
<label>🏠 当前时区</label>
|
||
<select value={home} onChange={(e) => setHome(e.target.value)}>
|
||
{HOME_OFFSETS.map((h) => (
|
||
<option key={h.key} value={h.key}>{h.label}</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
<div className="calc-field">
|
||
<label>✈️ 目的地</label>
|
||
<select value={slug} onChange={(e) => setSlug(e.target.value)}>
|
||
{destinations.map((d) => (
|
||
<option key={d.slug} value={d.slug}>{d.emoji} {d.name}</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
<div className="calc-field">
|
||
<label>🧭 飞行方向(主观)</label>
|
||
<div className="calc-options">
|
||
<button className={`calc-option${direction === "east" ? " active" : ""}`} onClick={() => setDirection("east")}>🌅 向东飞</button>
|
||
<button className={`calc-option${direction === "west" ? " active" : ""}`} onClick={() => setDirection("west")}>🌇 向西飞</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="jetlag-result">
|
||
<div className="jetlag-diff">
|
||
<span className="jetlag-emoji">{dest?.emoji || "🌍"}</span>
|
||
<strong>{result.abs === 0 ? "无时差" : `${result.abs} 小时`}</strong>
|
||
<small>
|
||
{result.diff === 0
|
||
? "与当前时区相同"
|
||
: result.diff > 0
|
||
? `目的地快 ${result.abs} 小时`
|
||
: `目的地慢 ${result.abs} 小时`}
|
||
</small>
|
||
</div>
|
||
<div className="jetlag-recovery">
|
||
<span>建议恢复期</span>
|
||
<strong className="gradient-text">{result.recovery} 天</strong>
|
||
{(direction === "east" || result.dir === "east") && result.abs > 3 && (
|
||
<p className="jetlag-note">向东飞行通常更难适应,可多留一天缓冲</p>
|
||
)}
|
||
</div>
|
||
<ul className="jetlag-tips">
|
||
{result.tips.map((t) => <li key={t}>💡 {t}</li>)}
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|