import type { SearchWork } from "@umt/contracts"; import { ArrowRight, Film, PackageSearch } from "lucide-react"; import { useState } from "react"; import { Link, useNavigate } from "react-router"; import { mediaServers } from "~/api/instances.ts"; import { useSearch } from "~/api/search.ts"; import { EmptyState, ErrorState, LoadingState } from "~/components/console/empty-state.tsx"; import { settingsPath } from "~/components/console/nav.ts"; import { useListPagination } from "~/components/console/use-list-pagination.ts"; import { Button } from "~/components/ui/button.tsx"; import { Pagination } from "~/components/ui/pagination.tsx"; import { actionIcon } from "./search-labels.ts"; import { SitePickerDialog } from "./site-picker-dialog.tsx"; import { releaseSearchPath, WorkCard, workRef } from "./work-card.tsx"; // 结果页第一阶段:作品(本地 + TMDB 合并,带库存对比)与操作;资源要点「搜索资源」选站点后才查(release-results.tsx) function SectionTitle({ id, title, extra }: { id: string; title: string; extra?: string }) { return (

{title} {extra ? {extra} : null}

); } /** 「搜索资源」:先选站点,再跳到第二阶段(同一搜索页带 work / in 参数) */ export function useReleaseSearchLauncher() { const navigate = useNavigate(); const [target, setTarget] = useState<{ title: string; ref: string } | null>(null); return { open: (t: { title: string; ref: string }) => setTarget(t), dialog: ( !o && setTarget(null)} title={target?.title ?? ""} onConfirm={(ids) => target && navigate(releaseSearchPath(target, ids))} /> ), }; } export function WorksSection({ q }: { q: string }) { const res = useSearch(q, "media", 50); const ms = mediaServers.useList(); const emby = (ms.data?.items ?? []).some((i) => (i as { enabled?: boolean }).enabled !== false); const launcher = useReleaseSearchLauncher(); const works = res.data?.media?.works ?? []; const paged = useListPagination(works, { resetKey: q }); const tmdb = res.data?.media?.tmdb; const searchReleases = (w: SearchWork) => { const ref = workRef(w); if (ref) launcher.open({ title: w.title, ref }); }; return (
{res.isPending ? ( ) : res.isError ? (
res.refetch()} />
) : works.length === 0 ? (
) : (
{paged.items?.map((w) => ( ))}
)} {res.data ? : null} {tmdb && !tmdb.configured ? (

TMDB 未配置,只搜了本地作品。 去配置

) : null} {tmdb?.error ?

TMDB 查询失败:{tmdb.error}

: null} {launcher.dialog}
); } export function ActionsSection({ q }: { q: string }) { const res = useSearch(q, "actions", 12); const items = res.data?.actions?.items ?? []; if (res.isPending || res.isError || items.length === 0) return null; return (
{items.map((a) => { const Icon = actionIcon(a.kind); return ( {a.title} {a.group} ); })}
); }