diff --git a/frontend/src/app/router.tsx b/frontend/src/app/router.tsx index 64bff69..ba3164e 100644 --- a/frontend/src/app/router.tsx +++ b/frontend/src/app/router.tsx @@ -5,9 +5,10 @@ import { SolutionListPage } from '@/features/solutions/SolutionListPage' import { TicketCreatePage } from '@/features/tickets/TicketCreatePage' import { TicketDetailPage } from '@/features/tickets/TicketDetailPage' import { TicketListPage } from '@/features/tickets/TicketListPage' +import { WorkbenchPage } from '@/features/workbench/WorkbenchPage' function getRoute() { - return window.location.hash.replace(/^#/, '') || '/tickets' + return window.location.hash.replace(/^#/, '') || '/' } export function Router() { @@ -23,6 +24,10 @@ export function Router() { return } + if (route === '/') { + return + } + if (route === '/solutions') { return } @@ -36,5 +41,5 @@ export function Router() { return } - return + return } diff --git a/frontend/src/features/design-system/DesignSystemPreview.tsx b/frontend/src/features/design-system/DesignSystemPreview.tsx index 81a2c31..a5a8cfb 100644 --- a/frontend/src/features/design-system/DesignSystemPreview.tsx +++ b/frontend/src/features/design-system/DesignSystemPreview.tsx @@ -1,9 +1,35 @@ +import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable' +import { AppDetailLayout } from '@/components/app/AppDetailLayout' +import { AppEmptyState } from '@/components/app/AppEmptyState' +import { AppFilterBar } from '@/components/app/AppFilterBar' import { AppPageHeader } from '@/components/app/AppPageHeader' import { AppPriorityBadge } from '@/components/app/AppPriorityBadge' +import { AppSection } from '@/components/app/AppSection' import { AppStatusBadge } from '@/components/app/AppStatusBadge' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader } from '@/components/ui/card' +import { Input } from '@/components/ui/input' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' + +type PreviewRow = { + id: number + name: string + status: string + priority: string +} + +const previewRows: PreviewRow[] = [ + { id: 1, name: '登录失败无法进入生产系统', status: 'in_progress', priority: 'urgent' }, + { id: 2, name: '报表导出结果缺少字段', status: 'pending', priority: 'medium' } +] + +const previewColumns: AppDataTableColumn[] = [ + { key: 'name', header: '问题', cell: (row) => row.name }, + { key: 'status', header: '状态', cell: (row) => }, + { key: 'priority', header: '优先级', cell: (row) => }, + { key: 'action', header: '操作', cell: () => 查看 } +] export function DesignSystemPreview() { return ( @@ -11,37 +37,71 @@ export function DesignSystemPreview() { 创建工单} /> - Buttons + 按钮 - Primary - Secondary - Ghost - Danger + 主要操作 + 次要操作 + 轻量操作 + 高风险操作 - Badges + 状态与优先级 - Default - Success - Warning - Danger - Info + 默认 + 成功 + 提醒 + 风险 + 信息 AI + + 重置筛选}> + + + + + + + 全部状态 + 待处理 + 处理中 + + + + + row.id} + emptyTitle="暂无数据" + emptyDescription="符合条件的数据会显示在这里。" + /> + + + + + } + > + + 页面级布局只复用 App 组件,业务字段由页面自行传入。 + + ) diff --git a/frontend/src/features/workbench/WorkbenchPage.tsx b/frontend/src/features/workbench/WorkbenchPage.tsx new file mode 100644 index 0000000..149f6ef --- /dev/null +++ b/frontend/src/features/workbench/WorkbenchPage.tsx @@ -0,0 +1,131 @@ +import * as React from 'react' +import { ArrowRight, Plus } from 'lucide-react' + +import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable' +import { AppEmptyState } from '@/components/app/AppEmptyState' +import { AppPageHeader } from '@/components/app/AppPageHeader' +import { AppPriorityBadge } from '@/components/app/AppPriorityBadge' +import { AppSection } from '@/components/app/AppSection' +import { AppStatusBadge } from '@/components/app/AppStatusBadge' +import { Button } from '@/components/ui/button' +import { Card, CardContent } from '@/components/ui/card' +import { formatDateTime } from '@/lib/format' + +import { listTickets } from '@/features/tickets/api' +import type { Ticket } from '@/features/tickets/types' + +const columns: AppDataTableColumn[] = [ + { + key: 'ticket_no', + header: '编号', + cell: (ticket) => {ticket.ticket_no} + }, + { + key: 'title', + header: '标题', + cell: (ticket) => {ticket.title} + }, + { + key: 'status', + header: '状态', + cell: (ticket) => + }, + { + key: 'priority', + header: '优先级', + cell: (ticket) => + }, + { + key: 'created_at', + header: '创建时间', + cell: (ticket) => {formatDateTime(ticket.created_at)} + } +] + +export function WorkbenchPage() { + const [tickets, setTickets] = React.useState([]) + const [error, setError] = React.useState('') + + React.useEffect(() => { + listTickets() + .then(setTickets) + .catch((reason: unknown) => setError(reason instanceof Error ? reason.message : '加载失败')) + }, []) + + const metrics = [ + { label: '待处理工单', value: tickets.filter((ticket) => ticket.status === 'pending').length }, + { label: '处理中', value: tickets.filter((ticket) => ticket.status === 'in_progress').length }, + { label: '高优先级', value: tickets.filter((ticket) => ticket.priority === 'high' || ticket.priority === 'urgent').length }, + { label: '已解决', value: tickets.filter((ticket) => ticket.status === 'resolved' || ticket.status === 'closed').length } + ] + const recentTickets = tickets.slice(0, 6) + + return ( + + + (window.location.hash = '/tickets/new')}> + + 新建工单 + + } + /> + + + {metrics.map((metric) => ( + + + {metric.label} + {metric.value} + + + ))} + + + {error ? : null} + + + (window.location.hash = '/tickets')}> + 查看全部 + + + } + > + ticket.id} + emptyTitle="暂无工单" + emptyDescription="创建第一张工单后,最近动态会显示在这里。" + onRowClick={(ticket) => (window.location.hash = `/tickets/${ticket.id}`)} + /> + + + + + (window.location.hash = '/tickets/new')}> + 创建工单 + + + (window.location.hash = '/tickets')}> + 工单中心 + + + (window.location.hash = '/solutions')}> + 解决方案库 + + + + + + + + ) +}
页面级布局只复用 App 组件,业务字段由页面自行传入。
{metric.label}
{metric.value}