"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import { apiGet } from "@/lib/api"; import type { Stock } from "@/lib/types"; import { PageHeader, Card, Banner, Empty, Pill, SkeletonLines, SearchBox, } from "@/components/ui"; import { Icon } from "@/components/icons"; const LIMIT = 300; function marketOf(symbol: string): string { return symbol.slice(-2); } function statusView(s: Stock) { switch (s.status) { case "L": return ( 上市 ); case "D": return 退市; case "P": return 暂停; default: return {s.status}; } } export default function StocksPage() { const [stocks, setStocks] = useState([]); const [q, setQ] = useState(""); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const [reloadKey, setReloadKey] = useState(0); const first = useRef(true); const load = useCallback(async () => { setError(""); try { const list = await apiGet( `/stocks?limit=${LIMIT}&q=${encodeURIComponent(q)}`, ); setStocks(list); } catch (e) { setError((e as Error).message); setStocks([]); } finally { setLoading(false); } }, [q]); // 输入防抖 250ms 后再请求 useEffect(() => { if (first.current) { first.current = false; void load(); return; } setLoading(true); const t = window.setTimeout(() => void load(), 250); return () => window.clearTimeout(t); }, [q, reloadKey, load]); return ( <> 共 {stocks.length} 条 } /> {error ? ( 加载失败:{error} ) : null}
setQ("")} placeholder="按代码 / 名称搜索,如 600519 或 茅台" label="搜索股票" />
{stocks.map((s) => ( ))}
代码 名称 市场 行业 地区 上市日 状态
{s.symbol} {s.name} {marketOf(s.symbol)} {s.industry ? {s.industry} : —} {s.area ?? "—"} {s.list_date} {statusView(s)}
{loading && !error && stocks.length === 0 ? (
) : null} {!loading && !error && stocks.length === 0 ? ( ) : null} {stocks.length > 0 ? (
显示前 {stocks.length} 条(接口上限 {LIMIT}) 上市 暂停 退市
) : null}
); } function Dot({ color }: { color: string }) { return (