import { useEffect, useMemo, useState, type RefObject } from "react" import { useNavigate, useSearch } from "@tanstack/react-router" import { Input } from "@/shared/ui/input" import { Pagination } from "@/shared/ui/pagination" import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, } from "@/shared/ui/select" import type { SelectionResults, SelectionSignal } from "../api/selection.types" import { ExecutionStatusTrigger } from "./execution-status-trigger" import { SignalDetailPanel } from "./signal-detail-panel" import { SignalRecordList } from "./signal-record-list" import { getSignalKey, matchesCategory, signalCategoryOptions, type SignalCategoryFilter, } from "./selection-presentation" import { SignalTable } from "./signal-table" const PAGE_SIZE_OPTIONS = [5, 10, 15] as const interface SelectionResultsWorkbenchProps { drawerOpen: boolean onOpenExecutionStatus: () => void result: SelectionResults triggerRef: RefObject } export function SelectionResultsWorkbench({ drawerOpen, onOpenExecutionStatus, result, triggerRef, }: SelectionResultsWorkbenchProps) { const search = useSearch({ from: "/_workspace/selection" }) const navigate = useNavigate({ from: "/selection" }) const query = search.search ?? "" const category = search.category ?? "all" const [selectedKey, setSelectedKey] = useState(null) const [expandedKeys, setExpandedKeys] = useState>( new Set(), ) const visibleSignals = useMemo(() => { const normalized = query.trim().toLowerCase() return result.signals.filter((signal) => { const matchesQuery = normalized.length === 0 || signal.name.toLowerCase().includes(normalized) || signal.ts_code.toLowerCase().includes(normalized) return matchesQuery && matchesCategory(signal, category) }) }, [category, query, result.signals]) const page = search.page const pageSize = search.pageSize const signalsTotal = result.signals_total ?? result.signal_count const pageCount = Math.max(1, Math.ceil(signalsTotal / pageSize)) const currentPage = Math.min(page, pageCount) const currentSignals = visibleSignals const selectedSignal = currentSignals.find((signal) => getSignalKey(signal) === selectedKey) ?? currentSignals[0] useEffect(() => { if (page > pageCount) { void navigate({ search: (previous) => ({ ...previous, page: pageCount }), }) } }, [navigate, page, pageCount]) function updateSearch(next: { page?: number pageSize?: number search?: string category?: SignalCategoryFilter }) { void navigate({ search: (previous) => ({ ...previous, ...next }) }) } function handleQueryChange(value: string) { updateSearch({ page: 1, search: value }) } function handleCategoryChange(value: SignalCategoryFilter) { updateSearch({ category: value, page: 1 }) } function handleToggleExpanded(signal: SelectionSignal) { const key = getSignalKey(signal) setExpandedKeys((previous) => { const next = new Set(previous) if (next.has(key)) next.delete(key) else next.add(key) return next }) } return (
handleQueryChange(event.target.value)} placeholder="搜索股票名称或代码" type="search" value={query} /> 筛选结果 {signalsTotal} 条
setSelectedKey(getSignalKey(signal))} selectedKey={selectedSignal ? getSignalKey(selectedSignal) : null} signals={currentSignals} /> setSelectedKey(getSignalKey(signal))} onToggleExpanded={handleToggleExpanded} signals={currentSignals} />
updateSearch({ page: nextPage })} onPageSizeChange={(nextPageSize) => updateSearch({ page: 1, pageSize: nextPageSize }) } page={currentPage} pageSize={pageSize} pageSizeOptions={PAGE_SIZE_OPTIONS} total={signalsTotal} />
) }