feat: rebuild support workspace frontend
This commit is contained in:
@@ -11,6 +11,7 @@ import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { createTicket } from './api'
|
||||
import { TICKET_CATEGORIES } from './ticketCategories'
|
||||
@@ -60,9 +61,11 @@ export function TicketCreatePage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<AppPage maxWidth="6xl" contentClassName="grid gap-5">
|
||||
<AppPageHeader title="创建工单" />
|
||||
<form className="grid gap-4 xl:grid-cols-[minmax(0,1fr)_340px]" onSubmit={handleSubmit}>
|
||||
<AppPage
|
||||
contentClassName="grid gap-5"
|
||||
header={<AppPageHeader title="创建工单" description="按提出方、问题描述和影响范围记录完整上下文" actions={<><Button type="button" variant="ghost" onClick={() => (window.location.hash = '/tickets')}>取消</Button><Button type="button" variant="outline" onClick={() => toast('暂存入口为视觉预留,当前不会保存数据')}>暂存</Button></>} />}
|
||||
>
|
||||
<form id="ticket-create-form" className="grid gap-4 xl:grid-cols-[minmax(0,1fr)_320px]" onSubmit={handleSubmit}>
|
||||
<div className="grid gap-4">
|
||||
{error ? (
|
||||
<Alert variant="destructive">
|
||||
@@ -72,72 +75,7 @@ export function TicketCreatePage() {
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
<AppSection title="基础信息">
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<AppField label="问题分类" required>
|
||||
<Select
|
||||
value={form.category_code}
|
||||
onValueChange={(value) => updateField('category_code', value as TicketInput['category_code'])}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="请选择问题分类" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{TICKET_CATEGORIES.map((category) => (
|
||||
<SelectItem key={category.code} value={category.code}>
|
||||
{category.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</AppField>
|
||||
<AppField label="工单标题" htmlFor="ticket-title" required>
|
||||
<Input
|
||||
id="ticket-title"
|
||||
required
|
||||
placeholder="例如:查询客户采集进度"
|
||||
value={form.title}
|
||||
onChange={(event) => updateField('title', event.target.value)}
|
||||
/>
|
||||
</AppField>
|
||||
</div>
|
||||
</AppSection>
|
||||
|
||||
<AppSection title="问题信息">
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<AppField label="问题表现" htmlFor="ticket-problem-performance" required>
|
||||
<Input
|
||||
id="ticket-problem-performance"
|
||||
required
|
||||
placeholder="例如:采集进度长时间未更新"
|
||||
value={form.problem_performance}
|
||||
onChange={(event) => updateField('problem_performance', event.target.value)}
|
||||
/>
|
||||
</AppField>
|
||||
<AppField label="问题对象" htmlFor="ticket-problem-object" required>
|
||||
<Input
|
||||
id="ticket-problem-object"
|
||||
required
|
||||
placeholder="例如:客户名称、账号、机构或任务编号"
|
||||
value={form.problem_object}
|
||||
onChange={(event) => updateField('problem_object', event.target.value)}
|
||||
/>
|
||||
</AppField>
|
||||
</div>
|
||||
<AppField label="问题描述" htmlFor="ticket-description" required>
|
||||
<Textarea
|
||||
id="ticket-description"
|
||||
required
|
||||
placeholder="请填写问题背景、期望结果、已知线索或需要支持团队确认的内容。"
|
||||
value={form.description}
|
||||
onChange={(event) => updateField('description', event.target.value)}
|
||||
/>
|
||||
</AppField>
|
||||
</AppSection>
|
||||
|
||||
<AppSection title="提出信息">
|
||||
<AppSection variant="card" title="提出方与对象" description="先确认由谁提出,以及具体影响到哪个业务对象。">
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<AppField label="提出人" htmlFor="ticket-submitter-name" required>
|
||||
<Input
|
||||
@@ -157,26 +95,33 @@ export function TicketCreatePage() {
|
||||
onChange={(event) => updateField('submitter_org', event.target.value)}
|
||||
/>
|
||||
</AppField>
|
||||
<AppField label="问题对象" htmlFor="ticket-problem-object" required>
|
||||
<Input
|
||||
id="ticket-problem-object"
|
||||
required
|
||||
placeholder="客户、账号、机构或任务编号"
|
||||
value={form.problem_object}
|
||||
onChange={(event) => updateField('problem_object', event.target.value)}
|
||||
/>
|
||||
</AppField>
|
||||
<AppField label="环境" required>
|
||||
<Select value={form.environment} onValueChange={(value) => updateField('environment', value)}><SelectTrigger className="w-full"><SelectValue placeholder="请选择问题环境" /></SelectTrigger><SelectContent><SelectGroup>{ENVIRONMENT_OPTIONS.map((environment) => <SelectItem key={environment} value={environment}>{environment}</SelectItem>)}</SelectGroup></SelectContent></Select>
|
||||
</AppField>
|
||||
</div>
|
||||
</AppSection>
|
||||
|
||||
<AppSection title="影响与环境">
|
||||
<AppSection variant="card" title="问题描述" description="描述用户看到的现象、发生时间与期望结果。">
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<AppField label="优先级" required>
|
||||
<Select value={form.priority} onValueChange={(value) => updateField('priority', value as TicketInput['priority'])}>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="请选择优先级" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="low">低</SelectItem>
|
||||
<SelectItem value="medium">中</SelectItem>
|
||||
<SelectItem value="high">高</SelectItem>
|
||||
<SelectItem value="urgent">紧急</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</AppField>
|
||||
<AppField label="工单标题" htmlFor="ticket-title" required><Input id="ticket-title" required placeholder="例如:查询客户采集进度" value={form.title} onChange={(event) => updateField('title', event.target.value)} /></AppField>
|
||||
<AppField label="问题表现" htmlFor="ticket-problem-performance" required><Input id="ticket-problem-performance" required placeholder="例如:采集进度长时间未更新" value={form.problem_performance} onChange={(event) => updateField('problem_performance', event.target.value)} /></AppField>
|
||||
</div>
|
||||
<AppField label="问题描述" htmlFor="ticket-description" required><Textarea id="ticket-description" required placeholder="请填写问题背景、期望结果、已知线索或需要支持团队确认的内容。" value={form.description} onChange={(event) => updateField('description', event.target.value)} /></AppField>
|
||||
</AppSection>
|
||||
|
||||
<AppSection variant="card" title="分类与影响" description="用于确定处理队列、响应顺序和影响范围。">
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<AppField label="问题分类" required><Select value={form.category_code} onValueChange={(value) => updateField('category_code', value as TicketInput['category_code'])}><SelectTrigger className="w-full"><SelectValue placeholder="请选择问题分类" /></SelectTrigger><SelectContent><SelectGroup>{TICKET_CATEGORIES.map((category) => <SelectItem key={category.code} value={category.code}>{category.label}</SelectItem>)}</SelectGroup></SelectContent></Select></AppField>
|
||||
<AppField label="优先级" required><Select value={form.priority} onValueChange={(value) => updateField('priority', value as TicketInput['priority'])}><SelectTrigger className="w-full"><SelectValue placeholder="请选择优先级" /></SelectTrigger><SelectContent><SelectGroup><SelectItem value="low">低</SelectItem><SelectItem value="medium">中</SelectItem><SelectItem value="high">高</SelectItem><SelectItem value="urgent">紧急</SelectItem></SelectGroup></SelectContent></Select></AppField>
|
||||
<AppField label="影响范围" htmlFor="ticket-impact-scope">
|
||||
<Input
|
||||
id="ticket-impact-scope"
|
||||
@@ -195,41 +140,32 @@ export function TicketCreatePage() {
|
||||
onChange={(event) => updateField('related_system', event.target.value)}
|
||||
/>
|
||||
</AppField>
|
||||
<AppField label="环境" required>
|
||||
<Select value={form.environment} onValueChange={(value) => updateField('environment', value)}>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="请选择问题环境" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{ENVIRONMENT_OPTIONS.map((environment) => (
|
||||
<SelectItem key={environment} value={environment}>
|
||||
{environment}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</AppField>
|
||||
</div>
|
||||
</AppSection>
|
||||
|
||||
<AppSection title="提交确认">
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<div className="flex items-start gap-2 text-sm text-muted-foreground">
|
||||
<CheckCircle2 className="mt-0.5 size-4" />
|
||||
<span>请确认分类、对象和描述已足够支持处理人员判断下一步动作。</span>
|
||||
</div>
|
||||
<Button type="submit" disabled={submitting}>
|
||||
<Send data-icon="inline-start" />
|
||||
{submitting ? '提交中...' : '提交工单'}
|
||||
</Button>
|
||||
<AppSection
|
||||
variant="card"
|
||||
title="提交确认"
|
||||
description="提交后工单将进入对应的支持处理队列。"
|
||||
footer={
|
||||
<>
|
||||
<span className="text-xs text-muted-foreground">确认无误后提交</span>
|
||||
<Button className="hidden md:inline-flex" type="submit" disabled={submitting}>
|
||||
<Send data-icon="inline-start" />
|
||||
{submitting ? '提交中...' : '提交工单'}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className="flex items-start gap-2 text-sm text-muted-foreground">
|
||||
<CheckCircle2 className="mt-0.5 size-4" />
|
||||
<span>请确认分类、对象和描述已足够支持处理人员判断下一步动作。</span>
|
||||
</div>
|
||||
</AppSection>
|
||||
</div>
|
||||
|
||||
<div className="grid content-start gap-4">
|
||||
<AppSection title="建单提示">
|
||||
<AppSection variant="card" title="建单提示">
|
||||
<div className="grid gap-3 text-sm text-muted-foreground">
|
||||
<div className="flex gap-2">
|
||||
<Lightbulb className="mt-0.5 size-4" />
|
||||
@@ -246,16 +182,10 @@ export function TicketCreatePage() {
|
||||
</div>
|
||||
</AppSection>
|
||||
|
||||
<AppSection
|
||||
title="AI 解决方案"
|
||||
action={<Badge variant="ai">即将接入</Badge>}
|
||||
>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
后续阶段将由 AI 识别问题后自动回填,当前不作为人工必填项。
|
||||
</p>
|
||||
</AppSection>
|
||||
<AppSection variant="card" title="Agent 辅助" action={<Badge variant="ai">演示</Badge>}><p className="text-sm text-muted-foreground">右侧助手会根据当前页面字段提供排查结构,但不会提交或保存工单。</p></AppSection>
|
||||
</div>
|
||||
</form>
|
||||
<div className="sticky bottom-3 rounded-lg border bg-card/95 p-3 shadow-lg backdrop-blur md:hidden"><Button form="ticket-create-form" type="submit" className="w-full" disabled={submitting}><Send data-icon="inline-start" />{submitting ? '提交中...' : '提交工单'}</Button></div>
|
||||
</AppPage>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user