Files
BossAi/web2/src/components/common/MatterCard.tsx
T
selfreleaseandDevin 4de36547cd
CI / 后端测试 (push) Has been cancelled
CI / 前端构建 (push) Has been cancelled
feat: BossAi v2.0 UI/UX 重构 + 看板断点续接 + 多轮上下文
- 新增 web2/ 前端:任务卡看板、风险雷达、决策日志、我的团队、快捷指令
- ChatPanel 增强:风险卡置顶、案卡接入、起诉状向导、语音输入
- 事项看板:点击查看详情弹窗、断点续接(恢复历史对话+matterId)
- 保密模式恢复:密码验证后重建内存 session,保留多轮上下文
- 多轮对话:ask/ask-stream/ask-analyze 注入 session 历史,AI 有上下文
- 追问优化:紧扣用户问题,不再偏离主题(社保问社保,工伤问工伤)
- 对话区可滚动,输入区固定可见
- 状态机放宽:允许从任意非终态转到 pending_expert
- session 存活检查端点,后端重启后自动重建 session
- DB migration:风险推送表、案卡表

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-31 08:38:33 +08:00

61 lines
2.1 KiB
TypeScript

/** 案卡组件(TASK-009)
* 公共组件:展示单张案卡,支持点击和归档
*/
import { Scale, TrendingUp, Heart, ShieldCheck, Archive } from 'lucide-react'
import { cn, MATTER_STATUS_LABELS, MATTER_STATUS_COLORS, SCENE_LABELS, formatRelative } from '@/lib/utils'
import type { Matter, Scene } from '@/types'
const SCENE_ICONS: Record<Scene, typeof Scale> = {
legal: Scale,
decision: TrendingUp,
life: Heart,
emotion: ShieldCheck,
}
interface MatterCardProps {
matter: Matter
onClick?: () => void
onArchive?: () => void
}
export function MatterCard({ matter, onClick, onArchive }: MatterCardProps) {
const Icon = SCENE_ICONS[matter.scene] || Scale
return (
<div
onClick={onClick}
className={cn(
'group cursor-pointer rounded-xl border bg-card p-3 shadow-sm transition-all hover:shadow-md',
MATTER_STATUS_COLORS[matter.status],
)}
>
<div className="flex items-start gap-2">
<div className="mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-white/60">
<Icon className="h-4 w-4" />
</div>
<div className="min-w-0 flex-1">
<div className="truncate text-sm font-medium text-foreground">{matter.title}</div>
<div className="mt-1 flex items-center gap-2 text-xs text-muted-foreground">
<span>{SCENE_LABELS[matter.scene]}</span>
<span>·</span>
<span>{formatRelative(matter.updated_at)}</span>
</div>
<div className="mt-2">
<span className={cn('inline-block rounded-full border px-2 py-0.5 text-xs', MATTER_STATUS_COLORS[matter.status])}>
{MATTER_STATUS_LABELS[matter.status]}
</span>
</div>
</div>
{onArchive && matter.status === 'closed' && (
<button
onClick={(e) => { e.stopPropagation(); onArchive() }}
className="opacity-0 transition-opacity group-hover:opacity-100"
aria-label="归档"
>
<Archive className="h-4 w-4 text-muted-foreground hover:text-foreground" />
</button>
)}
</div>
</div>
)
}