- 新增 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>
61 lines
2.1 KiB
TypeScript
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>
|
|
)
|
|
}
|