import { useState, useEffect, useMemo, useRef } from "react"; import { Search, FileText, Image, Video, Play, ChevronLeft, ChevronRight, Library, } from "lucide-react"; import { KIOSK_EVENT, loadKnowledgeEntries, type KnowledgeEntry } from "../kioskStorage"; import { PAGE_CONTENT_INSET } from "../pageContentInset"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "../components/ui/dialog"; import { useI18n } from "../i18n"; import { resolveKioskMediaUrl } from "../api/kioskApi"; /** 搜索框下方固定展示的关键词(顺序与文案一致) */ const QUICK_KEYWORDS = [ "天体物理", "教程", "日食", "星云", "暗物质", "望远镜", "深空摄影", ] as const; /** 每页条数(大屏约 5 列×2 行,共 10 条) */ const PAGE_SIZE = 10; function buildPageList(totalPages: number, currentPage: number): (number | "ellipsis")[] { if (totalPages <= 12) { return Array.from({ length: totalPages }, (_, i) => i + 1); } const set = new Set([1, totalPages, currentPage]); for (let p = currentPage - 2; p <= currentPage + 2; p++) { if (p >= 1 && p <= totalPages) set.add(p); } const sorted = [...set].sort((a, b) => a - b); const out: (number | "ellipsis")[] = []; for (let i = 0; i < sorted.length; i++) { const p = sorted[i]!; if (i > 0 && p - sorted[i - 1]! > 1) { out.push("ellipsis"); } out.push(p); } return out; } function matchesQuery(item: KnowledgeEntry, raw: string): boolean { const q = raw.trim(); if (q === "") return true; const lower = q.toLowerCase(); if (item.title.toLowerCase().includes(lower) || item.content.toLowerCase().includes(lower)) { return true; } return item.tags.some((t) => t.includes(q) || t.toLowerCase().includes(lower)); } /** 有条目视频地址即可播放(类型误选为「文字」时也能在知识库观看) */ function hasKnowledgeVideoUrl(item: KnowledgeEntry): boolean { return typeof item.videoUrl === "string" && item.videoUrl.trim() !== ""; } export function SearchPage() { const { t } = useI18n(); const [dbRev, setDbRev] = useState(0); const [searchQuery, setSearchQuery] = useState(""); const [page, setPage] = useState(1); const [activeItem, setActiveItem] = useState(null); const searchInputRef = useRef(null); useEffect(() => { const h = () => setDbRev((r) => r + 1); window.addEventListener(KIOSK_EVENT, h); window.addEventListener("storage", h); return () => { window.removeEventListener(KIOSK_EVENT, h); window.removeEventListener("storage", h); }; }, []); const searchResults = useMemo(() => loadKnowledgeEntries(), [dbRev]); const filteredResults = useMemo( () => searchResults.filter((item) => matchesQuery(item, searchQuery)), [searchResults, searchQuery] ); const totalPages = Math.max(1, Math.ceil(filteredResults.length / PAGE_SIZE)); useEffect(() => { setPage(1); }, [searchQuery, dbRev]); useEffect(() => { setPage((p) => Math.min(p, totalPages)); }, [totalPages]); const currentPage = Math.min(page, totalPages); const pageItems = filteredResults.slice( (currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE ); const toggleKeyword = (word: string) => { setSearchQuery((prev) => (prev.trim() === word ? "" : word)); }; const getTypeIcon = (type: string) => { switch (type) { case "文字": return FileText; case "图片": return Image; case "视频": return Video; default: return FileText; } }; const goPage = (p: number) => { setPage(Math.max(1, Math.min(p, totalPages))); }; return (
{/* 左:标题 · 右:搜索框,关键词仅在搜索框下方 */}
{t("search.title")}
{ e.preventDefault(); searchInputRef.current?.blur(); }} >
setSearchQuery(e.target.value)} placeholder={t("search.placeholder")} autoComplete="off" className="w-full rounded-lg border border-white/25 bg-transparent py-1.5 pl-8 pr-2.5 text-xs text-white placeholder-blue-300/80 transition-all focus:border-sky-400/80 focus:outline-none focus:ring-1 focus:ring-sky-400/30 sm:py-2 sm:pl-9 sm:text-sm" />
{QUICK_KEYWORDS.map((word) => { const active = searchQuery.trim() === word; return ( ); })}
{/* 搜索结果 + 分页 */}

{t("search.foundPrefix")}{" "} {filteredResults.length}{" "} {t("search.foundSuffix")}

{filteredResults.length > 0 && ( <>
{pageItems.map((result) => { const Icon = getTypeIcon(result.type); return (
setActiveItem(result)} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setActiveItem(result); } }} className="group flex min-h-0 min-w-0 cursor-pointer flex-col overflow-hidden rounded-lg border border-white/20 bg-white/10 transition-all hover:border-sky-400/45 hover:shadow-md hover:shadow-sky-400/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/70" >
{result.image ? ( ) : (
)} {result.type === "视频" || hasKnowledgeVideoUrl(result) ? (
) : null} {result.type}

{result.title}

{result.content.trim() ? (

{result.content.trim()}

) : null}
{result.tags.length > 0 ? ( <> {result.tags.slice(0, 3).map((tag) => ( {tag} ))} {result.tags.length > 3 ? ( +{result.tags.length - 3} ) : null} ) : null}
); })}
)} {filteredResults.length === 0 && (

{t("search.noneTitle")}

{t("search.noneHint")}

)}
(open ? null : setActiveItem(null))}> {activeItem ? (
{activeItem.title} {activeItem.type} · {activeItem.date} {activeItem.tags.length ? ` · ${activeItem.tags.join(" / ")}` : ""}
{activeItem.content?.trim() ? activeItem.content.trim() : "(无详细内容)"}
{hasKnowledgeVideoUrl(activeItem) ? (

视频(点击播放器控件播放)

) : activeItem.type === "视频" ? ( activeItem.image ? (

本条为「视频」类型但未填写视频地址,暂显示封面图。

{activeItem.title}
) : (
暂无视频地址
) ) : activeItem.image ? ( {activeItem.title} ) : (
暂无封面与视频
)} {hasKnowledgeVideoUrl(activeItem) && activeItem.image ? (

上图为视频封面;播放请使用上方控件。

) : null}
) : (
)}
); }