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, SelectionSort, SelectionStockResult, } from "../api/selection.types" import { ExecutionStatusTrigger } from "./execution-status-trigger" import { SignalDetailPanel } from "./signal-detail-panel" import { SignalRecordList } from "./signal-record-list" import { getStockKey, matchesCategory, signalCategoryOptions, type SignalCategoryFilter, } from "./selection-presentation" const PAGE_SIZE_OPTIONS = [5, 10, 15] as const const SCORE_SORT_OPTIONS: ReadonlyArray<{ label: string value: SelectionSort }> = [ { label: "按股票代码", value: "code" }, { label: "评分从高到低", value: "score_desc" }, { label: "评分从低到高", value: "score_asc" }, ] 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 sort = search.sort ?? "code" const [selectedKey, setSelectedKey] = useState(null) const [expandedKeys, setExpandedKeys] = useState>( new Set(), ) const visibleStocks = useMemo(() => { const normalized = query.trim().toLowerCase() return result.stocks.filter((stock) => { const matchesQuery = normalized.length === 0 || stock.name.toLowerCase().includes(normalized) || stock.ts_code.toLowerCase().includes(normalized) return matchesQuery && matchesCategory(stock, category) }) }, [category, query, result.stocks]) const page = search.page const pageSize = search.pageSize const stocksTotal = result.stocks_total ?? result.selected_stock_count const pageCount = Math.max(1, Math.ceil(stocksTotal / pageSize)) const currentPage = Math.min(page, pageCount) const currentStocks = visibleStocks const selectedStock = currentStocks.find((stock) => getStockKey(stock) === selectedKey) ?? currentStocks[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 sort?: SelectionSort }) { 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 handleSortChange(value: SelectionSort) { updateSearch({ page: 1, sort: value }) } function handleToggleExpanded(stock: SelectionStockResult) { const key = getStockKey(stock) 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} /> 筛选结果 {stocksTotal} 只
setSelectedKey(getStockKey(stock))} onToggleExpanded={handleToggleExpanded} selectedKey={selectedStock ? getStockKey(selectedStock) : null} stocks={currentStocks} />
updateSearch({ page: nextPage })} onPageSizeChange={(nextPageSize) => updateSearch({ page: 1, pageSize: nextPageSize }) } page={currentPage} pageSize={pageSize} pageSizeOptions={PAGE_SIZE_OPTIONS} total={stocksTotal} />
) }