收敛报销详情并展示五阶段进度(07-27-yang-jun-reimbursement-readiness)
这个提交包含在:
@@ -40,9 +40,11 @@ import { AttachmentGovernanceBadges } from "../components/common/AttachmentGover
|
||||
import { AttachmentDeleteConfirmDialog } from "../components/common/AttachmentDeleteConfirmDialog";
|
||||
import { useAttachmentDownloadReasonDialog } from "../components/common/useAttachmentDownloadReasonDialog";
|
||||
import { ConfirmDialog } from "../components/common/ConfirmDialog";
|
||||
import { RiskFlagsCard } from "../components/common/RiskFlagsCard";
|
||||
import { MobileIcon } from "../components/mobile/MobileIcon";
|
||||
import { MobileDetailHeader } from "../components/mobile/MobileDetailHeader";
|
||||
import { ReimbursementTravelSummary } from "../components/reimbursements/ReimbursementTravelSummary";
|
||||
import { ReimbursementProcessingProgress } from "../components/reimbursements/ReimbursementProcessingProgress";
|
||||
import { PageScaffold } from "../components/patterns/PageScaffold";
|
||||
import { Button } from "../components/ui/button";
|
||||
import {
|
||||
@@ -132,20 +134,6 @@ function formatDate(value: string | null | undefined) {
|
||||
return value || "未填写";
|
||||
}
|
||||
|
||||
function workflowActionLabel(action: string) {
|
||||
const labels: Record<string, string> = {
|
||||
start: "提交",
|
||||
approve: "通过",
|
||||
reject: "驳回",
|
||||
return: "退回",
|
||||
transfer: "转审",
|
||||
withdraw: "撤回",
|
||||
remind: "催办",
|
||||
overdue_remind: "超时提醒",
|
||||
};
|
||||
return labels[action] ?? action;
|
||||
}
|
||||
|
||||
function matchInvoiceSearch(
|
||||
invoice: {
|
||||
invoiceCode?: string | null;
|
||||
@@ -933,17 +921,40 @@ export function MobileReimbursementDetailPage() {
|
||||
<div className="text-xs text-zinc-500">报销人</div>
|
||||
<div className="mt-1 truncate font-semibold text-zinc-900">{detail.owner?.name ?? "-"}</div>
|
||||
</div>
|
||||
<div className="rounded-lg bg-zinc-50 px-3 py-2">
|
||||
<div className="text-xs text-zinc-500">部门</div>
|
||||
<div className="mt-1 truncate font-semibold text-zinc-900">{detail.department?.name ?? "-"}</div>
|
||||
</div>
|
||||
<div className="rounded-lg bg-zinc-50 px-3 py-2">
|
||||
<div className="text-xs text-zinc-500">申请人</div>
|
||||
<div className="mt-1 truncate font-semibold text-zinc-900">{detail.submitter?.name ?? "-"}</div>
|
||||
</div>
|
||||
</div>
|
||||
{detail.description ? <p className="mt-3 text-sm leading-6 text-zinc-600">{detail.description}</p> : null}
|
||||
<ReimbursementTravelSummary
|
||||
detail={detail}
|
||||
reimbursementId={reimbursementId}
|
||||
editable={Boolean(canEdit && isParticipant)}
|
||||
mobile
|
||||
embedded
|
||||
/>
|
||||
{detail.project || detail.linkedTravelRequest ? (
|
||||
<div className="mt-4 border-t border-zinc-200 pt-4 text-sm text-zinc-600">
|
||||
{detail.project ? <p>项目:{detail.project.projectNo} · {detail.project.name}</p> : null}
|
||||
{detail.linkedTravelRequest ? (
|
||||
<Link className="mt-2 inline-flex min-h-11 items-center text-primary hover:underline" to={`/travel-requests?tab=requests&id=${detail.linkedTravelRequest.id}`}>
|
||||
差旅申请:{detail.linkedTravelRequest.requestNo ?? "未编号"}
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{detail.riskFlags.length ? (
|
||||
<div className="mt-4 border-t border-zinc-200 pt-4">
|
||||
<RiskFlagsCard items={detail.riskFlags} title="待处理风险项" />
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
|
||||
<ReimbursementTravelSummary
|
||||
detail={detail}
|
||||
reimbursementId={reimbursementId}
|
||||
editable={Boolean(canEdit && isParticipant)}
|
||||
mobile
|
||||
/>
|
||||
|
||||
<section className="mobile-card" data-testid="mobile-reimbursement-items">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<h2 className="text-base font-semibold text-zinc-900">报销明细</h2>
|
||||
@@ -1164,7 +1175,6 @@ export function MobileReimbursementDetailPage() {
|
||||
) : null}
|
||||
|
||||
{detail.invoices.length || (detail.attachments ?? []).length ? (
|
||||
canOpenInvoiceDialog || canManageAttachments || Boolean(materialPreviewError) ? (
|
||||
<div className="mt-3 space-y-2">
|
||||
<div data-testid={TEST_IDS.reimbursement.invoices} className="space-y-2">
|
||||
{detail.invoices.map((invoice) => {
|
||||
@@ -1247,7 +1257,6 @@ export function MobileReimbursementDetailPage() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
) : (
|
||||
<div className="mt-3 rounded-lg border border-dashed border-zinc-200 p-5 text-center text-sm text-zinc-500">
|
||||
暂无报销材料
|
||||
@@ -1255,14 +1264,10 @@ export function MobileReimbursementDetailPage() {
|
||||
)}
|
||||
</section>
|
||||
|
||||
{detail.status === "reviewing" || detail.approvalHistory.length ? (
|
||||
<section className="mobile-card" data-testid="mobile-reimbursement-approval-history">
|
||||
<section className="mobile-card" data-testid="mobile-reimbursement-processing-section">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<h2 className="text-base font-semibold text-zinc-900">审批</h2>
|
||||
<p className="mt-1 text-xs text-zinc-500">
|
||||
当前节点:{detail.pendingWorkflow?.task?.step_name ?? "-"}
|
||||
</p>
|
||||
<h2 className="text-base font-semibold text-zinc-900">处理进度</h2>
|
||||
</div>
|
||||
{canRemindPendingWorkflow && pendingTaskId ? (
|
||||
<Button
|
||||
@@ -1276,27 +1281,10 @@ export function MobileReimbursementDetailPage() {
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{detail.approvalHistory.length ? (
|
||||
<div className="mt-3 space-y-2">
|
||||
{detail.approvalHistory.map((item) => (
|
||||
<article key={item.id} className="mobile-list-card">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="text-sm font-semibold text-zinc-900">{workflowActionLabel(item.action)}</div>
|
||||
<div className="shrink-0 text-xs text-zinc-500">{formatDisplayDateTime(item.createdAt)}</div>
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-zinc-500">
|
||||
{item.stepName ?? "-"} · {item.actorName || "系统"}
|
||||
</div>
|
||||
{item.comment ? <p className="mt-2 text-sm leading-5 text-zinc-600">{item.comment}</p> : null}
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="mt-3 text-sm text-zinc-500">暂无审批记录。</p>
|
||||
)}
|
||||
<div className="mt-3">
|
||||
<ReimbursementProcessingProgress progress={detail.processingProgress ?? { stages: [] }} mobile />
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{showSecondaryActions ? (
|
||||
<section className="mobile-card" data-testid="mobile-reimbursement-actions">
|
||||
|
||||
在新工单中引用
屏蔽一个用户