DG9 统一全站 UI 布局样板迁移

关联 Issue: DG9-0..DG9-12
这个提交包含在:
2026-05-14 00:52:59 +08:00
父节点 3ac5c785b1
当前提交 4b14c925b6
共修改 30 个文件,包含 743 行新增和 437 行删除
+67 -49
查看文件
@@ -9,10 +9,19 @@ import {
fetchWorkflowTaskSummary,
type WorkflowTaskSummary,
} from "../api/workflow";
import { PageHeader } from "../components/common/PageHeader";
import { Pagination } from "../components/common/Pagination";
import { StatusBadge } from "../components/common/StatusBadge";
import { TableSkeleton } from "../components/common/TableSkeleton";
import {
ActionToolbar,
FilterBar,
ListWorkspace,
PrimaryTable,
StatStrip,
WorkspaceEmptyState,
WorkspaceHeader,
} from "../components/patterns/ListWorkspace";
import { MetricCard } from "../components/patterns/MetricCard";
import { Button } from "../components/ui/button";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../components/ui/table";
import { Tabs, TabsList, TabsTrigger } from "../components/ui/tabs";
@@ -187,35 +196,34 @@ export function WorkflowTasksPage() {
const taskItems = query.data?.items ?? [];
return (
<div className="space-y-4 md:space-y-6">
<PageHeader title="审批中心" subtitle="集中处理待办、查看已办和业务审批历史。" />
<ListWorkspace>
<WorkspaceHeader
eyebrow="审批"
title="审批中心"
subtitle="集中处理待办、查看已办和业务审批历史。"
/>
<div className="grid gap-3 md:grid-cols-4" data-testid="workflow-task-summary">
<div className="rounded-lg border border-zinc-200 bg-white px-4 py-3">
<div className="text-xs text-zinc-500">待办总数</div>
<div className="mt-2 text-2xl font-semibold text-zinc-900">{summaryQuery.data?.pendingTotal ?? "-"}</div>
</div>
<div className="rounded-lg border border-zinc-200 bg-white px-4 py-3">
<div className="text-xs text-zinc-500">超时待办</div>
<div className="mt-2 text-2xl font-semibold text-amber-700">{summaryQuery.data?.overdueTotal ?? "-"}</div>
</div>
<div className="rounded-lg border border-zinc-200 bg-white px-4 py-3">
<div className="text-xs text-zinc-500">最早待处理</div>
<div className="mt-2 text-sm font-medium text-zinc-900">{formatSummaryTime(summaryQuery.data?.earliestPendingAt)}</div>
</div>
<div className="rounded-lg border border-zinc-200 bg-white px-4 py-3">
<div className="text-xs text-zinc-500">业务分布</div>
<div className="mt-2 truncate text-sm font-medium text-zinc-900">
{(summaryQuery.data?.byEntityType ?? [])
.filter((item) => item.pendingTotal > 0)
.map((item) => `${entityLabel(item.key)} ${item.pendingTotal}`)
.join(" / ") || "-"}
</div>
</div>
<div data-testid="workflow-task-summary">
<StatStrip columns={4}>
<MetricCard title="待办总数" value={summaryQuery.data?.pendingTotal ?? "-"} />
<MetricCard title="超时待办" value={summaryQuery.data?.overdueTotal ?? "-"} tone="warning" />
<MetricCard title="最早待处理" value={formatSummaryTime(summaryQuery.data?.earliestPendingAt)} />
<MetricCard
title="业务分布"
value={
<span className="block truncate text-base">
{(summaryQuery.data?.byEntityType ?? [])
.filter((item) => item.pendingTotal > 0)
.map((item) => `${entityLabel(item.key)} ${item.pendingTotal}`)
.join(" / ") || "-"}
</span>
}
/>
</StatStrip>
</div>
<div className="card-std space-y-4 p-4 md:p-5">
<div className="flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between">
<FilterBar>
<ActionToolbar className="items-start">
<Tabs
data-testid="workflow-tasks-status-tabs"
value={status}
@@ -234,13 +242,13 @@ export function WorkflowTasksPage() {
</TabsList>
</Tabs>
<div className="flex items-center gap-2">
<div className="flex w-full items-center gap-2 sm:w-auto">
<label className="text-sm font-medium text-zinc-700" htmlFor="workflow-entity-type-filter">
业务类型
</label>
<select
id="workflow-entity-type-filter"
className="select-std min-w-36"
className="select-std min-w-36 flex-1 sm:flex-none"
data-testid="workflow-tasks-entity-filter"
value={entityType}
onChange={(event) => updateSearchParams({ entityType: event.target.value as WorkflowEntityFilter, page: 1 })}
@@ -252,7 +260,8 @@ export function WorkflowTasksPage() {
))}
</select>
</div>
</div>
</ActionToolbar>
</FilterBar>
{query.isLoading ? <TableSkeleton columns={7} rows={5} /> : null}
@@ -270,24 +279,36 @@ export function WorkflowTasksPage() {
{!query.isLoading && !query.isError ? (
<>
<div className="space-y-3 md:hidden" data-testid="workflow-tasks-mobile-list">
<div className="mobile-page-stack md:hidden" data-testid="workflow-tasks-mobile-list">
{taskItems.length ? (
taskItems.map((task) => (
<article className="rounded-xl border border-zinc-200 bg-white p-4 shadow-sm" key={task.id}>
<div className="flex items-start justify-between gap-3">
<article className="mobile-list-card" key={task.id}>
<div className="mobile-list-card__header">
<div className="min-w-0">
<div className="truncate text-sm font-semibold text-zinc-900">{displayTitle(task)}</div>
<div className="mt-1 text-xs text-zinc-500">
<div className="mobile-list-card__title">{displayTitle(task)}</div>
<div className="mobile-list-card__subtitle">
{entityLabel(task.workflowInstance?.entityType)} · {displayReference(task)}
</div>
</div>
<StatusBadge className="shrink-0" status={task.status} />
<StatusBadge className="mobile-status-slot" status={task.status} />
</div>
<div className="mt-3 grid gap-1 rounded-lg bg-zinc-50 px-3 py-2 text-xs text-zinc-600">
<div>申请人:{displayApplicant(task)}</div>
<div>金额/天数:{displayMetric(task)}</div>
<div>当前节点:{task.stepName}</div>
<div>业务状态:{businessStatusLabel(task.businessSummary?.businessStatus)}</div>
<div className="mobile-field-list mt-3 rounded-lg bg-zinc-50 px-3 py-2">
<div className="mobile-field-row">
<span className="mobile-field-label">申请人</span>
<span className="mobile-field-value">{displayApplicant(task)}</span>
</div>
<div className="mobile-field-row">
<span className="mobile-field-label">金额/天数</span>
<span className="mobile-field-value">{displayMetric(task)}</span>
</div>
<div className="mobile-field-row">
<span className="mobile-field-label">当前节点</span>
<span className="mobile-field-value">{task.stepName}</span>
</div>
<div className="mobile-field-row">
<span className="mobile-field-label">业务状态</span>
<span className="mobile-field-value">{businessStatusLabel(task.businessSummary?.businessStatus)}</span>
</div>
</div>
<div className="mt-3 grid grid-cols-2 gap-2">
{status === "pending" && (task.allowedActions ?? []).includes("approve") ? (
@@ -317,13 +338,11 @@ export function WorkflowTasksPage() {
</article>
))
) : (
<div className="rounded-xl border border-dashed border-zinc-300 px-4 py-8 text-center text-sm text-zinc-500">
暂无数据
</div>
<WorkspaceEmptyState />
)}
</div>
<div className="hidden rounded-lg border border-zinc-200 md:block">
<PrimaryTable className="hidden md:block">
<Table data-testid="workflow-tasks-table">
<TableHeader>
<tr>
@@ -381,14 +400,14 @@ export function WorkflowTasksPage() {
))
) : (
<TableRow>
<TableCell className="py-3 text-zinc-500" colSpan={8}>
<TableCell className="px-6 py-8 text-center text-zinc-500" colSpan={8}>
暂无数据
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
</PrimaryTable>
</>
) : null}
@@ -398,7 +417,6 @@ export function WorkflowTasksPage() {
onPageChange={(p) => updateSearchParams({ page: p })}
testIdPrefix="workflow-tasks"
/>
</div>
</div>
</ListWorkspace>
);
}