chore: preserve frontend concept lab
This commit is contained in:
@@ -233,7 +233,6 @@ export function TicketListPage() {
|
||||
<AppPage contentClassName="gap-5">
|
||||
<AppPageHeader
|
||||
title="工单中心"
|
||||
description="集中查看、筛选和推进企业技术支持请求。"
|
||||
meta={hasActiveFilters ? <Badge variant="info">已筛选</Badge> : <Badge variant="outline">全部范围</Badge>}
|
||||
actions={
|
||||
<Button onClick={() => (window.location.hash = '/tickets/new')}>
|
||||
|
||||
94
frontend/src/features/workbench/ComponentStyleLab.tsx
Normal file
94
frontend/src/features/workbench/ComponentStyleLab.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import * as React from 'react'
|
||||
import { AlertCircle, ArrowLeft, ArrowRight, Bell, Check, ChevronRight, CircleUserRound, Info, Palette, Plus, Search } from 'lucide-react'
|
||||
|
||||
import { Alert, AlertAction, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel } from '@/components/ui/field'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
|
||||
type StyleKey = 'precision' | 'care' | 'graphite' | 'crystal'
|
||||
|
||||
const styles: Array<{ key: StyleKey; name: string; english: string; description: string; colors: string[] }> = [
|
||||
{ key: 'precision', name: '精密企业', english: 'Precision Enterprise', description: '紧凑、理性、低装饰,适合高频业务操作。', colors: ['#174A7E', '#DDEAF2', '#F5F7FA', '#1B2638'] },
|
||||
{ key: 'care', name: '温润服务', english: 'Human Care', description: '亲和、柔软、低压力,适合客服长时间使用。', colors: ['#6750A4', '#DFF2EA', '#F7F5FB', '#292535'] },
|
||||
{ key: 'graphite', name: '石墨焦点', english: 'Graphite Focus', description: '沉稳、高对比、聚焦内容,适合信息密集页面。', colors: ['#2F6BFF', '#263047', '#10141E', '#F2F5FC'] },
|
||||
{ key: 'crystal', name: '晶透层级', english: 'Crystal Layers', description: '清透、现代、具有空间层级,适合产品化升级。', colors: ['#4C5BD4', '#BDE7F2', '#EEF3FF', '#202945'] },
|
||||
]
|
||||
|
||||
export function ComponentStyleLab() {
|
||||
const [style, setStyle] = React.useState<StyleKey>('precision')
|
||||
const selected = styles.find((item) => item.key === style) ?? styles[0]
|
||||
|
||||
return (
|
||||
<main className="component-lab min-h-screen px-4 py-5 text-foreground sm:px-6 lg:px-8" data-style={style}>
|
||||
<div className="mx-auto flex max-w-[1480px] flex-col gap-5">
|
||||
<header className="flex flex-col gap-4 border-b border-border pb-5 lg:flex-row lg:items-end lg:justify-between">
|
||||
<div><Button variant="ghost" size="sm" onClick={() => (window.location.hash = '/concepts')}><ArrowLeft data-icon="inline-start" /> 返回设计入口</Button><div className="mt-4 flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.18em] text-primary"><Palette /> Component style laboratory</div><h1 className="mt-2 text-3xl font-semibold tracking-[-0.04em]">常用组件风格实验室</h1><p className="mt-2 max-w-2xl text-sm text-muted-foreground">切换左侧风格,观察同一组业务组件在色彩、圆角、阴影、边框和信息密度上的变化。</p></div>
|
||||
<div className="flex items-center gap-2"><Badge variant="secondary">当前:{selected.name}</Badge><Button><Plus data-icon="inline-start" /> 新建工单</Button></div>
|
||||
</header>
|
||||
|
||||
<div className="grid gap-5 xl:grid-cols-[260px_minmax(0,1fr)]">
|
||||
<aside className="grid h-fit gap-3 xl:sticky xl:top-5">
|
||||
{styles.map((item) => (
|
||||
<button className={style === item.key ? 'style-choice style-choice-active' : 'style-choice'} key={item.key} onClick={() => setStyle(item.key)} type="button">
|
||||
<div className="flex items-center justify-between"><strong className="text-sm">{item.name}</strong>{style === item.key ? <Check /> : <ChevronRight />}</div>
|
||||
<span className="mt-1 block text-left text-[10px] uppercase tracking-[0.12em] text-muted-foreground">{item.english}</span>
|
||||
<p className="mt-3 text-left text-xs leading-5 text-muted-foreground">{item.description}</p>
|
||||
<div className="mt-3 flex gap-1.5">{item.colors.map((color) => <span className="size-5 rounded-full border border-border" key={color} style={{ backgroundColor: color }} />)}</div>
|
||||
</button>
|
||||
))}
|
||||
</aside>
|
||||
|
||||
<div className="grid gap-5">
|
||||
<section className="style-hero grid gap-5 rounded-xl border border-border p-5 md:grid-cols-[1fr_auto] md:items-center">
|
||||
<div><span className="text-xs font-medium text-muted-foreground">STYLE 0{styles.findIndex((item) => item.key === style) + 1}</span><h2 className="mt-2 text-2xl font-semibold tracking-[-0.035em]">{selected.name}</h2><p className="mt-2 text-sm text-muted-foreground">{selected.description}</p></div>
|
||||
<div className="flex flex-wrap gap-2"><Button variant="outline">次要操作</Button><Button variant="secondary">辅助操作</Button><Button>主要操作 <ArrowRight data-icon="inline-end" /></Button></div>
|
||||
</section>
|
||||
|
||||
<section className="grid gap-5 lg:grid-cols-2">
|
||||
<Card>
|
||||
<CardHeader><CardTitle>按钮与状态</CardTitle><CardDescription>业务操作层级、状态标签和图标按钮</CardDescription></CardHeader>
|
||||
<CardContent className="flex flex-col gap-5">
|
||||
<div className="flex flex-wrap gap-2"><Button>创建工单</Button><Button variant="secondary">保存草稿</Button><Button variant="outline">关联方案</Button><Button variant="ghost">取消</Button><Button variant="destructive">关闭工单</Button><Button size="icon" variant="outline" aria-label="通知"><Bell /></Button></div>
|
||||
<div className="flex flex-wrap gap-2"><Badge>处理中</Badge><Badge variant="secondary">业务问题</Badge><Badge variant="success">已解决</Badge><Badge variant="warning">待回复</Badge><Badge variant="destructive">紧急</Badge><Badge variant="outline">普通</Badge></div>
|
||||
<Alert><Info /><AlertTitle>任务查询完成</AlertTitle><AlertDescription>TASK-8821 仍在采集中,最后更新时间为 10:14。</AlertDescription><AlertAction><Button size="xs" variant="outline">查看</Button></AlertAction></Alert>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader><CardTitle>表单控件</CardTitle><CardDescription>输入、选择、复选与开关状态</CardDescription></CardHeader>
|
||||
<CardContent><FieldGroup>
|
||||
<div className="grid gap-4 sm:grid-cols-2"><Field><FieldLabel htmlFor="lab-customer">提出机构</FieldLabel><Input id="lab-customer" defaultValue="深圳海纳科技有限公司" /></Field><Field><FieldLabel htmlFor="lab-category">问题分类</FieldLabel><Select defaultValue="business"><SelectTrigger className="w-full" id="lab-category"><SelectValue /></SelectTrigger><SelectContent><SelectGroup><SelectItem value="business">业务问题</SelectItem><SelectItem value="operation">操作问题</SelectItem><SelectItem value="data">数据问题</SelectItem></SelectGroup></SelectContent></Select></Field></div>
|
||||
<Field><FieldLabel htmlFor="lab-description">问题描述</FieldLabel><Textarea id="lab-description" defaultValue="客户反馈采集任务长时间没有进度变化。" /></Field>
|
||||
<div className="grid gap-3 sm:grid-cols-2"><Field orientation="horizontal"><Checkbox defaultChecked id="lab-follow" /><FieldContent><FieldLabel htmlFor="lab-follow">持续跟进</FieldLabel><FieldDescription>进度变化时提醒我</FieldDescription></FieldContent></Field><Field orientation="horizontal"><Switch defaultChecked id="lab-ai" /><FieldContent><FieldLabel htmlFor="lab-ai">AI 辅助</FieldLabel><FieldDescription>推荐相似解决方案</FieldDescription></FieldContent></Field></div>
|
||||
</FieldGroup></CardContent>
|
||||
</Card>
|
||||
</section>
|
||||
|
||||
<section className="grid gap-5 lg:grid-cols-[1.15fr_0.85fr]">
|
||||
<Card className="gap-0 py-0">
|
||||
<CardHeader className="border-b border-border px-5 py-4"><div className="flex items-center justify-between"><div><CardTitle>工单列表组件</CardTitle><CardDescription>同一内容在不同风格下的信息密度</CardDescription></div><Button size="sm" variant="outline"><Search data-icon="inline-start" /> 搜索</Button></div></CardHeader>
|
||||
<CardContent className="px-0">{[
|
||||
['查询申报数据采集进度','深圳海纳科技有限公司','待回复','TASK-8821'],['税局采集任务停留在初始化阶段','广州远望供应链有限公司','处理中','TASK-8794'],['财务人员登录生产环境失败','杭州云帆制造有限公司','待回复','账号 zhangqi'],
|
||||
].map(([title,customer,status,object],index)=><button className="style-ticket-row grid w-full grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 border-b border-border p-4 text-left last:border-0" key={title} type="button"><span className="style-avatar"><CircleUserRound /></span><span className="min-w-0"><strong className="block truncate text-sm">{title}</strong><span className="mt-1 block truncate text-xs text-muted-foreground">{customer} · {object}</span></span><span className="flex items-center gap-2"><Badge variant={index===1?'secondary':'warning'}>{status}</Badge><ChevronRight /></span></button>)}</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader><CardTitle>导航与内容卡片</CardTitle><CardDescription>标签页、数据摘要与错误状态</CardDescription></CardHeader>
|
||||
<CardContent><Tabs defaultValue="overview"><TabsList><TabsTrigger value="overview">概览</TabsTrigger><TabsTrigger value="timeline">时间线</TabsTrigger><TabsTrigger value="solution">解决方案</TabsTrigger></TabsList><TabsContent className="pt-4" value="overview"><div className="grid grid-cols-2 gap-3"><div className="style-stat"><span>待我处理</span><strong>12</strong></div><div className="style-stat"><span>今日解决</span><strong>18</strong></div></div></TabsContent><TabsContent className="pt-4" value="timeline"><p className="text-sm text-muted-foreground">09:52 客户提交问题 · 10:16 返回查询结果</p></TabsContent><TabsContent className="pt-4" value="solution"><p className="text-sm text-muted-foreground">匹配到 3 条可复用解决方案。</p></TabsContent></Tabs><Alert className="mt-4" variant="destructive"><AlertCircle /><AlertTitle>无法获取最新进度</AlertTitle><AlertDescription>上游服务暂时不可用,请稍后重试。</AlertDescription></Alert></CardContent>
|
||||
<CardFooter className="justify-end"><Button variant="outline">关闭</Button><Button className="ml-2">继续处理</Button></CardFooter>
|
||||
</Card>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
77
frontend/src/features/workbench/TicketScreenConcepts.tsx
Normal file
77
frontend/src/features/workbench/TicketScreenConcepts.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { ArrowLeft, ArrowRight, Bot, Building2, Check, ChevronRight, CircleCheck, ClipboardList, History, Lightbulb, Link2, MessageSquareText, Paperclip, Save, Search, Send, Sparkles, UserRound } from 'lucide-react'
|
||||
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Field, FieldDescription, FieldGroup, FieldLabel, FieldSet } from '@/components/ui/field'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
|
||||
function go(path: string) { window.location.hash = path }
|
||||
|
||||
function ScreenNav({ current, otherPath, otherLabel }: { current: string; otherPath: string; otherLabel: string }) {
|
||||
return <div className="flex flex-wrap items-center justify-between gap-3 border-b border-border pb-4"><Button variant="ghost" size="sm" onClick={() => go('/concepts')}><ArrowLeft data-icon="inline-start" /> 返回设计入口</Button><div className="flex items-center gap-2"><Badge variant="secondary">{current}</Badge><Button variant="outline" size="sm" onClick={() => go(otherPath)}>{otherLabel}<ArrowRight data-icon="inline-end" /></Button></div></div>
|
||||
}
|
||||
|
||||
function Required() { return <span className="text-destructive">*</span> }
|
||||
|
||||
export function TicketCreateConcept() {
|
||||
return (
|
||||
<main className="ticket-screen min-h-screen px-4 py-5 text-foreground sm:px-6 lg:px-8">
|
||||
<div className="mx-auto flex max-w-[1380px] flex-col gap-5">
|
||||
<ScreenNav current="核心页面 · 工单填写" otherPath="/concept/ticket-detail" otherLabel="查看工单详情" />
|
||||
<header className="flex flex-col gap-4 md:flex-row md:items-end md:justify-between"><div><p className="text-xs font-medium text-muted-foreground">客户问题记录</p><h1 className="mt-1 text-3xl font-semibold tracking-[-0.04em]">新建工单</h1><p className="mt-2 text-sm text-muted-foreground">记录客户遇到的问题,信息越完整,后续查询和解决越高效。</p></div><div className="flex items-center gap-2"><Button variant="outline"><Save data-icon="inline-start" /> 保存草稿</Button><Button><Check data-icon="inline-start" /> 创建工单</Button></div></header>
|
||||
<div className="grid gap-5 xl:grid-cols-[210px_minmax(0,1fr)_320px]">
|
||||
<Card className="h-fit"><CardHeader><CardTitle className="text-sm">填写进度</CardTitle><CardDescription>已完成 2 / 4</CardDescription></CardHeader><CardContent className="flex flex-col gap-1">{[
|
||||
{ icon: CircleCheck, label: '问题分类', state: '已完成' }, { icon: CircleCheck, label: '客户与对象', state: '已完成' }, { icon: ClipboardList, label: '问题描述', state: '正在填写' }, { icon: Paperclip, label: '补充信息', state: '可选' },
|
||||
].map((step,index)=><div className={index===2?'ticket-step ticket-step-active':'ticket-step'} key={step.label}><span className="ticket-step-icon"><step.icon /></span><span><strong className="block text-xs">{step.label}</strong><span className="text-[11px] text-muted-foreground">{step.state}</span></span></div>)}</CardContent></Card>
|
||||
<form className="flex flex-col gap-5">
|
||||
<Card><CardHeader><CardTitle className="flex items-center gap-2"><span className="ticket-section-no">01</span> 问题分类</CardTitle><CardDescription>分类会影响工单流程和后续推荐的处理方案。</CardDescription></CardHeader><CardContent><FieldSet><FieldGroup className="grid gap-4 md:grid-cols-2">
|
||||
<Field><FieldLabel htmlFor="create-category">问题分类 <Required /></FieldLabel><Select defaultValue="business"><SelectTrigger className="w-full" id="create-category"><SelectValue /></SelectTrigger><SelectContent><SelectGroup><SelectItem value="business">业务问题</SelectItem><SelectItem value="operation">操作问题</SelectItem><SelectItem value="data">数据问题</SelectItem><SelectItem value="technical">技术问题</SelectItem></SelectGroup></SelectContent></Select><FieldDescription>采集进度、业务规则或状态确认</FieldDescription></Field>
|
||||
<Field><FieldLabel htmlFor="create-priority">优先级 <Required /></FieldLabel><Select defaultValue="normal"><SelectTrigger className="w-full" id="create-priority"><SelectValue /></SelectTrigger><SelectContent><SelectGroup><SelectItem value="low">低</SelectItem><SelectItem value="normal">普通</SelectItem><SelectItem value="high">高</SelectItem><SelectItem value="urgent">紧急</SelectItem></SelectGroup></SelectContent></Select></Field>
|
||||
</FieldGroup></FieldSet></CardContent></Card>
|
||||
<Card><CardHeader><CardTitle className="flex items-center gap-2"><span className="ticket-section-no">02</span> 客户与问题对象</CardTitle><CardDescription>明确是哪家客户、哪个任务或账号遇到了问题。</CardDescription></CardHeader><CardContent><FieldSet><FieldGroup>
|
||||
<div className="grid gap-4 md:grid-cols-2"><Field><FieldLabel htmlFor="create-org">提出机构 <Required /></FieldLabel><Input id="create-org" defaultValue="深圳海纳科技有限公司" /></Field><Field><FieldLabel htmlFor="create-person">提出人 <Required /></FieldLabel><Input id="create-person" defaultValue="李经理" /></Field></div>
|
||||
<div className="grid gap-4 md:grid-cols-2"><Field><FieldLabel htmlFor="create-object">问题对象 <Required /></FieldLabel><Input id="create-object" defaultValue="TASK-8821" /><FieldDescription>客户名称、账号、机构或任务编号</FieldDescription></Field><Field><FieldLabel htmlFor="create-env">环境 <Required /></FieldLabel><Select defaultValue="production"><SelectTrigger className="w-full" id="create-env"><SelectValue /></SelectTrigger><SelectContent><SelectGroup><SelectItem value="production">生产环境</SelectItem><SelectItem value="test">测试环境</SelectItem><SelectItem value="unknown">待确认</SelectItem></SelectGroup></SelectContent></Select></Field></div>
|
||||
</FieldGroup></FieldSet></CardContent></Card>
|
||||
<Card className="ticket-current-section"><CardHeader><CardTitle className="flex items-center gap-2"><span className="ticket-section-no">03</span> 问题描述</CardTitle><CardDescription>使用客户能理解的语言描述问题表现,不需要先判断技术原因。</CardDescription></CardHeader><CardContent><FieldSet><FieldGroup>
|
||||
<Field><FieldLabel htmlFor="create-title">工单标题 <Required /></FieldLabel><Input id="create-title" defaultValue="查询申报数据采集进度" /></Field>
|
||||
<Field><FieldLabel htmlFor="create-performance">问题表现 <Required /></FieldLabel><Textarea className="min-h-28" id="create-performance" defaultValue="客户反馈 TASK-8821 从今天早上开始一直显示“采集中”,长时间没有进度变化,希望确认目前处理到哪一步。" /><FieldDescription>建议写明客户看到的状态、发生时间和期望确认的内容。</FieldDescription></Field>
|
||||
<Field><FieldLabel htmlFor="create-scope">影响范围</FieldLabel><Input id="create-scope" defaultValue="单个客户,影响本期申报数据获取" /></Field>
|
||||
</FieldGroup></FieldSet></CardContent></Card>
|
||||
<Card><CardHeader><CardTitle className="flex items-center gap-2"><span className="ticket-section-no">04</span> 补充信息</CardTitle><CardDescription>添加截图、错误信息或客户提供的其他材料。</CardDescription></CardHeader><CardContent><button className="ticket-upload flex min-h-28 w-full flex-col items-center justify-center gap-2 rounded-lg border border-dashed border-border text-sm text-muted-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring" type="button"><Paperclip /><span>拖放附件到这里,或点击选择文件</span><span className="text-xs">支持截图、日志和文档,单个文件不超过 20 MB</span></button></CardContent><CardFooter className="justify-between"><span className="text-xs text-muted-foreground">必填信息已完整,可以创建工单。</span><Button><Check data-icon="inline-start" /> 创建工单</Button></CardFooter></Card>
|
||||
</form>
|
||||
<div className="grid h-fit gap-4 xl:sticky xl:top-5"><Card className="ticket-summary-card"><CardHeader><CardTitle>工单摘要</CardTitle><CardDescription>创建后将按以下信息进入处理队列</CardDescription></CardHeader><CardContent className="flex flex-col gap-4"><div><Badge>业务问题</Badge><Badge className="ml-2" variant="secondary">普通</Badge></div><div><strong className="text-sm">查询申报数据采集进度</strong><p className="mt-2 text-xs leading-5 text-muted-foreground">深圳海纳科技有限公司 · 李经理</p></div><div className="grid grid-cols-2 gap-3">{[['问题对象','TASK-8821'],['环境','生产环境']].map(([label,value])=><div className="rounded-lg bg-background/80 p-3" key={label}><span className="text-[11px] text-muted-foreground">{label}</span><p className="mt-1 text-xs font-medium">{value}</p></div>)}</div></CardContent></Card><Card><CardHeader><div className="flex items-center gap-2 text-xs font-semibold text-primary"><Sparkles /> AI 建单辅助</div><CardTitle className="text-sm">已识别到任务编号</CardTitle><CardDescription>TASK-8821 可以在创建工单后直接查询采集进度。</CardDescription></CardHeader><CardContent><Button className="w-full" variant="outline"><Search data-icon="inline-start" /> 预先查询任务</Button></CardContent></Card></div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export function TicketDetailConcept() {
|
||||
return (
|
||||
<main className="ticket-screen min-h-screen px-4 py-5 text-foreground sm:px-6 lg:px-8"><div className="mx-auto flex max-w-[1440px] flex-col gap-5">
|
||||
<ScreenNav current="核心页面 · 工单详情" otherPath="/concept/ticket-create" otherLabel="查看工单填写" />
|
||||
<header className="grid gap-4 lg:grid-cols-[1fr_auto] lg:items-end"><div><div className="mb-3 flex flex-wrap items-center gap-2"><Badge variant="secondary">业务问题</Badge><Badge variant="destructive">待回复</Badge><span className="text-xs text-muted-foreground">T202607130042 · 创建于 09:52</span></div><h1 className="text-3xl font-semibold tracking-[-0.04em]">查询申报数据采集进度</h1><p className="mt-2 text-sm text-muted-foreground">深圳海纳科技有限公司 · TASK-8821 · 生产环境</p></div><div className="flex flex-wrap gap-2"><Button variant="outline"><Link2 data-icon="inline-start" /> 关联解决方案</Button><Button><Send data-icon="inline-start" /> 回复客户</Button></div></header>
|
||||
<section className="ticket-status-strip grid overflow-hidden rounded-xl border border-border bg-card sm:grid-cols-4">{[
|
||||
{label:'已创建',time:'09:52',done:true},{label:'处理中',time:'10:02',done:true},{label:'等待回复客户',time:'当前',done:true},{label:'客户确认 / 关闭',time:'待完成',done:false},
|
||||
].map((item,index)=><div className="flex items-center gap-3 border-b border-border p-4 last:border-0 sm:border-b-0 sm:border-r" key={item.label}><span className={item.done?'ticket-status-dot ticket-status-done':'ticket-status-dot'}>{item.done?<Check />:index+1}</span><span><strong className="block text-xs">{item.label}</strong><span className="text-[11px] text-muted-foreground">{item.time}</span></span></div>)}</section>
|
||||
<div className="grid gap-5 xl:grid-cols-[minmax(0,1fr)_340px]"><div className="grid gap-5">
|
||||
<Card><CardHeader><CardTitle>问题概览</CardTitle><CardDescription>客户描述与当前查询结果</CardDescription></CardHeader><CardContent className="grid gap-5"><div className="grid gap-3 sm:grid-cols-3">{[['问题对象','TASK-8821'],['影响范围','单个客户'],['优先级','普通']].map(([label,value])=><div className="rounded-lg bg-muted p-3" key={label}><span className="text-[11px] text-muted-foreground">{label}</span><p className="mt-1 text-sm font-medium">{value}</p></div>)}</div><div><span className="text-xs font-medium">问题表现</span><p className="mt-2 text-sm leading-6">客户反馈任务从今天早上开始一直显示“采集中”,长时间没有进度变化,希望确认目前处理到哪一步。</p></div><div className="ticket-result rounded-xl border border-primary/20 p-4"><div className="flex items-center gap-2 text-xs font-semibold text-primary"><Bot /> 采集任务查询结果 · 10:16</div><p className="mt-3 text-sm leading-6">任务仍在执行,上游税局接口最近一次返回超时;最后有效更新时间为 10:14,当前无需人工重启。</p></div></CardContent></Card>
|
||||
<Card><CardHeader><CardTitle>处理过程</CardTitle><CardDescription>客户沟通、内部处理和系统查询按时间记录</CardDescription></CardHeader><CardContent><Tabs defaultValue="timeline"><TabsList variant="line"><TabsTrigger value="timeline">时间线</TabsTrigger><TabsTrigger value="comments">内部评论</TabsTrigger><TabsTrigger value="attachments">附件 2</TabsTrigger></TabsList><TabsContent className="pt-5" value="timeline"><div className="ticket-timeline">{[
|
||||
{icon:UserRound,who:'李经理 · 客户',time:'09:52',title:'提交客户问题',text:'TASK-8821 一直显示采集中,麻烦确认一下当前进度。'},
|
||||
{icon:Search,who:'王工 · 客服',time:'10:02',title:'开始处理并查询任务',text:'已确认任务编号和生产环境,发起采集进度查询。'},
|
||||
{icon:Bot,who:'采集任务查询服务',time:'10:16',title:'返回查询结果',text:'任务运行中,上游接口最近一次返回超时。'},
|
||||
{icon:MessageSquareText,who:'系统建议',time:'10:18',title:'生成客户回复草稿',text:'建议说明上游接口状态,并承诺继续关注任务进度。'},
|
||||
].map((event)=><div className="ticket-event" key={`${event.time}-${event.title}`}><span className="ticket-event-icon"><event.icon /></span><div><div className="flex flex-wrap items-center gap-2"><strong className="text-sm">{event.title}</strong><span className="text-xs text-muted-foreground">{event.who} · {event.time}</span></div><p className="mt-2 text-sm leading-6 text-muted-foreground">{event.text}</p></div></div>)}</div></TabsContent><TabsContent value="comments"><p className="py-8 text-center text-sm text-muted-foreground">暂无内部评论</p></TabsContent><TabsContent value="attachments"><p className="py-8 text-center text-sm text-muted-foreground">客户截图.png · 任务详情.pdf</p></TabsContent></Tabs></CardContent></Card>
|
||||
<Card className="ticket-reply-card"><CardHeader><CardTitle>回复客户</CardTitle><CardDescription>内容会作为处理记录保存,并同步更新客户沟通状态。</CardDescription></CardHeader><CardContent><Textarea className="min-h-28 bg-background" defaultValue="您好,已确认 TASK-8821 仍在采集中。目前上游税局接口响应较慢,我们会继续关注任务进度,恢复后第一时间同步给您。" /></CardContent><CardFooter className="justify-between"><Button variant="ghost"><Sparkles data-icon="inline-start" /> 优化措辞</Button><div className="flex gap-2"><Button variant="outline">仅保存记录</Button><Button><Send data-icon="inline-start" /> 回复并继续跟进</Button></div></CardFooter></Card>
|
||||
</div><aside className="grid h-fit gap-4 xl:sticky xl:top-5">
|
||||
<Card><CardHeader><CardTitle className="flex items-center gap-2 text-sm"><Building2 /> 客户信息</CardTitle></CardHeader><CardContent className="flex flex-col gap-3"><strong className="text-sm">深圳海纳科技有限公司</strong><div className="flex items-center gap-2 text-xs text-muted-foreground"><UserRound /> 李经理 · 财务负责人</div><Badge className="w-fit" variant="secondary">重点客户</Badge><Button variant="outline" size="sm">查看该客户历史工单</Button></CardContent></Card>
|
||||
<Card className="ticket-solution-card"><CardHeader><div className="flex items-center gap-2 text-xs font-semibold text-primary"><Lightbulb /> 推荐解决方案</div><CardTitle className="text-sm">采集任务长时间无进度的排查步骤</CardTitle><CardDescription>匹配度 93% · 已复用 28 次</CardDescription></CardHeader><CardContent><p className="text-xs leading-5 text-muted-foreground">先检查任务状态、上游接口响应和最近一次有效日志,再判断是否需要人工重试。</p></CardContent><CardFooter className="flex-col gap-2"><Button className="w-full"><Link2 data-icon="inline-start" /> 关联到当前工单</Button><Button className="w-full" variant="ghost">查看完整方案 <ChevronRight data-icon="inline-end" /></Button></CardFooter></Card>
|
||||
<Card><CardHeader><CardTitle className="flex items-center gap-2 text-sm"><History /> 下一步</CardTitle></CardHeader><CardContent className="flex flex-col gap-2">{['回复客户当前进度','30 分钟后再次查询','任务恢复后填写解决结果'].map((item,index)=><div className="flex items-center gap-3 rounded-lg border border-border p-3 text-xs" key={item}><span className="flex size-5 items-center justify-center rounded bg-secondary">{index+1}</span>{item}</div>)}</CardContent></Card>
|
||||
</aside></div>
|
||||
</div></main>
|
||||
)
|
||||
}
|
||||
171
frontend/src/features/workbench/WorkbenchConceptGallery.tsx
Normal file
171
frontend/src/features/workbench/WorkbenchConceptGallery.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
import * as React from 'react'
|
||||
import {
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
Bot,
|
||||
Building2,
|
||||
Check,
|
||||
ChevronRight,
|
||||
CircleUserRound,
|
||||
Clock3,
|
||||
FileText,
|
||||
Filter,
|
||||
Headphones,
|
||||
History,
|
||||
LayoutGrid,
|
||||
Lightbulb,
|
||||
Link2,
|
||||
MessageSquareText,
|
||||
Palette,
|
||||
Plus,
|
||||
Search,
|
||||
Send,
|
||||
Sparkles,
|
||||
Tags,
|
||||
UserRound,
|
||||
} from 'lucide-react'
|
||||
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { TicketCreateConcept, TicketDetailConcept } from './TicketScreenConcepts'
|
||||
import { ComponentStyleLab } from './ComponentStyleLab'
|
||||
|
||||
const tickets = [
|
||||
{ no: 'T202607130042', title: '查询申报数据采集进度', customer: '深圳海纳科技有限公司', object: 'TASK-8821', wait: '等待 24 分钟', status: '待回复', category: '业务问题' },
|
||||
{ no: 'T202607130039', title: '税局采集任务停留在初始化阶段', customer: '广州远望供应链有限公司', object: 'TASK-8794', wait: '处理 46 分钟', status: '处理中', category: '数据问题' },
|
||||
{ no: 'T202607130031', title: '财务人员登录生产环境失败', customer: '杭州云帆制造有限公司', object: '账号 zhangqi', wait: '等待 1 小时', status: '待回复', category: '操作问题' },
|
||||
{ no: 'T202607130028', title: '确认本期申报状态是否成功', customer: '北京启明企业服务有限公司', object: '2026 年 6 月属期', wait: '处理 2 小时', status: '待确认', category: '业务问题' },
|
||||
]
|
||||
|
||||
const solutions = [
|
||||
{ title: '采集任务长时间无进度的排查步骤', score: '93%', source: '客户支持知识库', used: '已复用 28 次' },
|
||||
{ title: '税局接口波动时的客户回复模板', score: '87%', source: '解决方案库', used: '已复用 41 次' },
|
||||
{ title: '采集任务初始化失败处理指引', score: '82%', source: '历史工单', used: '已复用 16 次' },
|
||||
]
|
||||
|
||||
function go(path: string) { window.location.hash = path }
|
||||
|
||||
function ConceptNav({ label }: { label: string }) {
|
||||
return (
|
||||
<div className="flex items-center justify-between border-b border-border pb-4">
|
||||
<Button variant="ghost" size="sm" onClick={() => go('/concepts')}><ArrowLeft data-icon="inline-start" /> 三套设计方案</Button>
|
||||
<Badge variant="secondary">{label}</Badge>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function ConceptPreviewRouter() {
|
||||
const [route, setRoute] = React.useState(() => window.location.hash.replace(/^#/, '') || '/concepts')
|
||||
React.useEffect(() => {
|
||||
const update = () => setRoute(window.location.hash.replace(/^#/, '') || '/concepts')
|
||||
window.addEventListener('hashchange', update)
|
||||
return () => window.removeEventListener('hashchange', update)
|
||||
}, [])
|
||||
if (route === '/concept') return <SupportDeskConcept />
|
||||
if (route === '/concept/customer-case') return <CustomerCaseConcept />
|
||||
if (route === '/concept/knowledge-assist') return <KnowledgeAssistConcept />
|
||||
if (route === '/concept/ticket-create') return <TicketCreateConcept />
|
||||
if (route === '/concept/ticket-detail') return <TicketDetailConcept />
|
||||
if (route === '/concept/components') return <ComponentStyleLab />
|
||||
return <ConceptGallery />
|
||||
}
|
||||
|
||||
export function ConceptGallery() {
|
||||
const concepts = [
|
||||
{ key: 'A', title: '客服处理工作台', desc: '以我的待办、待回复和即将超时为中心,适合客服高频批量处理。', path: '/concept', className: 'support', tags: ['效率优先', '列表清晰', '推荐'] },
|
||||
{ key: 'B', title: '客户问题工作区', desc: '把客户资料、问题描述、沟通记录和处理动作放在同一视野。', path: '/concept/customer-case', className: 'case', tags: ['上下文完整', '单案处理', '沟通导向'] },
|
||||
{ key: 'C', title: '知识辅助工作台', desc: '突出相似工单、解决方案复用和 AI 客户回复辅助。', path: '/concept/knowledge-assist', className: 'knowledge', tags: ['知识复用', 'AI 辅助', '方案沉淀'] },
|
||||
]
|
||||
return (
|
||||
<main className="support-gallery min-h-screen px-4 py-8 text-foreground sm:px-6 lg:px-8 lg:py-12">
|
||||
<div className="mx-auto flex max-w-7xl flex-col gap-8">
|
||||
<header className="border-b border-border pb-7">
|
||||
<div className="mb-3 flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.18em] text-primary"><Headphones /> Customer support workspace</div>
|
||||
<h1 className="text-4xl font-semibold tracking-[-0.05em] sm:text-5xl">客户问题工单 · 三套设计方向</h1>
|
||||
<p className="mt-4 max-w-3xl text-sm leading-6 text-muted-foreground">围绕 B 端客户问题记录、业务查询、客户回复和解决方案复用设计,不再使用运维监控大盘表达。</p>
|
||||
</header>
|
||||
<section className="grid gap-5 lg:grid-cols-3">
|
||||
{concepts.map((item) => (
|
||||
<Card className="group gap-0 overflow-hidden py-0 transition-transform duration-300 hover:-translate-y-1" key={item.key}>
|
||||
<button className="text-left outline-none focus-visible:ring-2 focus-visible:ring-ring" onClick={() => go(item.path)} type="button">
|
||||
<div className={`support-preview support-preview-${item.className} m-2 h-52 rounded-lg border border-border p-4`}>
|
||||
<div className="flex items-center justify-between text-[10px] font-semibold tracking-[0.16em]"><span>CONCEPT {item.key}</span><span className="size-2 rounded-full bg-current" /></div>
|
||||
<div className="mt-5 h-4 w-3/5 rounded bg-current opacity-80" /><div className="mt-2 h-2 w-2/5 rounded bg-current opacity-25" />
|
||||
<div className="mt-5 grid grid-cols-[0.8fr_1.2fr] gap-2"><div className="h-24 rounded border border-current/15 bg-current/10" /><div className="grid gap-2"><div className="rounded border border-current/15 bg-current/10" /><div className="rounded border border-current/15 bg-current/10" /><div className="rounded border border-current/15 bg-current/10" /></div></div>
|
||||
</div>
|
||||
<CardHeader className="px-5 pb-3 pt-4"><CardTitle className="flex items-center justify-between text-lg">{item.title}<ArrowRight className="transition-transform group-hover:translate-x-1" /></CardTitle><CardDescription className="min-h-10 leading-5">{item.desc}</CardDescription></CardHeader>
|
||||
<CardContent className="flex flex-wrap gap-2 px-5 pb-5">{item.tags.map((tag) => <Badge key={tag} variant="secondary">{tag}</Badge>)}</CardContent>
|
||||
</button>
|
||||
</Card>
|
||||
))}
|
||||
</section>
|
||||
<section className="grid gap-4 rounded-xl border border-border bg-card p-5 md:grid-cols-[1fr_auto_auto] md:items-center">
|
||||
<div><h2 className="font-semibold">查看核心业务页面</h2><p className="mt-1 text-sm text-muted-foreground">工作台之外,继续查看完整的工单填写与工单详情设计。</p></div>
|
||||
<Button variant="outline" onClick={() => go('/concept/ticket-create')}>工单填写 <ArrowRight data-icon="inline-end" /></Button>
|
||||
<Button onClick={() => go('/concept/ticket-detail')}>工单详情 <ArrowRight data-icon="inline-end" /></Button>
|
||||
</section>
|
||||
<Button className="self-start" variant="outline" onClick={() => go('/concept/components')}><Palette data-icon="inline-start" /> 查看组件风格实验室</Button>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export function SupportDeskConcept() {
|
||||
return (
|
||||
<main className="support-desk min-h-screen px-4 py-5 text-foreground sm:px-6 lg:px-8">
|
||||
<div className="mx-auto flex max-w-[1440px] flex-col gap-5">
|
||||
<ConceptNav label="A · 客服处理工作台" />
|
||||
<header className="flex flex-col gap-4 md:flex-row md:items-end md:justify-between">
|
||||
<div><p className="text-xs font-medium text-muted-foreground">星期一 · 7 月 13 日</p><h1 className="mt-1 text-3xl font-semibold tracking-[-0.04em]">我的客服工作台</h1><p className="mt-2 text-sm text-muted-foreground">优先回复等待中的客户,再处理新进入的问题。</p></div>
|
||||
<Button><Plus data-icon="inline-start" /> 记录客户问题</Button>
|
||||
</header>
|
||||
<section className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
||||
{[['待我处理','12','其中 5 项待回复'],['即将超时','3','最短剩余 18 分钟'],['今日已解决','18','业务问题占 72%'],['待客户确认','6','已发送处理结果']].map(([label,value,note], i) => (
|
||||
<Card className={i === 0 ? 'support-focus-card' : ''} key={label}><CardContent><div className="flex items-center justify-between text-xs text-muted-foreground"><span>{label}</span>{i === 0 ? <CircleUserRound /> : <Clock3 />}</div><strong className="mt-4 block text-3xl tracking-[-0.04em]">{value}</strong><p className="mt-1 text-xs text-muted-foreground">{note}</p></CardContent></Card>
|
||||
))}
|
||||
</section>
|
||||
<div className="grid gap-5 xl:grid-cols-[220px_minmax(0,1fr)]">
|
||||
<Card className="h-fit"><CardHeader><CardTitle className="text-sm">处理视图</CardTitle></CardHeader><CardContent className="flex flex-col gap-1">{[['我的待办','12'],['待回复客户','5'],['业务问题','9'],['数据问题','4'],['待客户确认','6']].map(([label,count],i)=><Button className="justify-between" key={label} variant={i===0?'secondary':'ghost'}>{label}<Badge variant="secondary">{count}</Badge></Button>)}</CardContent></Card>
|
||||
<Card className="gap-0 py-0"><CardHeader className="border-b border-border px-5 py-4"><div><CardTitle>优先处理</CardTitle><CardDescription>按等待客户回复时间和承诺时限排序</CardDescription></div><div className="mt-3 flex gap-2"><div className="relative flex-1"><Search className="absolute left-3 top-2.5 text-muted-foreground" /><Input className="pl-9" placeholder="搜索客户、问题、任务编号" /></div><Button variant="outline" size="icon"><Filter /></Button></div></CardHeader><CardContent className="px-0">{tickets.map((ticket,i)=><button className="grid w-full grid-cols-[minmax(0,1fr)_auto] gap-4 border-b border-border px-5 py-4 text-left transition-colors last:border-0 hover:bg-muted/60" key={ticket.no} type="button"><span className="min-w-0"><span className="flex flex-wrap items-center gap-2"><strong className="truncate text-sm">{ticket.title}</strong><Badge variant={i===0?'destructive':'secondary'}>{ticket.status}</Badge><Badge variant="outline">{ticket.category}</Badge></span><span className="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground"><span><Building2 /> {ticket.customer}</span><span>{ticket.object}</span><span>{ticket.no}</span></span></span><span className="flex items-center gap-3 text-xs text-muted-foreground"><span>{ticket.wait}</span><ChevronRight /></span></button>)}</CardContent></Card>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export function CustomerCaseConcept() {
|
||||
return (
|
||||
<main className="customer-case min-h-screen px-4 py-5 text-foreground sm:px-6 lg:px-8">
|
||||
<div className="mx-auto flex max-w-[1500px] flex-col gap-4">
|
||||
<ConceptNav label="B · 客户问题工作区" />
|
||||
<header className="flex items-center justify-between"><div><h1 className="text-2xl font-semibold tracking-[-0.03em]">客户问题工作区</h1><p className="mt-1 text-xs text-muted-foreground">当前处理 · 4 项</p></div><Button><Plus data-icon="inline-start" /> 新建工单</Button></header>
|
||||
<div className="grid gap-4 xl:grid-cols-[300px_minmax(0,1fr)_330px]">
|
||||
<Card className="gap-0 py-0"><CardHeader className="border-b border-border p-4"><div className="relative"><Search className="absolute left-3 top-2.5 text-muted-foreground" /><Input className="pl-9" placeholder="搜索当前问题" /></div></CardHeader><CardContent className="px-0">{tickets.slice(0,3).map((t,i)=><button className={`w-full border-b border-border p-4 text-left ${i===0?'case-selected':'hover:bg-muted/60'}`} key={t.no} type="button"><div className="flex items-start justify-between gap-2"><Badge variant={i===0?'destructive':'secondary'}>{t.status}</Badge><span className="text-[11px] text-muted-foreground">{t.wait}</span></div><strong className="mt-3 block text-sm leading-5">{t.title}</strong><span className="mt-2 block truncate text-xs text-muted-foreground">{t.customer}</span><span className="mt-1 block text-[11px] text-muted-foreground">{t.object}</span></button>)}</CardContent></Card>
|
||||
<div className="grid gap-4">
|
||||
<Card><CardHeader><div className="flex flex-wrap items-center gap-2"><Badge variant="destructive">待回复</Badge><Badge variant="outline">业务问题</Badge><span className="text-xs text-muted-foreground">T202607130042</span></div><CardTitle className="pt-2 text-xl">查询申报数据采集进度</CardTitle><CardDescription>客户反馈任务提交后已等待较长时间,希望确认当前处理阶段。</CardDescription></CardHeader><CardContent className="grid gap-3 sm:grid-cols-3">{[['问题对象','TASK-8821'],['环境','生产环境'],['影响范围','单个客户']].map(([l,v])=><div className="rounded-lg bg-muted p-3" key={l}><span className="text-[11px] text-muted-foreground">{l}</span><p className="mt-1 text-sm font-medium">{v}</p></div>)}</CardContent></Card>
|
||||
<Card><CardHeader><CardTitle className="flex items-center gap-2 text-sm"><MessageSquareText /> 沟通与处理记录</CardTitle></CardHeader><CardContent className="flex flex-col gap-5"><div className="case-message"><span className="case-avatar"><UserRound /></span><div><div className="flex items-center gap-2 text-xs"><strong>李经理 · 客户</strong><span className="text-muted-foreground">09:52</span></div><p className="mt-2 rounded-r-xl rounded-bl-xl bg-muted p-3 text-sm leading-6">TASK-8821 从早上开始一直显示“采集中”,麻烦帮忙确认一下现在到哪一步了?</p></div></div><div className="case-message"><span className="case-avatar case-avatar-system"><Bot /></span><div><div className="flex items-center gap-2 text-xs"><strong>系统查询结果</strong><span className="text-muted-foreground">10:16</span></div><p className="mt-2 rounded-r-xl rounded-bl-xl border border-primary/20 bg-primary/5 p-3 text-sm leading-6">任务仍在执行,上游税局接口最近一次返回超时;最后有效更新时间为 10:14。</p></div></div><div className="rounded-xl border border-border p-3"><textarea className="min-h-20 w-full resize-none bg-transparent text-sm outline-none" defaultValue="您好,已确认任务仍在采集中,目前上游税局接口响应较慢,我们会继续关注任务进度并及时同步给您。" /><div className="mt-3 flex items-center justify-between"><Button variant="ghost" size="sm"><Sparkles data-icon="inline-start" /> 优化回复</Button><Button size="sm"><Send data-icon="inline-start" /> 回复客户</Button></div></div></CardContent></Card>
|
||||
</div>
|
||||
<div className="grid content-start gap-4"><Card><CardHeader><CardTitle className="flex items-center gap-2 text-sm"><Building2 /> 客户信息</CardTitle></CardHeader><CardContent className="flex flex-col gap-3 text-sm"><strong>深圳海纳科技有限公司</strong><span className="text-xs text-muted-foreground">联系人:李经理 · 财务负责人</span><span className="text-xs text-muted-foreground">客户等级:重点客户</span><Button variant="outline" size="sm">查看历史问题</Button></CardContent></Card><Card><CardHeader><CardTitle className="flex items-center gap-2 text-sm"><Lightbulb /> 建议下一步</CardTitle></CardHeader><CardContent className="flex flex-col gap-2">{['回复客户当前进度','30 分钟后再次查询','进度恢复后关闭工单'].map((x,i)=><button className="flex items-center gap-3 rounded-lg border border-border p-3 text-left text-xs hover:bg-muted" key={x} type="button"><span className="flex size-5 items-center justify-center rounded bg-secondary">{i+1}</span>{x}</button>)}</CardContent></Card></div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export function KnowledgeAssistConcept() {
|
||||
return (
|
||||
<main className="knowledge-assist min-h-screen px-4 py-5 text-foreground sm:px-6 lg:px-8">
|
||||
<div className="mx-auto flex max-w-[1440px] flex-col gap-5">
|
||||
<ConceptNav label="C · 知识辅助工作台" />
|
||||
<header className="grid gap-4 lg:grid-cols-[1fr_auto] lg:items-end"><div><div className="mb-2 flex items-center gap-2 text-xs font-semibold text-primary"><Sparkles /> KNOWLEDGE ASSISTED SUPPORT</div><h1 className="text-3xl font-semibold tracking-[-0.04em]">先找到答案,再回复客户</h1><p className="mt-2 text-sm text-muted-foreground">从历史工单、解决方案和知识库中定位最合适的处理方式。</p></div><Button variant="outline" onClick={()=>go('/concepts')}><LayoutGrid data-icon="inline-start" /> 切换设计</Button></header>
|
||||
<Card className="knowledge-query"><CardContent className="grid gap-3 sm:grid-cols-[1fr_auto]"><div className="relative"><Search className="absolute left-4 top-3.5 text-primary" /><Input className="h-11 bg-background pl-11 text-sm" defaultValue="客户 TASK-8821 采集进度长时间未更新,如何处理?" /></div><Button size="lg"><Sparkles data-icon="inline-start" /> 分析客户问题</Button></CardContent></Card>
|
||||
<div className="grid gap-5 xl:grid-cols-[minmax(0,1.1fr)_minmax(360px,0.9fr)]">
|
||||
<div className="grid gap-5"><Card><CardHeader><div className="flex items-center justify-between"><div><CardTitle>推荐处理依据</CardTitle><CardDescription>已检索 38 条内容,以下 3 条匹配度最高</CardDescription></div><Badge variant="secondary">耗时 0.8 秒</Badge></div></CardHeader><CardContent className="flex flex-col gap-3">{solutions.map((s,i)=><button className="group grid grid-cols-[auto_1fr_auto] items-start gap-3 rounded-xl border border-border p-4 text-left transition-all hover:border-primary/35 hover:shadow-sm" key={s.title} type="button"><span className="flex size-8 items-center justify-center rounded-lg bg-primary/10 text-primary">{i===0?<FileText />:i===1?<MessageSquareText />:<History />}</span><span><strong className="text-sm">{s.title}</strong><span className="mt-2 flex flex-wrap gap-2 text-xs text-muted-foreground"><span>{s.source}</span><span>·</span><span>{s.used}</span></span></span><Badge variant={i===0?'default':'secondary'}>{s.score}</Badge></button>)}</CardContent></Card><Card><CardHeader><CardTitle className="flex items-center gap-2"><Link2 /> 相似历史工单</CardTitle></CardHeader><CardContent className="grid gap-3 sm:grid-cols-2">{[['T202607030005','税局采集任务停留在初始化阶段','确认税局接口波动,任务恢复后回复客户并关闭。'],['T202607010002','查询申报数据采集进度','查询任务日志后确认仍在采集中。']].map(([no,t,r])=><div className="rounded-xl border border-border p-4" key={no}><span className="text-xs text-muted-foreground">{no}</span><strong className="mt-2 block text-sm">{t}</strong><p className="mt-3 text-xs leading-5 text-muted-foreground">处理结果:{r}</p></div>)}</CardContent></Card></div>
|
||||
<Card className="knowledge-answer"><CardHeader><div className="flex items-center gap-2 text-xs font-semibold text-primary"><Bot /> AI 辅助处理草稿</div><CardTitle>建议处理方式</CardTitle><CardDescription>基于查询结果和已发布解决方案生成,请确认后使用。</CardDescription></CardHeader><CardContent className="flex flex-col gap-4"><div><span className="text-xs font-medium">排查步骤</span><ol className="mt-3 flex flex-col gap-3">{['查询 TASK-8821 当前状态和最近更新时间','检查最近一次上游接口响应与有效日志','若任务仍在运行,向客户说明并持续关注'].map((x,i)=><li className="flex gap-3 text-sm leading-5" key={x}><span className="flex size-5 shrink-0 items-center justify-center rounded-full bg-primary text-[10px] text-primary-foreground">{i+1}</span>{x}</li>)}</ol></div><div className="border-t border-border pt-4"><div className="flex items-center justify-between"><span className="text-xs font-medium">客户回复草稿</span><Badge variant="secondary">可编辑</Badge></div><p className="mt-3 rounded-xl bg-background p-4 text-sm leading-6">您好,已确认任务仍在采集中,目前上游税局接口响应较慢。我们会继续关注任务进度,恢复后第一时间同步给您。</p></div><div className="grid grid-cols-2 gap-2"><Button variant="outline"><Tags data-icon="inline-start" /> 关联方案</Button><Button><Check data-icon="inline-start" /> 采用并回复</Button></div><Button className="w-full" variant="ghost"><FileText data-icon="inline-start" /> 保存为解决方案草稿</Button></CardContent></Card>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
176
frontend/src/features/workbench/WorkbenchConceptPage.tsx
Normal file
176
frontend/src/features/workbench/WorkbenchConceptPage.tsx
Normal file
@@ -0,0 +1,176 @@
|
||||
import {
|
||||
Activity,
|
||||
ArrowUpRight,
|
||||
Bot,
|
||||
CheckCircle2,
|
||||
ChevronRight,
|
||||
CircleDot,
|
||||
Clock3,
|
||||
Plus,
|
||||
Radar,
|
||||
ShieldAlert,
|
||||
Sparkles,
|
||||
Users,
|
||||
} from 'lucide-react'
|
||||
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Card,
|
||||
CardAction,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
|
||||
const queue = [
|
||||
{ no: 'TK-20260713-042', title: '核心结算任务连续失败', team: '资金清算组', age: '12 分钟', tone: '紧急' },
|
||||
{ no: 'TK-20260713-039', title: '企业授信报告无法生成', team: '风险运营组', age: '28 分钟', tone: '高优先' },
|
||||
{ no: 'TK-20260713-031', title: '客户经理账号权限异常', team: '机构服务组', age: '46 分钟', tone: '处理中' },
|
||||
]
|
||||
|
||||
const pulse = [18, 24, 22, 31, 27, 44, 38, 52, 43, 61, 49, 56]
|
||||
|
||||
export function WorkbenchConceptPage() {
|
||||
return (
|
||||
<main className="concept-shell min-h-screen px-4 py-5 text-foreground sm:px-6 lg:px-8 lg:py-7">
|
||||
<div className="mx-auto flex max-w-7xl flex-col gap-5">
|
||||
<header className="flex flex-col gap-4 border-b border-border/70 pb-5 md:flex-row md:items-end md:justify-between">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.18em] text-muted-foreground">
|
||||
<Radar className="text-primary" />
|
||||
Service signal · 07 / 13
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="concept-title text-3xl font-semibold tracking-[-0.04em] sm:text-4xl">今日服务态势</h1>
|
||||
<p className="mt-2 max-w-2xl text-sm leading-6 text-muted-foreground">
|
||||
早上好,王工。当前服务运行平稳,但有 2 项风险需要在下一小时内处理。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="outline">查看值班表</Button>
|
||||
<Button onClick={() => (window.location.hash = '/tickets/new')}>
|
||||
<Plus data-icon="inline-start" />
|
||||
创建工单
|
||||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section className="concept-signal-grid grid overflow-hidden rounded-xl border border-border bg-card shadow-sm lg:grid-cols-[1.35fr_repeat(3,0.65fr)]">
|
||||
<div className="concept-hero flex min-h-48 flex-col justify-between gap-6 p-5 sm:p-6">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="text-xs font-medium text-primary-foreground/70">实时服务健康度</span>
|
||||
<div className="flex items-baseline gap-3">
|
||||
<strong className="concept-score text-6xl font-semibold tracking-[-0.08em] text-primary-foreground">92</strong>
|
||||
<span className="text-sm text-primary-foreground/70">/ 100</span>
|
||||
</div>
|
||||
</div>
|
||||
<Badge className="border-primary-foreground/20 bg-primary-foreground/10 text-primary-foreground">运行平稳</Badge>
|
||||
</div>
|
||||
<div className="flex h-11 items-end gap-1" aria-label="最近十二小时工单趋势">
|
||||
{pulse.map((height, index) => (
|
||||
<span
|
||||
className="min-w-2 flex-1 rounded-t-sm bg-primary-foreground/20 transition-colors hover:bg-primary-foreground/50"
|
||||
key={`${height}-${index}`}
|
||||
style={{ height: `${height}%` }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-xs text-primary-foreground/70">
|
||||
<span>过去 12 小时</span>
|
||||
<span className="flex items-center gap-1 text-primary-foreground"><Activity /> 较昨日改善 8%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{[
|
||||
{ icon: Clock3, label: '待响应', value: '08', note: '3 项临近 SLA' },
|
||||
{ icon: Users, label: '处理中', value: '23', note: '7 个支持小组' },
|
||||
{ icon: CheckCircle2, label: '今日解决', value: '47', note: '平均 38 分钟' },
|
||||
].map((item) => (
|
||||
<div className="flex min-h-36 flex-col justify-between border-t border-border p-5 lg:min-h-48 lg:border-l lg:border-t-0" key={item.label}>
|
||||
<div className="flex items-center justify-between text-muted-foreground">
|
||||
<span className="text-xs font-medium">{item.label}</span>
|
||||
<item.icon />
|
||||
</div>
|
||||
<div>
|
||||
<strong className="concept-stat text-4xl font-semibold tracking-[-0.05em]">{item.value}</strong>
|
||||
<p className="mt-2 text-xs text-muted-foreground">{item.note}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<div className="grid gap-5 xl:grid-cols-[minmax(0,1.35fr)_minmax(320px,0.65fr)]">
|
||||
<Card className="gap-0 py-0">
|
||||
<CardHeader className="border-b border-border px-5 py-4">
|
||||
<div>
|
||||
<CardTitle>优先处置队列</CardTitle>
|
||||
<CardDescription>按业务影响与 SLA 风险动态排序</CardDescription>
|
||||
</div>
|
||||
<CardAction>
|
||||
<Button variant="ghost" size="sm" onClick={() => (window.location.hash = '/tickets')}>
|
||||
全部工单 <ArrowUpRight data-icon="inline-end" />
|
||||
</Button>
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
<CardContent className="px-0">
|
||||
<div className="divide-y divide-border">
|
||||
{queue.map((item, index) => (
|
||||
<button
|
||||
className="group grid w-full grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-4 px-5 py-4 text-left outline-none transition-colors hover:bg-muted/60 focus-visible:bg-muted focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
|
||||
key={item.no}
|
||||
type="button"
|
||||
>
|
||||
<span className="concept-index flex size-8 items-center justify-center rounded-md border border-border bg-background text-xs font-semibold text-muted-foreground">
|
||||
{String(index + 1).padStart(2, '0')}
|
||||
</span>
|
||||
<span className="min-w-0">
|
||||
<span className="flex items-center gap-2">
|
||||
<strong className="truncate text-sm font-medium">{item.title}</strong>
|
||||
{index === 0 ? <CircleDot className="text-destructive" /> : null}
|
||||
</span>
|
||||
<span className="mt-1 block text-xs text-muted-foreground">{item.no} · {item.team}</span>
|
||||
</span>
|
||||
<span className="flex items-center gap-3">
|
||||
<span className="hidden text-xs text-muted-foreground sm:inline">{item.age}</span>
|
||||
<Badge variant={index === 0 ? 'destructive' : 'secondary'}>{item.tone}</Badge>
|
||||
<ChevronRight className="text-muted-foreground transition-transform group-hover:translate-x-0.5" />
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="concept-ai-card border-primary/20 bg-primary/[0.045]">
|
||||
<CardHeader>
|
||||
<div className="flex size-9 items-center justify-center rounded-lg bg-primary text-primary-foreground shadow-sm">
|
||||
<Bot />
|
||||
</div>
|
||||
<CardAction><Badge variant="secondary"><Sparkles /> AI 研判</Badge></CardAction>
|
||||
<CardTitle className="pt-3">建议优先排查结算任务</CardTitle>
|
||||
<CardDescription className="leading-5">关联日志中有 4 次相同的数据库连接池耗尽信号。</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-3">
|
||||
<div className="rounded-lg border border-border/70 bg-background/80 p-3">
|
||||
<div className="flex items-center gap-2 text-xs font-medium"><ShieldAlert className="text-warning" /> 影响判断</div>
|
||||
<p className="mt-2 text-xs leading-5 text-muted-foreground">可能影响 12 家机构的日终清算,建议 30 分钟内升级至二线。</p>
|
||||
</div>
|
||||
<Button className="w-full justify-between" variant="outline">
|
||||
查看诊断依据 <ArrowUpRight data-icon="inline-end" />
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<footer className="flex items-center justify-between border-t border-border/70 pt-4 text-xs text-muted-foreground">
|
||||
<span>数据更新于 10:42:18</span>
|
||||
<Button variant="ghost" size="xs" onClick={() => (window.location.hash = '/concepts')}>查看其他设计方向</Button>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user