Files
Profeto/frontend/src/components/ui/Stat.tsx
T
shangfangjian 2cfc9ccc0f refactor(ui): 合并双组件库为 components/ui 基元层,设计令牌变量化,报头字体子集自托管
- 新建 components/ui/(Button/Input/Select/Modal/Tabs/Spinner/Skeleton 等),变体用联合类型约束,根治幻影变体
- admin/components.tsx 与 matches/ui.tsx 改为再导出壳,消除 Spinner 三份重复
- 日期工具收口到 lib/date.ts,滚动监听收口到 lib/useScroll.ts
- index.css 引入 :root RGB 三元组令牌,tailwind.config 接 <alpha-value>(修复透明度修饰符静默失效)
- 刘建毛草 5MB 全字库(jsDelivr 未锁版) → 1KB 自托管子集(仅「先知」二字,unicode-range 限定)
2026-09-22 23:44:46 +08:00

68 lines
2.4 KiB
TypeScript

/**
* 统计与进度可视化:StatCard / ProgressBar / AgentWeightsBar。
* 原位于 admin/components.tsx。
*/
export function StatCard({
label,
value,
hint,
}: {
label: string
value: string | number
hint?: string
}) {
return (
<div className="border border-ink-900 bg-paper-50 px-4 py-3.5">
<span className="text-2xs tracking-[0.2em] text-ink-400">{label}</span>
<div className="mt-1.5 font-serif text-3xl font-bold tabular-nums leading-none text-ink-900">
{value}
</div>
{hint && <div className="mt-1.5 text-2xs text-ink-400">{hint}</div>}
</div>
)
}
/** 进度条:同前台置信度细线 */
export function ProgressBar({ value, className = '' }: { value: number; className?: string }) {
const clamped = Math.max(0, Math.min(100, value))
return (
<div className={`h-2 w-full overflow-hidden rounded-full bg-ink-200 ${className}`} role="progressbar" aria-valuenow={clamped}>
<div
className="h-full rounded-full bg-press transition-[width] duration-500"
style={{ width: `${clamped}%` }}
/>
</div>
)
}
/** Agent 权重条形图 */
export function AgentWeightsBar({ weights, okCount }: { weights: Record<string, number>; okCount: number }) {
const entries = Object.entries(weights).filter(([, w]) => w > 0)
if (entries.length === 0) return null
const total = entries.reduce((s, [, w]) => s + w, 0) || 1
const colors = ['bg-ink-900', 'bg-ink-700', 'bg-ink-500', 'bg-press', 'bg-ink-300']
return (
<div className="mt-2 border-t border-ink-200 pt-2">
<div className="mb-1 text-2xs text-ink-400">终裁专家权重</div>
<div className="space-y-1">
{entries.map(([k, w], i) => (
<div key={k} className="flex items-center gap-2 text-2xs">
<div className="h-3.5 flex-1 overflow-hidden rounded-sm bg-ink-200/60">
<div
className={`h-full ${colors[i % colors.length]} transition-all duration-500`}
style={{ width: `${Math.max(3, Math.round((w / total) * 100))}%` }}
/>
</div>
<span className="w-12 text-right tabular-nums text-ink-500">
{Math.round((w / total) * 100)}%
</span>
<span className="w-24 truncate text-ink-400" title={k}>{k}</span>
</div>
))}
</div>
<div className="mt-1 text-2xs text-ink-400">有效专家:{okCount}/{entries.length}</div>
</div>
)
}