48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
|
|
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)
|
||
|
|
}
|