Develop #32
@@ -1,4 +1,4 @@
|
|||||||
import { lazy, Suspense, useState } from "react"
|
import { lazy, Suspense, useState, type ReactNode } from "react"
|
||||||
import { Button } from "@/shared/ui/button"
|
import { Button } from "@/shared/ui/button"
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -47,55 +47,91 @@ export function RadarDetailDialog({ row }: { row: RadarRankingRow }) {
|
|||||||
{row.sector_name}
|
{row.sector_name}
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="max-h-[90dvh] grid-cols-1 overflow-y-auto sm:max-w-4xl">
|
<DialogContent className="max-h-[90dvh] grid-cols-1 overflow-y-auto sm:max-w-4xl">
|
||||||
<DialogTitle>{row.sector_name}</DialogTitle>
|
{open ? <RadarDetail row={row} /> : null}
|
||||||
<DialogDescription>
|
|
||||||
{row.sector_type === "concept" ? "概念" : "行业"} · {row.sector_code}{" "}
|
|
||||||
· {row.trade_date}
|
|
||||||
</DialogDescription>
|
|
||||||
{open ? (
|
|
||||||
<RadarDetail
|
|
||||||
initialMetric={row.metric_kind}
|
|
||||||
query={{
|
|
||||||
sectorType: row.sector_type,
|
|
||||||
sectorCode: row.sector_code,
|
|
||||||
tradeDate: row.trade_date,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function RadarDetail({
|
function RadarDetail({ row }: { row: RadarRankingRow }) {
|
||||||
query,
|
const query: RadarSectorQuery = {
|
||||||
initialMetric,
|
sectorType: row.sector_type,
|
||||||
}: {
|
sectorCode: row.sector_code,
|
||||||
query: RadarSectorQuery
|
tradeDate: row.trade_date,
|
||||||
initialMetric: RadarMetricKind
|
}
|
||||||
}) {
|
|
||||||
const detail = useSectorRadarDetail(query)
|
const detail = useSectorRadarDetail(query)
|
||||||
if (detail.isPending) return <p role="status">正在加载板块详情…</p>
|
if (detail.isPending)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RadarDetailHeader row={row} />
|
||||||
|
<p role="status">正在加载板块详情…</p>
|
||||||
|
</>
|
||||||
|
)
|
||||||
if (detail.isError)
|
if (detail.isError)
|
||||||
return (
|
return (
|
||||||
<div role="alert">
|
<>
|
||||||
板块详情加载失败。
|
<RadarDetailHeader row={row} />
|
||||||
<Button onClick={() => void detail.refetch()} variant="outline">
|
<div role="alert">
|
||||||
重试详情
|
板块详情加载失败。
|
||||||
</Button>
|
<Button onClick={() => void detail.refetch()} variant="outline">
|
||||||
</div>
|
重试详情
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
if (!detail.data || detail.data.status === "no_data")
|
if (!detail.data || detail.data.status === "no_data")
|
||||||
return <p>暂无板块详情数据。</p>
|
return (
|
||||||
return <RadarDetailBody data={detail.data} initialMetric={initialMetric} />
|
<>
|
||||||
|
<RadarDetailHeader row={row} />
|
||||||
|
<p>暂无板块详情数据。</p>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<RadarDetailBody
|
||||||
|
data={detail.data}
|
||||||
|
initialMetric={row.metric_kind}
|
||||||
|
row={row}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function RadarDetailHeader({
|
||||||
|
row,
|
||||||
|
actions,
|
||||||
|
}: {
|
||||||
|
row: RadarRankingRow
|
||||||
|
actions?: ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
role="group"
|
||||||
|
aria-label="板块详情标题栏"
|
||||||
|
className="flex min-w-0 flex-col gap-2 pr-8 sm:flex-row sm:items-center"
|
||||||
|
>
|
||||||
|
<div className="flex min-w-0 flex-wrap items-baseline gap-x-3 gap-y-1">
|
||||||
|
<DialogTitle className="shrink-0">{row.sector_name}</DialogTitle>
|
||||||
|
<DialogDescription className="whitespace-nowrap">
|
||||||
|
{row.sector_type === "concept" ? "概念" : "行业"} · {row.sector_code}{" "}
|
||||||
|
· {row.trade_date}
|
||||||
|
</DialogDescription>
|
||||||
|
</div>
|
||||||
|
{actions ? (
|
||||||
|
<div className="flex shrink-0 items-center gap-2 sm:ml-auto">
|
||||||
|
{actions}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function RadarDetailBody({
|
function RadarDetailBody({
|
||||||
data,
|
data,
|
||||||
initialMetric,
|
initialMetric,
|
||||||
|
row,
|
||||||
}: {
|
}: {
|
||||||
data: RadarDetailResponse
|
data: RadarDetailResponse
|
||||||
initialMetric: RadarMetricKind
|
initialMetric: RadarMetricKind
|
||||||
|
row: RadarRankingRow
|
||||||
}) {
|
}) {
|
||||||
const [metric, setMetric] = useState<RadarMetricKind>(initialMetric)
|
const [metric, setMetric] = useState<RadarMetricKind>(initialMetric)
|
||||||
const [memberMetric, setMemberMetric] =
|
const [memberMetric, setMemberMetric] =
|
||||||
@@ -130,39 +166,50 @@ function RadarDetailBody({
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="min-w-0 space-y-5">
|
<div className="min-w-0 space-y-5">
|
||||||
<div className="flex flex-wrap justify-end gap-2">
|
<div className="space-y-3">
|
||||||
<Button
|
<RadarDetailHeader
|
||||||
variant="outline"
|
row={row}
|
||||||
size="sm"
|
actions={
|
||||||
disabled={!data.members.length}
|
<>
|
||||||
onClick={() => void copyMembers()}
|
{exportStatus ? (
|
||||||
>
|
<p role="status" className="text-xs text-muted-foreground">
|
||||||
复制成员
|
{exportStatus}
|
||||||
</Button>
|
</p>
|
||||||
<Button
|
) : null}
|
||||||
variant="outline"
|
<Button
|
||||||
size="sm"
|
variant="outline"
|
||||||
disabled={!data.members.length}
|
size="sm"
|
||||||
onClick={exportMembers}
|
disabled={!data.members.length}
|
||||||
>
|
onClick={() => void copyMembers()}
|
||||||
导出成员
|
>
|
||||||
</Button>
|
复制成员
|
||||||
</div>
|
</Button>
|
||||||
{exportStatus ? <p role="status">{exportStatus}</p> : null}
|
<Button
|
||||||
<div className="flex flex-wrap gap-x-6 gap-y-2 rounded-md bg-muted/50 p-3">
|
variant="outline"
|
||||||
<p>实际交易日:{data.trade_date ?? "—"}</p>
|
size="sm"
|
||||||
<p>
|
disabled={!data.members.length}
|
||||||
当日涨跌幅:
|
onClick={exportMembers}
|
||||||
<span className={radarValueTone(data.pct_change)}>
|
>
|
||||||
{formatRadarValue(data.pct_change, "percent")}
|
导出成员
|
||||||
</span>
|
</Button>
|
||||||
</p>
|
</>
|
||||||
<p>
|
}
|
||||||
领涨股:
|
/>
|
||||||
{data.leading_stock
|
<div className="flex flex-wrap gap-x-6 gap-y-1 rounded-md bg-muted/50 px-3 py-2">
|
||||||
? `${data.leading_stock.name ?? ""} ${data.leading_stock.ts_code}`
|
<p>实际交易日:{data.trade_date ?? "—"}</p>
|
||||||
: "暂无数据"}
|
<p>
|
||||||
</p>
|
当日涨跌幅:
|
||||||
|
<span className={radarValueTone(data.pct_change)}>
|
||||||
|
{formatRadarValue(data.pct_change, "percent")}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
领涨股:
|
||||||
|
{data.leading_stock
|
||||||
|
? `${data.leading_stock.name ?? ""} ${data.leading_stock.ts_code}`
|
||||||
|
: "暂无数据"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<section aria-label="三指标排名" className="space-y-3">
|
<section aria-label="三指标排名" className="space-y-3">
|
||||||
<div className="grid grid-cols-3 gap-2">
|
<div className="grid grid-cols-3 gap-2">
|
||||||
|
|||||||
@@ -142,6 +142,21 @@ describe("radar detail interactions", () => {
|
|||||||
expect(detailQuery).not.toHaveBeenCalled()
|
expect(detailQuery).not.toHaveBeenCalled()
|
||||||
fireEvent.click(screen.getByRole("button", { name: "机器人" }))
|
fireEvent.click(screen.getByRole("button", { name: "机器人" }))
|
||||||
const dialog = await screen.findByRole("dialog")
|
const dialog = await screen.findByRole("dialog")
|
||||||
|
const compactHeader = within(dialog).getByRole("group", {
|
||||||
|
name: "板块详情标题栏",
|
||||||
|
})
|
||||||
|
expect(
|
||||||
|
within(compactHeader).getByRole("heading", { name: "机器人" }),
|
||||||
|
).toBeInTheDocument()
|
||||||
|
expect(
|
||||||
|
within(compactHeader).getByText(/概念 · BK01 · 2026-08-28/),
|
||||||
|
).toBeInTheDocument()
|
||||||
|
expect(
|
||||||
|
within(compactHeader).getByRole("button", { name: "复制成员" }),
|
||||||
|
).toBeInTheDocument()
|
||||||
|
expect(
|
||||||
|
within(compactHeader).getByRole("button", { name: "导出成员" }),
|
||||||
|
).toBeInTheDocument()
|
||||||
expect(detailQuery).toHaveBeenCalledWith({
|
expect(detailQuery).toHaveBeenCalledWith({
|
||||||
sectorType: "concept",
|
sectorType: "concept",
|
||||||
sectorCode: "BK01",
|
sectorCode: "BK01",
|
||||||
|
|||||||
Reference in New Issue
Block a user