diff --git a/src/routes/sectors.tsx b/src/routes/sectors.tsx index 9d3db24..1557a80 100644 --- a/src/routes/sectors.tsx +++ b/src/routes/sectors.tsx @@ -1,29 +1,28 @@ import { createFileRoute, Link } from "@tanstack/react-router"; -import { useState, useMemo } from "react"; +import { useMemo, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { fetchSectors, type SectorItem, type SectorType } from "@/lib/stock-api"; import { Card, CardContent } from "@/components/ui/card"; -import { ArrowLeft, TrendingUp, TrendingDown, RefreshCw } from "lucide-react"; +import { ArrowLeft, RefreshCw, ArrowDown, ArrowUp, TrendingUp, TrendingDown } from "lucide-react"; export const Route = createFileRoute("/sectors")({ component: SectorsPage, }); -/* ── Tab 定义 ── */ +/* ── Tabs:行业 / 概念 ── */ const TABS: { key: SectorType; label: string }[] = [ - { key: "industry", label: "行业板块" }, - { key: "concept", label: "概念板块" }, + { key: "industry", label: "行业" }, + { key: "concept", label: "概念" }, ]; -type SortMode = "mainNetInflow" | "changePercent"; +/* ── 排序维度 ── */ +type SortKey = "mainNetInflow" | "changePercent"; -/* ── 页面组件 ── */ function SectorsPage() { const [tab, setTab] = useState("industry"); - const [sort, setSort] = useState("mainNetInflow"); + const [sortKey, setSortKey] = useState("mainNetInflow"); + const [asc, setAsc] = useState(false); - // 使用 useQuery:queryKey 包含 tab,切换 tab 时 queryKey 变化 - // React Query 自动隔离 industry / concept 的数据,不会混用 const { data = [], isLoading, isFetching, refetch } = useQuery({ queryKey: ["sectors", tab], queryFn: ({ signal }) => fetchSectors(tab, signal), @@ -31,14 +30,22 @@ function SectorsPage() { retry: false, }); - const sorted = useMemo( - () => - [...data].sort((a, b) => { - if (sort === "mainNetInflow") return b.mainNetInflow - a.mainNetInflow; - return (b.changePercent ?? 0) - (a.changePercent ?? 0); - }), - [data, sort], - ); + const sorted = useMemo(() => { + const dir = asc ? 1 : -1; + return [...data].sort((a, b) => { + const av = sortKey === "mainNetInflow" ? a.mainNetInflow : a.changePercent ?? -Infinity; + const bv = sortKey === "mainNetInflow" ? b.mainNetInflow : b.changePercent ?? -Infinity; + return (bv - av) * dir; + }); + }, [data, sortKey, asc]); + + const toggleSort = (key: SortKey) => { + if (key === sortKey) setAsc((v) => !v); + else { + setSortKey(key); + setAsc(false); + } + }; return (
@@ -60,9 +67,9 @@ function SectorsPage() {
- {/* Tab + 排序切换 */} -
-
+ {/* Tab 切换:行业 / 概念 */} +
+
{TABS.map((t) => ( ))}
-
- - -
- {/* 数据概览 */} -
+ {/* 排序切换 */} +

- 共 {sorted.length} 个板块 · 按{sort === "mainNetInflow" ? "主力净流入" : "涨跌幅"}降序 + 共 {sorted.length} 个板块 · {tab === "industry" ? "行业" : "概念"}

+
+ toggleSort("mainNetInflow")} /> + toggleSort("changePercent")} /> +
{/* 卡片列表 */} @@ -130,7 +119,31 @@ function SectorsPage() { ); } -/* ── 工具函数 ── */ +function SortButton({ + label, + active, + asc, + onClick, +}: { + label: string; + active: boolean; + asc: boolean; + onClick: () => void; +}) { + return ( + + ); +} + +/* ── 数值格式化 ── */ function formatInflow(val: number | null | undefined): string { if (val == null) return "--"; const abs = Math.abs(val); @@ -202,11 +215,7 @@ function SectorBlock({ item }: { item: SectorItem }) { change >= 0 ? "text-red-500" : "text-green-500" }`} > - {change >= 0 ? ( - - ) : ( - - )} + {change >= 0 ? : } {change >= 0 ? "+" : ""} {change.toFixed(2)}%