重构审批中心与报销详情降噪(approval-detail-simplification-refactor)
这个提交包含在:
@@ -22,6 +22,7 @@ import {
|
||||
withdrawReimbursement,
|
||||
linkReimbursementProject,
|
||||
type ReimbursementItem,
|
||||
type ReimbursementStatus,
|
||||
} from "../api/reimbursements";
|
||||
import { remindWorkflowTask } from "../api/workflow";
|
||||
import {
|
||||
@@ -145,6 +146,22 @@ function budgetControlModeLabel(controlMode: string | null | undefined) {
|
||||
return controlMode ? labels[controlMode] ?? "按制度处理" : "按制度处理";
|
||||
}
|
||||
|
||||
function reimbursementStatusLabel(status: ReimbursementStatus) {
|
||||
const labels: Record<ReimbursementStatus, string> = {
|
||||
draft: "草稿",
|
||||
reviewing: "审批中",
|
||||
approved: "已批准",
|
||||
paid: "已支付",
|
||||
rejected: "已驳回",
|
||||
cancelled: "已取消",
|
||||
};
|
||||
return labels[status] ?? status;
|
||||
}
|
||||
|
||||
function materialCompletenessLabel(count: number, noun: string) {
|
||||
return count > 0 ? `${count} ${noun}` : `缺${noun}`;
|
||||
}
|
||||
|
||||
export function ReimbursementDetailPage() {
|
||||
const { reimbursementId = "" } = useParams();
|
||||
const navigate = useNavigate();
|
||||
@@ -158,6 +175,7 @@ export function ReimbursementDetailPage() {
|
||||
const [confirmDeleteItemId, setConfirmDeleteItemId] = useState("");
|
||||
const [confirmDeleteReimbursement, setConfirmDeleteReimbursement] = useState(false);
|
||||
const [isErpPreviewOpen, setIsErpPreviewOpen] = useState(false);
|
||||
const [showSecondaryDetail, setShowSecondaryDetail] = useState(false);
|
||||
const [submitConfirmPrecheck, setSubmitConfirmPrecheck] = useState<PrecheckResponse | null>(null);
|
||||
const [isInvoiceDialogOpen, setIsInvoiceDialogOpen] = useState(false);
|
||||
const [invoiceSearchInput, setInvoiceSearchInput] = useState("");
|
||||
@@ -971,12 +989,159 @@ export function ReimbursementDetailPage() {
|
||||
return `${linkedItem.expenseType?.name ?? "未识别费用类型"} · ${linkedItem.description ?? "未填写说明"}`;
|
||||
};
|
||||
|
||||
const itemCount = detail.items.length;
|
||||
const invoiceCount = detail.invoices.length;
|
||||
const attachmentCount = (detail.attachments ?? []).length;
|
||||
const hasBlockingPrecheck = blockStandardChecks.length > 0;
|
||||
const currentHandlerText = (() => {
|
||||
if (detail.status === "reviewing") {
|
||||
return detail.pendingWorkflow?.task?.step_name ?? "当前审批节点";
|
||||
}
|
||||
if (detail.status === "approved") {
|
||||
return canPay ? "财务处理" : "等待财务";
|
||||
}
|
||||
if (detail.status === "paid") {
|
||||
return payment.paidBy?.name ?? "财务已处理";
|
||||
}
|
||||
if (["draft", "rejected"].includes(detail.status)) {
|
||||
return detail.owner?.name ?? "报销人";
|
||||
}
|
||||
return "-";
|
||||
})();
|
||||
const nextStepText = (() => {
|
||||
if (["draft", "rejected"].includes(detail.status)) {
|
||||
if (!itemCount) {
|
||||
return "补报销明细";
|
||||
}
|
||||
if (!invoiceCount) {
|
||||
return "添加发票";
|
||||
}
|
||||
if (hasBlockingPrecheck) {
|
||||
return "修改超标明细";
|
||||
}
|
||||
return "提交审批";
|
||||
}
|
||||
if (detail.status === "reviewing") {
|
||||
return detail.workflowProgress
|
||||
? `查看第 ${detail.workflowProgress.currentStepIndex + 1} / ${detail.workflowProgress.totalSteps} 步`
|
||||
: "查看审批进度";
|
||||
}
|
||||
if (detail.status === "approved") {
|
||||
return canPay ? "执行付款" : "等待财务付款";
|
||||
}
|
||||
if (detail.status === "paid") {
|
||||
return "查看付款结果";
|
||||
}
|
||||
return "查看记录";
|
||||
})();
|
||||
const primaryAction = (() => {
|
||||
if (["draft", "rejected"].includes(detail.status) && canEditItems) {
|
||||
if (!itemCount) {
|
||||
return (
|
||||
<Button
|
||||
data-testid="reimbursement-primary-action"
|
||||
disabled={itemSaveMutation.isPending || itemDeleteMutation.isPending}
|
||||
onClick={startCreateItem}
|
||||
type="button"
|
||||
>
|
||||
新增明细
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
if (!invoiceCount) {
|
||||
return (
|
||||
<Button
|
||||
data-testid="reimbursement-primary-action"
|
||||
disabled={!canOpenInvoiceBindDialog}
|
||||
onClick={() => handleInvoiceDialogOpenChange(true)}
|
||||
type="button"
|
||||
>
|
||||
添加发票
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
data-testid={TEST_IDS.reimbursement.submit}
|
||||
disabled={mutation.isPending || hasBlockingPrecheck}
|
||||
onClick={() => void handleSubmitAction()}
|
||||
type="button"
|
||||
>
|
||||
{mutation.isPending ? "提交中..." : "提交报销"}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
if (detail.status === "reviewing") {
|
||||
return (
|
||||
<Button asChild data-testid="reimbursement-primary-action">
|
||||
<a href="#reimbursement-workflow-progress-panel">看审批进度</a>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
if (detail.status === "approved") {
|
||||
return (
|
||||
<Button asChild data-testid="reimbursement-primary-action">
|
||||
<a href={canPay ? "#reimbursement-action-panel" : "#reimbursement-payment-panel"}>
|
||||
{canPay ? "执行支付" : "看付款状态"}
|
||||
</a>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
if (detail.status === "paid") {
|
||||
return (
|
||||
<Button asChild data-testid="reimbursement-primary-action">
|
||||
<a href="#reimbursement-payment-panel">查看付款结果</a>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button asChild data-testid="reimbursement-primary-action">
|
||||
<Link to="/reimbursements">返回列表</Link>
|
||||
</Button>
|
||||
);
|
||||
})();
|
||||
|
||||
const secondaryActions = [
|
||||
detail.status === "reviewing" && canRemindPendingWorkflow && pendingTaskId ? (
|
||||
<Button
|
||||
key="remind"
|
||||
data-testid="reimbursement-remind-workflow"
|
||||
disabled={remindMutation.isPending}
|
||||
onClick={() => remindMutation.mutate(pendingTaskId)}
|
||||
type="button"
|
||||
variant="secondary"
|
||||
>
|
||||
{remindMutation.isPending ? "催办中..." : "催办审批"}
|
||||
</Button>
|
||||
) : detail.status === "paid" ? (
|
||||
<Button key="print" asChild variant="secondary">
|
||||
<Link to={`/reimbursements/${detail.id}/print`}>打印 / PDF</Link>
|
||||
</Button>
|
||||
) : (
|
||||
<Button key="back" asChild variant="secondary" data-testid="reimbursement-back">
|
||||
<Link to="/reimbursements">报销列表</Link>
|
||||
</Button>
|
||||
),
|
||||
<Button
|
||||
key="more"
|
||||
type="button"
|
||||
variant="secondary"
|
||||
data-testid="reimbursement-more-toggle"
|
||||
onClick={() => setShowSecondaryDetail((value) => !value)}
|
||||
>
|
||||
{showSecondaryDetail ? "收起更多" : "更多"}
|
||||
</Button>,
|
||||
];
|
||||
|
||||
return (
|
||||
<PageScaffold>
|
||||
{downloadReasonDialog}
|
||||
<EntityOverviewHero
|
||||
title={`报销详情 ${displayCode}`}
|
||||
subtitle="先确认当前状态和下一步,再处理明细、发票、附件和审批记录。"
|
||||
meta={(
|
||||
<>
|
||||
<StatusBadge status={detail.status} />
|
||||
@@ -987,43 +1152,134 @@ export function ReimbursementDetailPage() {
|
||||
</>
|
||||
)}
|
||||
actions={(
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button asChild variant="secondary">
|
||||
<Link to={`/reimbursements/${detail.id}/print`}>打印 / PDF</Link>
|
||||
</Button>
|
||||
<Button asChild variant="secondary" data-testid="reimbursement-back">
|
||||
<Link to="/reimbursements">报销列表</Link>
|
||||
</Button>
|
||||
{canRemindPendingWorkflow && pendingTaskId ? (
|
||||
<Button
|
||||
data-testid="reimbursement-remind-workflow"
|
||||
disabled={remindMutation.isPending}
|
||||
onClick={() => remindMutation.mutate(pendingTaskId)}
|
||||
type="button"
|
||||
variant="secondary"
|
||||
>
|
||||
{remindMutation.isPending ? "催办中..." : "催办审批"}
|
||||
</Button>
|
||||
) : null}
|
||||
{canDeleteReimbursement ? (
|
||||
<Button
|
||||
data-testid="reimbursement-delete-draft"
|
||||
disabled={deleteReimbursementMutation.isPending}
|
||||
onClick={() => setConfirmDeleteReimbursement(true)}
|
||||
type="button"
|
||||
variant="destructive"
|
||||
>
|
||||
删除草稿
|
||||
</Button>
|
||||
) : null}
|
||||
<div className="flex flex-wrap gap-2" data-testid="reimbursement-first-screen-actions">
|
||||
{primaryAction}
|
||||
{secondaryActions}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="detail-workspace-grid">
|
||||
<div className="space-y-5">
|
||||
<SectionPanel title="当前状态与下一步" description="提交、付款、附件和审批依据集中在本页处理。">
|
||||
<div className="space-y-4" data-testid="reimbursement-overview">
|
||||
<SectionPanel title="当前状态与下一步">
|
||||
<div className="detail-command-panel space-y-4" data-testid="reimbursement-overview">
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
|
||||
<div>
|
||||
<div className="text-xs font-semibold text-zinc-500">下一步</div>
|
||||
<div className="mt-1 text-2xl font-semibold text-zinc-900">{nextStepText}</div>
|
||||
</div>
|
||||
<div className="shrink-0">
|
||||
<StatusBadge status={detail.status} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="detail-fact-grid xl:grid-cols-4" data-testid="reimbursement-summary">
|
||||
<div className="detail-fact-card">
|
||||
<div className="detail-fact-card__label">状态</div>
|
||||
<div className="detail-fact-card__value">状态:{reimbursementStatusLabel(detail.status)}</div>
|
||||
</div>
|
||||
<div className="detail-fact-card">
|
||||
<div className="detail-fact-card__label">处理人</div>
|
||||
<div className="detail-fact-card__value">{currentHandlerText}</div>
|
||||
</div>
|
||||
<div className="detail-fact-card">
|
||||
<div className="detail-fact-card__label">金额</div>
|
||||
<div className="detail-fact-card__value">¥{detail.totalAmount ?? "-"}</div>
|
||||
</div>
|
||||
<div className="detail-fact-card">
|
||||
<div className="detail-fact-card__label">材料完整性</div>
|
||||
<div className="detail-fact-card__value">
|
||||
{materialCompletenessLabel(itemCount, "条明细")} / {materialCompletenessLabel(invoiceCount, "张发票")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{actionError ? (
|
||||
<p
|
||||
className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600"
|
||||
data-testid="reimbursement-action-error"
|
||||
>
|
||||
{actionError}
|
||||
</p>
|
||||
) : null}
|
||||
{submitInvoiceNotice ? (
|
||||
<p className="rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-700">
|
||||
{submitInvoiceNotice}
|
||||
</p>
|
||||
) : null}
|
||||
{hasBlockingPrecheck ? (
|
||||
<p className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600">
|
||||
请修改超标明细后提交。
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{["approved", "paid"].includes(detail.status) ? (
|
||||
<div id="reimbursement-payment-panel" className="detail-command-panel mt-4" data-testid="reimbursement-payment-status">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<h4 className="text-base font-semibold text-zinc-900">付款状态</h4>
|
||||
<span
|
||||
className={`inline-flex rounded-full border px-2 py-0.5 text-xs font-medium ${
|
||||
detail.status === "paid"
|
||||
? "border-emerald-200 bg-emerald-50 text-emerald-700"
|
||||
: "border-amber-200 bg-amber-50 text-amber-700"
|
||||
}`}
|
||||
>
|
||||
{detail.status === "paid" ? "已付款" : "未付款"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="detail-fact-grid mt-3 xl:grid-cols-4">
|
||||
<div className="detail-fact-card">
|
||||
<div className="detail-fact-card__label">付款日期</div>
|
||||
<div className="detail-fact-card__value">{payment.paymentDate ?? "-"}</div>
|
||||
</div>
|
||||
<div className="detail-fact-card">
|
||||
<div className="detail-fact-card__label">付款凭证号</div>
|
||||
<div className="detail-fact-card__value">{payment.paymentVoucherNo ?? "-"}</div>
|
||||
</div>
|
||||
<div className="detail-fact-card">
|
||||
<div className="detail-fact-card__label">付款渠道</div>
|
||||
<div className="detail-fact-card__value">{payment.paymentChannel ?? "-"}</div>
|
||||
</div>
|
||||
<div className="detail-fact-card">
|
||||
<div className="detail-fact-card__label">操作人 / 时间</div>
|
||||
<div className="detail-fact-card__value">
|
||||
{payment.paidBy?.name ?? "-"} · {formatDateTime(payment.paidAt)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{showSecondaryDetail ? (
|
||||
<div className="space-y-4 pt-4" data-testid="reimbursement-secondary-detail">
|
||||
<div className="detail-command-panel">
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
<h4 className="text-base font-semibold text-zinc-900">更多入口</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{detail.status !== "paid" ? (
|
||||
<Button asChild variant="secondary">
|
||||
<Link to={`/reimbursements/${detail.id}/print`}>打印 / PDF</Link>
|
||||
</Button>
|
||||
) : null}
|
||||
<Button asChild variant="secondary" data-testid="reimbursement-secondary-back">
|
||||
<Link to="/reimbursements">报销列表</Link>
|
||||
</Button>
|
||||
{canDeleteReimbursement ? (
|
||||
<Button
|
||||
data-testid="reimbursement-delete-draft"
|
||||
disabled={deleteReimbursementMutation.isPending}
|
||||
onClick={() => setConfirmDeleteReimbursement(true)}
|
||||
type="button"
|
||||
variant="destructive"
|
||||
>
|
||||
删除草稿
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<StatStrip columns={4} className="ops-status-strip">
|
||||
<MetricCard
|
||||
title="关联项目"
|
||||
@@ -1041,7 +1297,7 @@ export function ReimbursementDetailPage() {
|
||||
/>
|
||||
</StatStrip>
|
||||
|
||||
<div className="detail-command-panel" data-testid="reimbursement-summary">
|
||||
<div className="detail-command-panel" data-testid="reimbursement-secondary-summary">
|
||||
<div className="detail-fact-grid">
|
||||
<div className="detail-fact-card">
|
||||
<div className="detail-fact-card__label">报销编号</div>
|
||||
@@ -1062,7 +1318,7 @@ export function ReimbursementDetailPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="detail-command-panel" data-testid="reimbursement-payment-status">
|
||||
<div className="detail-command-panel" data-testid="reimbursement-secondary-payment-status">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
<h4 className="text-base font-semibold text-zinc-900">付款状态</h4>
|
||||
@@ -1163,7 +1419,6 @@ export function ReimbursementDetailPage() {
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 text-sm">
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
@@ -1242,7 +1497,9 @@ export function ReimbursementDetailPage() {
|
||||
) : (
|
||||
<p className="text-sm text-zinc-500">暂无附件材料</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</SectionPanel>
|
||||
|
||||
<SectionPanel title="明细与发票" description="明细决定金额,发票决定提交完整性。">
|
||||
@@ -1579,75 +1836,84 @@ export function ReimbursementDetailPage() {
|
||||
</div>
|
||||
|
||||
<div className="space-y-5">
|
||||
<DetailSideMeta title="报销摘要">
|
||||
<div>报销编号:{displayCode}</div>
|
||||
<div>报销人:{detail.owner?.name ?? "-"}</div>
|
||||
<div>提交人:{detail.submitter?.name ?? "-"}</div>
|
||||
<div>当前审批:{detail.pendingWorkflow?.task?.step_name ?? "-"}</div>
|
||||
<div>明细数:{detail.items.length}</div>
|
||||
<div>票据数:{detail.invoices.length}</div>
|
||||
<div>关联项目:{detail.project ? `${detail.project.projectNo} · ${detail.project.name}` : "未关联"}</div>
|
||||
</DetailSideMeta>
|
||||
|
||||
{detail.status === "reviewing" && detail.workflowProgress ? (
|
||||
<DetailSideMeta
|
||||
title="审批进度"
|
||||
description={`当前位于第 ${detail.workflowProgress.currentStepIndex + 1} / ${detail.workflowProgress.totalSteps} 步。`}
|
||||
>
|
||||
<div className="detail-timeline" data-testid={TEST_IDS.reimbursement.workflowProgress}>
|
||||
{detail.workflowProgress.steps.map((step, index) => (
|
||||
<div
|
||||
key={step.id}
|
||||
className={
|
||||
step.status === "current"
|
||||
? "detail-timeline-item detail-timeline-item--current"
|
||||
: step.status === "completed"
|
||||
? "detail-timeline-item detail-timeline-item--done"
|
||||
: "detail-timeline-item"
|
||||
}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-3 text-sm">
|
||||
<span className="font-medium text-zinc-900">
|
||||
第 {index + 1} 步 · {step.name}
|
||||
</span>
|
||||
<span className="text-xs text-zinc-500">
|
||||
{step.status === "completed" ? "已完成" : step.status === "current" ? "当前节点" : "待处理"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{showSecondaryDetail ? (
|
||||
<DetailSideMeta title="报销摘要">
|
||||
<div>报销编号:{displayCode}</div>
|
||||
<div>报销人:{detail.owner?.name ?? "-"}</div>
|
||||
<div>提交人:{detail.submitter?.name ?? "-"}</div>
|
||||
<div>当前审批:{detail.pendingWorkflow?.task?.step_name ?? "-"}</div>
|
||||
<div>明细数:{detail.items.length}</div>
|
||||
<div>票据数:{detail.invoices.length}</div>
|
||||
<div>附件数:{attachmentCount}</div>
|
||||
<div>关联项目:{detail.project ? `${detail.project.projectNo} · ${detail.project.name}` : "未关联"}</div>
|
||||
</DetailSideMeta>
|
||||
) : null}
|
||||
|
||||
<DetailSideMeta title="审批记录">
|
||||
{detail.approvalHistory.length ? (
|
||||
<div className="detail-timeline" data-testid="reimbursement-approval-history">
|
||||
{detail.approvalHistory.map((item) => (
|
||||
<div key={item.id} className="detail-timeline-item">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="font-medium text-zinc-900">{workflowActionLabel(item.action)}</span>
|
||||
<span className="text-xs text-zinc-500">{formatDateTime(item.createdAt)}</span>
|
||||
{detail.status === "reviewing" && detail.workflowProgress ? (
|
||||
<div id="reimbursement-workflow-progress-panel">
|
||||
<DetailSideMeta
|
||||
title="审批进度"
|
||||
description={`当前位于第 ${detail.workflowProgress.currentStepIndex + 1} / ${detail.workflowProgress.totalSteps} 步。`}
|
||||
>
|
||||
<div className="detail-timeline" data-testid={TEST_IDS.reimbursement.workflowProgress}>
|
||||
{detail.workflowProgress.steps.map((step, index) => (
|
||||
<div
|
||||
key={step.id}
|
||||
className={
|
||||
step.status === "current"
|
||||
? "detail-timeline-item detail-timeline-item--current"
|
||||
: step.status === "completed"
|
||||
? "detail-timeline-item detail-timeline-item--done"
|
||||
: "detail-timeline-item"
|
||||
}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-3 text-sm">
|
||||
<span className="font-medium text-zinc-900">
|
||||
第 {index + 1} 步 · {step.name}
|
||||
</span>
|
||||
<span className="text-xs text-zinc-500">
|
||||
{step.status === "completed" ? "已完成" : step.status === "current" ? "当前节点" : "待处理"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">
|
||||
{item.stepName ?? "-"} · {item.actorName || "系统"}
|
||||
))}
|
||||
</div>
|
||||
</DetailSideMeta>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{showSecondaryDetail ? (
|
||||
<DetailSideMeta title="审批记录">
|
||||
{detail.approvalHistory.length ? (
|
||||
<div className="detail-timeline" data-testid="reimbursement-approval-history">
|
||||
{detail.approvalHistory.map((item) => (
|
||||
<div key={item.id} className="detail-timeline-item">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="font-medium text-zinc-900">{workflowActionLabel(item.action)}</span>
|
||||
<span className="text-xs text-zinc-500">{formatDateTime(item.createdAt)}</span>
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">
|
||||
{item.stepName ?? "-"} · {item.actorName || "系统"}
|
||||
</div>
|
||||
{item.comment ? <div className="mt-2 text-zinc-600">{item.comment}</div> : null}
|
||||
</div>
|
||||
{item.comment ? <div className="mt-2 text-zinc-600">{item.comment}</div> : null}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-sm text-zinc-500">暂无审批记录。</div>
|
||||
)}
|
||||
</DetailSideMeta>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-sm text-zinc-500">暂无审批记录。</div>
|
||||
)}
|
||||
</DetailSideMeta>
|
||||
) : null}
|
||||
|
||||
<ReimbursementActions
|
||||
status={detail.status}
|
||||
canPay={canPay}
|
||||
actionError={actionError}
|
||||
showSubmitAction={false}
|
||||
showPayAction={detail.status === "approved"}
|
||||
actionError={["reviewing", "approved"].includes(detail.status) ? actionError : ""}
|
||||
submitDisabled={blockStandardChecks.length > 0}
|
||||
submitHint={blockStandardChecks.length > 0 ? "请修改超标明细后提交" : ""}
|
||||
submitNotice={submitInvoiceNotice}
|
||||
submitHint=""
|
||||
submitNotice=""
|
||||
isPending={mutation.isPending}
|
||||
onSubmit={() => void handleSubmitAction()}
|
||||
onWithdraw={(comment) => mutation.mutate({ action: "withdraw", comment })}
|
||||
|
||||
在新工单中引用
屏蔽一个用户