feat(web): 完善选股详情图表与案例图

This commit is contained in:
yuxuanhui
2026-09-01 13:37:19 +08:00
parent 9145fe4b2c
commit feb8fd4feb
27 changed files with 788 additions and 25 deletions
@@ -0,0 +1,47 @@
import { useState } from "react"
import type { SelectionPatternScore } from "../api/selection.types"
import { patternCaseImageById } from "./pattern-case-assets"
interface PatternCaseImageProps {
score: SelectionPatternScore | null
}
export function PatternCaseImage({ score }: PatternCaseImageProps) {
const [failedSource, setFailedSource] = useState<string | null>(null)
const matchedCase = score?.status === "failed" ? null : score?.case
if (!matchedCase) return null
const source =
patternCaseImageById[matchedCase.id as keyof typeof patternCaseImageById]
return (
<section aria-labelledby="best-pattern-case" className="mt-4">
<div className="mb-2 flex items-baseline justify-between gap-3">
<h3 className="text-xs font-medium" id="best-pattern-case">
最接近案例:{matchedCase.name}
</h3>
<span className="text-xs tabular-nums text-muted-foreground">
{formatScore(score?.value)} 分
</span>
</div>
{!source || failedSource === source ? (
<p className="rounded-md border border-border/60 p-3 text-xs text-muted-foreground">
案例图片暂不可用。
</p>
) : (
<img
alt={`${matchedCase.name} B1 参考案例图`}
className="aspect-2/1 w-full rounded-md border border-border/60 object-contain"
loading="lazy"
onError={() => setFailedSource(source)}
src={source}
/>
)}
</section>
)
}
function formatScore(value: number | null | undefined) {
return value === null || value === undefined ? "—" : value.toFixed(2)
}