feat(selection): 集成 B1 FastDTW 图形评分
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user