重构审批中心与报销详情降噪(approval-detail-simplification-refactor)

这个提交包含在:
2026-05-29 13:10:06 +08:00
父节点 29cfea2bbb
当前提交 5f10b83706
共修改 11 个文件,包含 739 行新增和 177 行删除
+111 -67
查看文件
@@ -21,7 +21,6 @@ import {
StatStrip,
WorkspaceEmptyState,
WorkspaceHeader,
WorkspaceNotice,
} from "../components/patterns/ListWorkspace";
import { MetricCard } from "../components/patterns/MetricCard";
import { Button } from "../components/ui/button";
@@ -137,12 +136,12 @@ function isAlreadyProcessedTaskError(error: unknown) {
export function WorkflowTasksPage() {
const [searchParams, setSearchParams] = useSearchParams();
const [confirmApproveTaskId, setConfirmApproveTaskId] = useState<string | null>(null);
const queryClient = useQueryClient();
const status = normalizeStatusFilter(searchParams.get("status"));
const entityType = normalizeEntityFilter(searchParams.get("entityType"));
const page = Number(searchParams.get("page") ?? "1") || 1;
const [confirmApproveTaskId, setConfirmApproveTaskId] = useState<string | null>(null);
const [showAdvanced, setShowAdvanced] = useState(() => status !== "pending" || entityType !== "all");
const queryClient = useQueryClient();
function updateSearchParams(next: {
status?: WorkflowStatusFilter;
@@ -203,6 +202,15 @@ export function WorkflowTasksPage() {
const totalPages = Math.max(1, Math.ceil((query.data?.total ?? 0) / PAGE_SIZE));
const taskItems = query.data?.items ?? [];
const confirmCopy = confirmApproveTaskId ? getWorkflowConfirmCopy("approve") : null;
const isDefaultTaskView = status === "pending" && entityType === "all";
const advancedVisible = showAdvanced || !isDefaultTaskView;
const pendingTotal = summaryQuery.data?.pendingTotal ?? "-";
const overdueTotal = summaryQuery.data?.overdueTotal ?? "-";
const pendingEntitySummary =
(summaryQuery.data?.byEntityType ?? [])
.filter((item) => item.pendingTotal > 0)
.map((item) => `${entityLabel(item.key)} ${item.pendingTotal}`)
.join(" / ") || "-";
return (
<>
@@ -210,73 +218,107 @@ export function WorkflowTasksPage() {
<WorkspaceHeader
eyebrow="审批"
title="审批中心"
subtitle="先看待处理,再进入详情核对依据;快速通过前仍会弹出确认。"
actions={
<Button
type="button"
variant="secondary"
data-testid="workflow-tasks-advanced-toggle"
onClick={() => setShowAdvanced((value) => !value)}
>
{advancedVisible ? "收起高级" : "高级 / 历史"}
</Button>
}
/>
<div data-testid="workflow-task-summary">
<StatStrip columns={4} className="ops-status-strip">
<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>
<FilterBar className="ops-command-bar !p-3 md:!p-3.5">
<div className="ops-command-bar__row">
<Tabs
data-testid="workflow-tasks-status-tabs"
value={status}
onValueChange={(value) => updateSearchParams({ status: value as WorkflowStatusFilter, page: 1 })}
>
<TabsList className="ops-command-bar__tabs !h-auto !justify-start !bg-slate-50">
{WORKFLOW_STATUS_OPTIONS.map((item) => (
<TabsTrigger
data-testid={`workflow-tasks-tab-${item.value}`}
key={item.value}
value={item.value}
>
{item.label}
</TabsTrigger>
))}
</TabsList>
</Tabs>
<div className="flex min-w-0 flex-wrap items-center gap-3" data-testid="workflow-tasks-focus">
<span className="badge-neutral">待处理</span>
<span className="text-2xl font-semibold text-zinc-900">{pendingTotal}</span>
<span className="text-sm text-zinc-500">
最早:{formatSummaryTime(summaryQuery.data?.earliestPendingAt)}
</span>
{summaryQuery.data?.overdueTotal ? (
<span className="rounded-full border border-amber-200 bg-amber-50 px-2.5 py-1 text-xs font-medium text-amber-700">
超时 {summaryQuery.data.overdueTotal}
</span>
) : null}
</div>
<div className="ops-command-bar__actions">
<label className="ops-filter-label" htmlFor="workflow-entity-type-filter">
业务类型
</label>
<select
id="workflow-entity-type-filter"
className="select-sm min-w-32"
data-testid="workflow-tasks-entity-filter"
value={entityType}
onChange={(event) => updateSearchParams({ entityType: event.target.value as WorkflowEntityFilter, page: 1 })}
>
{WORKFLOW_ENTITY_OPTIONS.map((item) => (
<option key={item.value} value={item.value}>
{item.label}
</option>
))}
</select>
{!isDefaultTaskView ? (
<Button
type="button"
size="sm"
onClick={() => {
setShowAdvanced(false);
updateSearchParams({ status: "pending", entityType: "all", page: 1 });
}}
>
回到待处理
</Button>
) : null}
<span className="text-xs text-zinc-500">业务:{pendingEntitySummary}</span>
</div>
</div>
</FilterBar>
{status === "pending" ? (
<WorkspaceNotice title="处理原则" tone="neutral">
先看业务编号、申请人、金额/天数和当前节点;需要查看附件、审批历史或驳回原因时进入详情页。
</WorkspaceNotice>
{advancedVisible ? (
<div className="space-y-3" data-testid="workflow-tasks-advanced-panel">
<div data-testid="workflow-task-summary">
<StatStrip columns={4} className="ops-status-strip">
<MetricCard title="待办总数" value={pendingTotal} />
<MetricCard title="超时待办" value={overdueTotal} tone="warning" />
<MetricCard title="最早待处理" value={formatSummaryTime(summaryQuery.data?.earliestPendingAt)} />
<MetricCard
title="业务分布"
value={<span className="block truncate text-base">{pendingEntitySummary}</span>}
/>
</StatStrip>
</div>
<FilterBar className="ops-command-bar !p-3 md:!p-3.5">
<div className="ops-command-bar__row">
<Tabs
data-testid="workflow-tasks-status-tabs"
value={status}
onValueChange={(value) => updateSearchParams({ status: value as WorkflowStatusFilter, page: 1 })}
>
<TabsList className="ops-command-bar__tabs !h-auto !justify-start !bg-slate-50">
{WORKFLOW_STATUS_OPTIONS.map((item) => (
<TabsTrigger
data-testid={`workflow-tasks-tab-${item.value}`}
key={item.value}
value={item.value}
>
{item.label}
</TabsTrigger>
))}
</TabsList>
</Tabs>
<div className="ops-command-bar__actions">
<label className="ops-filter-label" htmlFor="workflow-entity-type-filter">
业务类型
</label>
<select
id="workflow-entity-type-filter"
className="select-sm min-w-32"
data-testid="workflow-tasks-entity-filter"
value={entityType}
onChange={(event) =>
updateSearchParams({ entityType: event.target.value as WorkflowEntityFilter, page: 1 })
}
>
{WORKFLOW_ENTITY_OPTIONS.map((item) => (
<option key={item.value} value={item.value}>
{item.label}
</option>
))}
</select>
</div>
</div>
</FilterBar>
</div>
) : null}
{query.isLoading ? <TableSkeleton columns={7} rows={5} /> : null}
@@ -348,13 +390,15 @@ export function WorkflowTasksPage() {
variant="secondary"
data-testid={TEST_IDS.workflow.detailLink}
>
<Link to={`/workflow/tasks/${task.id}`}>详情</Link>
<Link to={`/workflow/tasks/${task.id}`}>查看</Link>
</Button>
</div>
</article>
))
) : (
<WorkspaceEmptyState>当前筛选下没有审批任务。</WorkspaceEmptyState>
<WorkspaceEmptyState>
{isDefaultTaskView ? "当前没有待处理任务。" : "当前筛选下没有审批任务。"}
</WorkspaceEmptyState>
)}
</div>
@@ -405,10 +449,10 @@ export function WorkflowTasksPage() {
<Button
asChild
size="sm"
variant="secondary"
variant={status === "pending" && (task.allowedActions ?? []).includes("approve") ? "secondary" : "default"}
data-testid={TEST_IDS.workflow.detailLink}
>
<Link to={`/workflow/tasks/${task.id}`}>详情</Link>
<Link to={`/workflow/tasks/${task.id}`}>查看</Link>
</Button>
</div>
</TableCell>
@@ -417,7 +461,7 @@ export function WorkflowTasksPage() {
) : (
<TableRow>
<TableCell className="px-6 py-8 text-center text-zinc-500" colSpan={8}>
当前筛选下没有审批任务
{isDefaultTaskView ? "当前没有待处理任务" : "当前筛选下没有审批任务"}
</TableCell>
</TableRow>
)}