148 lines
6.3 KiB
TypeScript
148 lines
6.3 KiB
TypeScript
"use client";
|
||
|
||
import { useMemo, useState } from "react";
|
||
import type { Destination } from "@/lib/types";
|
||
import ToolCityExits from "@/components/ToolCityExits";
|
||
|
||
interface Idea {
|
||
emoji: string;
|
||
title: string;
|
||
vibe: string;
|
||
cost: string;
|
||
}
|
||
|
||
const IDEAS: Record<string, Idea[]> = {
|
||
chiangmai: [
|
||
{ emoji: "⛰️", title: "徒步 Doi Suthep", vibe: "清晨清爽,下山喝咖啡续命", cost: "¥40–80" },
|
||
{ emoji: "🛁", title: "热水泉泡汤", vibe: "周末放松肩颈,适合写周报前", cost: "¥30–60" },
|
||
{ emoji: "🍜", title: "周日步行街夜市", vibe: "人多但氛围足,试小吃就好", cost: "¥50–100" },
|
||
{ emoji: "🧘", title: "乌布式瑜伽体验课", vibe: "本地工作室很多试听课", cost: "¥60–120" },
|
||
],
|
||
bali: [
|
||
{ emoji: "🏄", title: "库塔/Canggu 冲浪课", vibe: "初学友好,防晒务必做好", cost: "¥200–350" },
|
||
{ emoji: "🛕", title: "乌布神庙半日游", vibe: "避开正午,早晚光线最好", cost: "¥80–150" },
|
||
{ emoji: "🌾", title: "骑车穿梭梯田", vibe: "租摩托前先熟悉靠左行", cost: "¥100–180" },
|
||
{ emoji: "🌅", title: "乌鲁瓦图日落", vibe: "建议提前占位,注意崖边安全", cost: "¥50–90" },
|
||
],
|
||
lisbon: [
|
||
{ emoji: "🚋", title: "28 路电车+阿尔法玛", vibe: "早上去人少,适合拍照", cost: "¥20–40" },
|
||
{ emoji: "🏖️", title: "去 Cascais 吹海风", vibe: "火车 40 分钟,周末轻松局", cost: "¥60–120" },
|
||
{ emoji: "🎵", title: "Fado 小酒馆之夜", vibe: "预约座位,点一杯就够", cost: "¥150–250" },
|
||
{ emoji: "🏛️", title: "贝伦甜挞巡礼", vibe: "经典但值得,搭配公园散步", cost: "¥40–70" },
|
||
],
|
||
barcelona: [
|
||
{ emoji: "🏖️", title: "Barceloneta 海边慢跑", vibe: "清晨最舒服,避开午后暴晒", cost: "免费" },
|
||
{ emoji: "🎨", title: "公园桂尔半日", vibe: "提前买票,错峰体验更好", cost: "¥120–180" },
|
||
{ emoji: "🍷", title: "Gracia 小酒馆串店", vibe: "当地人周末去处", cost: "¥150–280" },
|
||
{ emoji: "⛰️", title: "Montjuïc 看城景", vibe: "日落时光线绝佳", cost: "¥30–60" },
|
||
],
|
||
mexico: [
|
||
{ emoji: "🌮", title: "街区 taco 巡礼", vibe: "选人气摊,循序渐进试辣", cost: "¥60–120" },
|
||
{ emoji: "🖼️", title: "人类学博物馆", vibe: "室内避暑+高质量展览", cost: "¥50–80" },
|
||
{ emoji: "🛶", title: "Xochimilco 彩色船", vibe: "周末热闹,建议结伴", cost: "¥150–250" },
|
||
{ emoji: "☕", title: "Roma/Condesa 咖啡漫步", vibe: "树荫街道适合边走边聊", cost: "¥40–90" },
|
||
],
|
||
tokyo: [
|
||
{ emoji: "♨️", title: "钱汤/温泉放松", vibe: "带走疲劳,注意礼仪", cost: "¥50–120" },
|
||
{ emoji: "🎌", title: "下北泽古着街", vibe: "周末市集氛围强", cost: "看心情" },
|
||
{ emoji: "🗻", title: "高尾山一日轻徒步", vibe: "电车直达,难度友好", cost: "¥80–150" },
|
||
{ emoji: "🍣", title: "筑地外围早餐", vibe: "早起有回报", cost: "¥100–200" },
|
||
],
|
||
};
|
||
|
||
interface Props {
|
||
destinations: Destination[];
|
||
}
|
||
|
||
export default function WeekendIdeas({ destinations }: Props) {
|
||
const [slug, setSlug] = useState(destinations[0]?.slug || "chiangmai");
|
||
const [energy, setEnergy] = useState<"chill" | "active" | "social">("chill");
|
||
const [picked, setPicked] = useState(0);
|
||
|
||
const list = useMemo(() => {
|
||
const all = IDEAS[slug] || IDEAS.chiangmai;
|
||
if (energy === "active") return [...all].sort((a, b) => b.title.length - a.title.length);
|
||
if (energy === "social") return [...all].reverse();
|
||
return all;
|
||
}, [slug, energy]);
|
||
|
||
const dest = destinations.find((d) => d.slug === slug);
|
||
const current = list[picked % list.length];
|
||
|
||
const shuffle = () => setPicked((p) => (p + 1) % list.length);
|
||
|
||
return (
|
||
<section className="section weekend-section" id="weekend">
|
||
<div className="container">
|
||
<div className="section-header reveal">
|
||
<span className="section-tag">🎉 WEEKEND</span>
|
||
<h2>周末活动灵感</h2>
|
||
<p>旅居不是只有工位,周末也要会玩会恢复</p>
|
||
</div>
|
||
|
||
<div className="weekend-card reveal">
|
||
<div className="weekend-controls">
|
||
<label>
|
||
<span>城市</span>
|
||
<select
|
||
value={slug}
|
||
onChange={(e) => {
|
||
setSlug(e.target.value);
|
||
setPicked(0);
|
||
}}
|
||
>
|
||
{destinations.map((d) => (
|
||
<option key={d.slug} value={d.slug}>{d.emoji} {d.name}</option>
|
||
))}
|
||
</select>
|
||
</label>
|
||
<div className="weekend-energy">
|
||
{([
|
||
["chill", "😌 轻松恢复"],
|
||
["active", "🏃 动起来"],
|
||
["social", "🍻 社交局"],
|
||
] as const).map(([k, label]) => (
|
||
<button
|
||
key={k}
|
||
type="button"
|
||
className={`weekend-chip${energy === k ? " active" : ""}`}
|
||
onClick={() => { setEnergy(k); setPicked(0); }}
|
||
>
|
||
{label}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="weekend-hero">
|
||
<span className="weekend-emoji">{current.emoji}</span>
|
||
<div>
|
||
<small>{dest?.emoji} {dest?.name} · 周末提案</small>
|
||
<h3>{current.title}</h3>
|
||
<p>{current.vibe}</p>
|
||
<em>预算约 {current.cost}</em>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="weekend-actions">
|
||
<button type="button" className="btn btn-primary" onClick={shuffle}>换一个灵感 🎲</button>
|
||
<div className="weekend-list">
|
||
{list.map((idea, i) => (
|
||
<button
|
||
key={idea.title}
|
||
type="button"
|
||
className={`weekend-mini${i === picked % list.length ? " on" : ""}`}
|
||
onClick={() => setPicked(i)}
|
||
>
|
||
{idea.emoji} {idea.title}
|
||
</button>
|
||
))}
|
||
</div>
|
||
{dest && <ToolCityExits dest={dest} />}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|