feat(detail-page-structure-progress-actions): 统一详情处理进度与操作区

这个提交包含在:
2026-08-03 16:00:42 +08:00
父节点 8d428bd51d
当前提交 b7d5761067
共修改 31 个文件,包含 1453 行新增和 383 行删除
@@ -98,7 +98,7 @@ export function BusinessDetailActionPanel({
const hasContentBeforeSummary = Boolean(primaryAction || secondaryActions || moreActions || hasChildren);
return (
<SectionPanel title={title} actions={status} className={cn("detail-action-panel xl:sticky xl:top-24", className)}>
<SectionPanel title={title} actions={status} className={cn("detail-action-panel lg:sticky lg:top-24", className)}>
<div className="detail-action-stack" data-testid={testId}>
{primaryAction ? <div className="grid gap-2">{primaryAction}</div> : null}
{secondaryActions ? <div className="workflow-action-bar">{secondaryActions}</div> : null}
@@ -1,12 +1,15 @@
import { AlertCircle, Check, Circle, Clock, ExternalLink } from "lucide-react";
import { AlertCircle, Ban, Check, Circle, Clock, ExternalLink } from "lucide-react";
import { Link } from "react-router-dom";
import type { ReimbursementProcessingProgress as ProcessingProgress } from "../../api/reimbursements";
import type { ProcessingProgress as ProcessingProgressValue } from "../../types/processingProgress";
import { formatDisplayDateTime } from "../../lib/displayFormat";
import { cn } from "../../lib/utils";
type Props = {
progress: ProcessingProgress;
progress: ProcessingProgressValue;
ariaLabel: string;
testId: string;
mobile?: boolean;
targetLinkLabel?: string;
};
const STATE_TEXT = {
@@ -14,15 +17,18 @@ const STATE_TEXT = {
current: "处理中",
pending: "待处理",
exception: "需处理",
terminated: "已终止",
} as const;
export function ReimbursementProcessingProgress({ progress, mobile = false }: Props) {
export function ProcessingProgress({
progress,
ariaLabel,
testId,
mobile = false,
targetLinkLabel = "查看详情",
}: Props) {
return (
<ol
className="space-y-0"
data-testid={mobile ? "mobile-reimbursement-processing-progress" : "reimbursement-workflow-progress"}
aria-label="报销处理进度"
>
<ol className="space-y-0" data-testid={testId} aria-label={ariaLabel}>
{progress.stages.map((stage, index) => {
const expanded = stage.state === "current" || stage.state === "exception" || Boolean(stage.comment);
const Icon = stage.state === "completed"
@@ -31,7 +37,9 @@ export function ReimbursementProcessingProgress({ progress, mobile = false }: Pr
? Clock
: stage.state === "exception"
? AlertCircle
: Circle;
: stage.state === "terminated"
? Ban
: Circle;
const facts = [stage.actorName, stage.occurredAt ? formatDisplayDateTime(stage.occurredAt) : null, stage.result]
.filter(Boolean);
@@ -47,6 +55,7 @@ export function ReimbursementProcessingProgress({ progress, mobile = false }: Pr
stage.state === "current" && "border-blue-300 bg-blue-50 text-blue-700",
stage.state === "exception" && "border-red-300 bg-red-50 text-red-700",
stage.state === "pending" && "border-zinc-300 text-zinc-400",
stage.state === "terminated" && "border-zinc-300 bg-zinc-100 text-zinc-500",
)}
aria-hidden="true"
>
@@ -69,7 +78,7 @@ export function ReimbursementProcessingProgress({ progress, mobile = false }: Pr
className="mt-2 inline-flex min-h-11 items-center gap-1 text-sm font-medium text-primary hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2"
to={stage.targetHref}
>
查看所在付款批次
{targetLinkLabel}
<ExternalLink aria-hidden="true" className="size-4" />
</Link>
) : null}