MIF7 移动信息架构整改收口
这个提交包含在:
@@ -33,6 +33,7 @@ import { useAuth } from "../auth/AuthContext";
|
||||
import { hasPermission } from "../auth/permissions";
|
||||
import { ConfirmDialog } from "../components/common/ConfirmDialog";
|
||||
import { MobileIcon } from "../components/mobile/MobileIcon";
|
||||
import { MobileDetailHeader } from "../components/mobile/MobileDetailHeader";
|
||||
import { PageScaffold } from "../components/patterns/PageScaffold";
|
||||
import { Button } from "../components/ui/button";
|
||||
import {
|
||||
@@ -92,6 +93,10 @@ const STATUS_TONE: Record<ReimbursementStatus, string> = {
|
||||
cancelled: "bg-zinc-100 text-zinc-500",
|
||||
};
|
||||
|
||||
function reimbursementFallback(status: ReimbursementStatus) {
|
||||
return `/mobile/reimbursements?status=${encodeURIComponent(status)}`;
|
||||
}
|
||||
|
||||
function todayDate() {
|
||||
return new Date().toISOString().slice(0, 10);
|
||||
}
|
||||
@@ -795,18 +800,17 @@ export function MobileReimbursementDetailPage() {
|
||||
)}
|
||||
data-testid="mobile-reimbursement-detail"
|
||||
>
|
||||
<header className="flex items-center gap-3" data-testid="mobile-reimbursement-detail">
|
||||
<Button asChild size="sm" variant="secondary">
|
||||
<Link to="/mobile/reimbursements">返回</Link>
|
||||
</Button>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="truncate text-base font-semibold text-zinc-900">{displayCode}</div>
|
||||
<div className="text-xs text-zinc-500">报销详情</div>
|
||||
</div>
|
||||
<span className={cn("shrink-0 rounded-full px-2.5 py-1 text-xs font-medium", STATUS_TONE[detail.status])}>
|
||||
{STATUS_LABEL[detail.status]}
|
||||
</span>
|
||||
</header>
|
||||
<MobileDetailHeader
|
||||
title={displayCode}
|
||||
subtitle="报销详情"
|
||||
fallbackTo={reimbursementFallback(detail.status)}
|
||||
status={
|
||||
<span className={cn("rounded-full px-2.5 py-1 text-xs font-medium", STATUS_TONE[detail.status])}>
|
||||
{STATUS_LABEL[detail.status]}
|
||||
</span>
|
||||
}
|
||||
testId="mobile-reimbursement-detail-header"
|
||||
/>
|
||||
|
||||
<section className="mobile-card">
|
||||
<div className="text-xs text-zinc-500">合计金额</div>
|
||||
@@ -828,49 +832,6 @@ export function MobileReimbursementDetailPage() {
|
||||
{detail.description ? <p className="mt-3 text-sm leading-6 text-zinc-600">{detail.description}</p> : null}
|
||||
</section>
|
||||
|
||||
{detail.status === "reviewing" || detail.approvalHistory.length ? (
|
||||
<section className="mobile-card" data-testid="mobile-reimbursement-approval-history">
|
||||
<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>
|
||||
</div>
|
||||
{canRemindPendingWorkflow && pendingTaskId ? (
|
||||
<Button
|
||||
size="sm"
|
||||
type="button"
|
||||
variant="secondary"
|
||||
disabled={remindMutation.isPending}
|
||||
onClick={() => remindMutation.mutate(pendingTaskId)}
|
||||
>
|
||||
{remindMutation.isPending ? "催办中..." : "催办"}
|
||||
</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">{formatDateTime(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>
|
||||
)}
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
<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>
|
||||
@@ -1139,6 +1100,49 @@ export function MobileReimbursementDetailPage() {
|
||||
)}
|
||||
</section>
|
||||
|
||||
{detail.status === "reviewing" || detail.approvalHistory.length ? (
|
||||
<section className="mobile-card" data-testid="mobile-reimbursement-approval-history">
|
||||
<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>
|
||||
</div>
|
||||
{canRemindPendingWorkflow && pendingTaskId ? (
|
||||
<Button
|
||||
size="sm"
|
||||
type="button"
|
||||
variant="secondary"
|
||||
disabled={remindMutation.isPending}
|
||||
onClick={() => remindMutation.mutate(pendingTaskId)}
|
||||
>
|
||||
{remindMutation.isPending ? "催办中..." : "催办"}
|
||||
</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">{formatDateTime(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>
|
||||
)}
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{showSecondaryActions ? (
|
||||
<section className="mobile-card" data-testid="mobile-reimbursement-actions">
|
||||
<h2 className="text-base font-semibold text-zinc-900">操作</h2>
|
||||
|
||||
在新工单中引用
屏蔽一个用户