feat: 核心股追踪题材默认展开显示
移除展开/收起交互,题材标签区始终展示 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+59
-72
@@ -1,8 +1,8 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Fragment, useState } from "react";
|
||||
import { Fragment } from "react";
|
||||
import { fetchActiveCoreStocks } from "@/lib/core-stock-api";
|
||||
import { ArrowLeft, RefreshCw, Flame, ChevronDown, ChevronRight } from "lucide-react";
|
||||
import { ArrowLeft, RefreshCw, Flame } from "lucide-react";
|
||||
|
||||
export const Route = createFileRoute("/core-stocks")({
|
||||
component: CoreStocksPage,
|
||||
@@ -25,7 +25,6 @@ function CoreStocksPage() {
|
||||
|
||||
const dates = data?.dates ?? [];
|
||||
const stocks = data?.stocks ?? [];
|
||||
const [expanded, setExpanded] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
@@ -76,76 +75,64 @@ function CoreStocksPage() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{stocks.map((s) => {
|
||||
const isOpen = expanded === s.stockCode;
|
||||
return (
|
||||
<Fragment key={s.stockCode}>
|
||||
<tr
|
||||
onClick={() => setExpanded(isOpen ? null : s.stockCode)}
|
||||
className="border-b last:border-0 hover:bg-muted/30 cursor-pointer"
|
||||
>
|
||||
<td className="px-3 py-1.5 whitespace-nowrap">
|
||||
<span className="inline-flex items-center gap-1">
|
||||
{isOpen ? <ChevronDown className="h-3.5 w-3.5 text-muted-foreground" /> : <ChevronRight className="h-3.5 w-3.5 text-muted-foreground" />}
|
||||
<Link
|
||||
to="/stock/$code"
|
||||
params={{ code: s.stockCode }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="font-medium hover:text-primary hover:underline"
|
||||
>
|
||||
{s.stockName}
|
||||
</Link>
|
||||
<span className="ml-1 text-[10px] text-muted-foreground">{s.stockCode}</span>
|
||||
</span>
|
||||
</td>
|
||||
{dates.map((d) => {
|
||||
const g = s.dailyGains[d];
|
||||
const cls = g == null ? "text-muted-foreground/40" : g >= 0 ? "text-red-500" : "text-green-500";
|
||||
return (
|
||||
<td key={d} className={`px-2 py-1.5 text-right tabular-nums whitespace-nowrap ${cls}`}>
|
||||
{formatGain(g)}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
<td className="px-2 py-1.5 text-right tabular-nums whitespace-nowrap text-muted-foreground">
|
||||
{s.coverCount ?? "·"}
|
||||
</td>
|
||||
<td className="px-2 py-1.5 text-right tabular-nums whitespace-nowrap text-muted-foreground">
|
||||
{s.lastAppear ? s.lastAppear.slice(5) : "·"}
|
||||
</td>
|
||||
<td className="px-2 py-1.5 text-right tabular-nums whitespace-nowrap">
|
||||
<span className="inline-flex items-center gap-0.5 text-orange-500">
|
||||
<Flame className="h-3 w-3" />
|
||||
{s.appearCount}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{isOpen && (
|
||||
<tr className="border-b bg-muted/20">
|
||||
<td colSpan={dates.length + 4} className="px-3 py-2">
|
||||
<div className="text-[11px] text-muted-foreground mb-1">所属题材</div>
|
||||
{s.themes && s.themes.length > 0 ? (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{s.themes.map((t) => (
|
||||
<Link
|
||||
key={t.theme_code}
|
||||
to="/theme/$code"
|
||||
params={{ code: t.theme_code }}
|
||||
className="px-1.5 py-0.5 rounded bg-primary/10 text-primary text-[11px] hover:bg-primary/20 transition-colors"
|
||||
>
|
||||
{t.theme_name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-xs text-muted-foreground">暂无题材数据</span>
|
||||
)}
|
||||
{stocks.map((s) => (
|
||||
<Fragment key={s.stockCode}>
|
||||
<tr className="border-b last:border-0 hover:bg-muted/30">
|
||||
<td className="px-3 py-1.5 whitespace-nowrap">
|
||||
<Link
|
||||
to="/stock/$code"
|
||||
params={{ code: s.stockCode }}
|
||||
className="font-medium hover:text-primary hover:underline"
|
||||
>
|
||||
{s.stockName}
|
||||
</Link>
|
||||
<span className="ml-1 text-[10px] text-muted-foreground">{s.stockCode}</span>
|
||||
</td>
|
||||
{dates.map((d) => {
|
||||
const g = s.dailyGains[d];
|
||||
const cls = g == null ? "text-muted-foreground/40" : g >= 0 ? "text-red-500" : "text-green-500";
|
||||
return (
|
||||
<td key={d} className={`px-2 py-1.5 text-right tabular-nums whitespace-nowrap ${cls}`}>
|
||||
{formatGain(g)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
);
|
||||
})}
|
||||
<td className="px-2 py-1.5 text-right tabular-nums whitespace-nowrap text-muted-foreground">
|
||||
{s.coverCount ?? "·"}
|
||||
</td>
|
||||
<td className="px-2 py-1.5 text-right tabular-nums whitespace-nowrap text-muted-foreground">
|
||||
{s.lastAppear ? s.lastAppear.slice(5) : "·"}
|
||||
</td>
|
||||
<td className="px-2 py-1.5 text-right tabular-nums whitespace-nowrap">
|
||||
<span className="inline-flex items-center gap-0.5 text-orange-500">
|
||||
<Flame className="h-3 w-3" />
|
||||
{s.appearCount}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className="border-b bg-muted/20">
|
||||
<td colSpan={dates.length + 4} className="px-3 py-2">
|
||||
<div className="text-[11px] text-muted-foreground mb-1">所属题材</div>
|
||||
{s.themes && s.themes.length > 0 ? (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{s.themes.map((t) => (
|
||||
<Link
|
||||
key={t.theme_code}
|
||||
to="/theme/$code"
|
||||
params={{ code: t.theme_code }}
|
||||
className="px-1.5 py-0.5 rounded bg-primary/10 text-primary text-[11px] hover:bg-primary/20 transition-colors"
|
||||
>
|
||||
{t.theme_name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-xs text-muted-foreground">暂无题材数据</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user