Files
zhixing-system/zhixing-web/src/features/selection/components/pattern-score.tsx
T

100 lines
3.0 KiB
TypeScript
Raw Normal View History

import { Badge } from "@/shared/ui/badge"
import type { SelectionPatternScore } from "../api/selection.types"
interface PatternScoreProps {
score: SelectionPatternScore | null
}
const breakdownLabels = {
trend_structure: "趋势",
kdj_state: "KDJ",
volume_pattern: "量能",
price_shape: "价格形态",
} as const
export function PatternScoreSummary({ score }: PatternScoreProps) {
if (!score) return <span className="text-muted-foreground">未评分</span>
if (score.status === "failed") {
return <Badge variant="destructive">评分暂不可用</Badge>
}
if (score.status === "below_threshold") {
return (
<Badge variant="outline">
未匹配到 {formatScore(score.threshold)} 分以上案例
</Badge>
)
}
return (
<span className="space-y-0.5">
<strong className="block tabular-nums text-primary">
{formatScore(score.value)} 分
</strong>
<span className="block text-[10px] text-muted-foreground">
{score.case?.name ?? "最佳案例未知"}
</span>
</span>
)
}
export function PatternScoreDetails({ score }: PatternScoreProps) {
if (!score) {
return (
<p className="text-xs text-muted-foreground">本次运行未执行图形评分。</p>
)
}
if (score.status === "failed") {
return (
<div className="space-y-1">
<Badge variant="destructive">评分暂不可用</Badge>
<p className="text-xs text-muted-foreground">
{score.reason || "案例库或评分计算暂时不可用。"}
</p>
</div>
)
}
if (score.status === "below_threshold") {
return (
<div className="space-y-1">
<Badge variant="outline">低于匹配阈值</Badge>
<p className="text-xs text-muted-foreground">
未匹配到 {formatScore(score.threshold)} 分以上案例。
</p>
</div>
)
}
return (
<div className="space-y-3">
<div className="flex items-start justify-between gap-3">
<div>
<p className="text-2xl font-semibold tabular-nums">
{formatScore(score.value)} 分
</p>
<p className="text-xs text-muted-foreground">
最佳案例:{score.case?.name ?? "未知"}
</p>
</div>
<Badge variant="default">已匹配</Badge>
</div>
{score.breakdown ? (
<dl className="grid grid-cols-2 gap-2 text-xs">
{Object.entries(breakdownLabels).map(([key, label]) => (
<div key={key} className="rounded-md border border-border/60 p-2">
<dt className="text-muted-foreground">{label}</dt>
<dd className="mt-1 font-semibold tabular-nums">
{formatScore(
score.breakdown?.[key as keyof typeof breakdownLabels],
)}
</dd>
</div>
))}
</dl>
) : null}
</div>
)
}
function formatScore(value: number | null | undefined) {
return value === null || value === undefined ? "—" : value.toFixed(2)
}