"use client"; import { useEffect, useState, useCallback } from "react"; import { useRouter, usePathname } from "next/navigation"; import { api } from "@/lib/api"; import { TOOL_LINKS } from "@/lib/tools"; import type { SearchResult } from "@/lib/types"; const TYPE_LABELS: Record = { destination: "目的地", blog: "博客", visa: "签证", faq: "FAQ", tool: "工具", page: "页面", }; const PAGE_RESULTS: SearchResult[] = [ { type: "page", title: "旅居计划中心", subtitle: "时间轴、预算、签证提醒与出发清单", emoji: "🗓️", url: "/plan" }, { type: "page", title: "城市对比台", subtitle: "并排对比费用网速气候,写入计划", emoji: "⚖️", url: "/compare" }, { type: "page", title: "下一站决策", subtitle: "预算网速气候智能推荐", emoji: "🧭", url: "/next-stop" }, { type: "page", title: "游民活动", subtitle: "线上圆桌与线下聚会", emoji: "🎉", url: "/meetups" }, { type: "page", title: "社区讨论", subtitle: "签证远程住宿经验交流", emoji: "💬", url: "/community" }, { type: "page", title: "游民匹配", subtitle: "滑动匹配同路游民", emoji: "💕", url: "/dating" }, { type: "page", title: "私信", subtitle: "与匹配成功的游民聊天", emoji: "✉️", url: "/chat" }, { type: "page", title: "游民学院", subtitle: "课程电子书与远程岗位", emoji: "🎓", url: "/digital" }, { type: "page", title: "赏金任务", subtitle: "远程小任务接单", emoji: "💼", url: "/gigs" }, { type: "page", title: "会员定价", subtitle: "免费版与 VIP 方案", emoji: "💎", url: "/pricing" }, { type: "page", title: "通知中心", subtitle: "匹配与社区动态", emoji: "🔔", url: "/notifications" }, { type: "page", title: "开通会员", subtitle: "VIP 匹配直播与课程", emoji: "✨", url: "/join" }, { type: "page", title: "工具箱", subtitle: "全部旅居工具入口", emoji: "🛠️", url: "/tools" }, { type: "page", title: "关于 nomadro", subtitle: "品牌与联系方式", emoji: "🌏", url: "/about" }, { type: "page", title: "更新日志", subtitle: "功能迭代记录", emoji: "📜", url: "/changelog" }, { type: "page", title: "隐私政策", subtitle: "Cookie 与数据说明", emoji: "🔒", url: "/privacy" }, ]; function searchLocal(q: string, hidePlanCompare: boolean): SearchResult[] { const s = q.toLowerCase(); const tools = TOOL_LINKS .filter((t) => t.title.toLowerCase().includes(s) || t.desc.toLowerCase().includes(s) || t.id.includes(s)) .filter((t) => !hidePlanCompare || (t.href !== "/plan" && t.href !== "/compare")) .map((t) => ({ type: "tool", title: t.title, subtitle: t.desc, emoji: t.emoji, url: t.href, })); const pages = PAGE_RESULTS.filter( (p) => p.title.toLowerCase().includes(s) || p.subtitle.toLowerCase().includes(s) ).filter((p) => !hidePlanCompare || (p.url !== "/plan" && p.url !== "/compare")); return [...tools, ...pages]; } export default function GlobalSearch() { const [open, setOpen] = useState(false); const [query, setQuery] = useState(""); const [results, setResults] = useState([]); const [loading, setLoading] = useState(false); const [activeIdx, setActiveIdx] = useState(0); const router = useRouter(); const isHome = usePathname() === "/"; useEffect(() => { const onKey = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === "k") { e.preventDefault(); setOpen((o) => !o); } if (e.key === "Escape") setOpen(false); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, []); const doSearch = useCallback(async (q: string) => { if (!q.trim()) { setResults([]); return; } setLoading(true); const local = searchLocal(q, isHome); try { const data = await api.search(q); const merged = [...local, ...data].slice(0, 20); setResults(merged); setActiveIdx(0); } catch { setResults(local); setActiveIdx(0); } finally { setLoading(false); } }, [isHome]); useEffect(() => { const timer = setTimeout(() => doSearch(query), 250); return () => clearTimeout(timer); }, [query, doSearch]); const navigate = (url: string) => { setOpen(false); setQuery(""); if (url.startsWith("/#")) { router.push("/"); setTimeout(() => { document.querySelector(url.replace("/", ""))?.scrollIntoView({ behavior: "smooth" }); }, 300); } else { router.push(url); } }; const onKeyDown = (e: React.KeyboardEvent) => { if (e.key === "ArrowDown") { e.preventDefault(); setActiveIdx((i) => Math.min(i + 1, results.length - 1)); } if (e.key === "ArrowUp") { e.preventDefault(); setActiveIdx((i) => Math.max(i - 1, 0)); } if (e.key === "Enter" && results[activeIdx]) navigate(results[activeIdx].url); }; return ( <> {open && (
setOpen(false)}>
e.stopPropagation()}>
setQuery(e.target.value)} onKeyDown={onKeyDown} />
{loading &&

搜索中...

} {!loading && query && results.length === 0 && (

😢 没有找到「{query}」相关结果

)} {!loading && results.map((r, i) => ( ))} {!query && (

💡 试试:清迈、机票、税居、工具箱

⌨️ ↑↓ 选择 · Enter 跳转 · Esc 关闭

)}
)} ); }