重构 Wave3 详情与移动体验(05-28-frontend-ux-wave3-business-mobile)
这个提交包含在:
@@ -25,6 +25,8 @@ import { PageHeader } from "../components/common/PageHeader";
|
||||
import { StatusBadge } from "../components/common/StatusBadge";
|
||||
import { DateTextInput } from "../components/common/DateTextInput";
|
||||
import { WorkspaceNotice } from "../components/patterns/ListWorkspace";
|
||||
import { MetricCard } from "../components/patterns/MetricCard";
|
||||
import { PageScaffold } from "../components/patterns/PageScaffold";
|
||||
import { Button } from "../components/ui/button";
|
||||
import { Input } from "../components/ui/input";
|
||||
import { Label } from "../components/ui/label";
|
||||
@@ -244,6 +246,10 @@ export function ContractsRegistryPage() {
|
||||
? "保留现有责任人"
|
||||
: "默认当前用户";
|
||||
const amountInvalid = Boolean(editor.amount.trim()) && !AMOUNT_PATTERN.test(editor.amount.trim());
|
||||
const activeContractCount = visibleContracts.filter((item) => item.status === "active").length;
|
||||
const draftContractCount = visibleContracts.filter((item) => item.status === "draft").length;
|
||||
const archivedContractCount = visibleContracts.filter((item) => item.status === "archived").length;
|
||||
const expiringContractCount = expiringQuery.data?.total ?? expiringItems.length;
|
||||
|
||||
const refreshAll = async () => {
|
||||
await Promise.all([
|
||||
@@ -305,10 +311,18 @@ export function ContractsRegistryPage() {
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageScaffold>
|
||||
<PageHeader
|
||||
eyebrow="合同"
|
||||
title="合同台账"
|
||||
subtitle="维护合同元数据、生命周期、到期预警与导出,不在 OA 存放涉密正文附件。"
|
||||
meta={
|
||||
<>
|
||||
<span className="info-chip">可见 {visibleContracts.length} 份</span>
|
||||
<span className="info-chip">30 天到期 {expiringContractCount} 份</span>
|
||||
<span className="info-chip">私密正文不入库</span>
|
||||
</>
|
||||
}
|
||||
actions={
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
@@ -334,12 +348,21 @@ export function ContractsRegistryPage() {
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="card-std space-y-4 p-6">
|
||||
<section className="ops-status-strip grid gap-4 md:grid-cols-2 xl:grid-cols-4" data-testid="contracts-registry-summary">
|
||||
<MetricCard title="可见合同" value={visibleContracts.length} note={filters.projectId ? "已按项目过滤" : "当前筛选结果"} />
|
||||
<MetricCard title="生效中" value={activeContractCount} note="需要关注到期和责任人" />
|
||||
<MetricCard title="草稿" value={draftContractCount} note="补齐摘要、金额和归档位置" tone={draftContractCount > 0 ? "warning" : "neutral"} />
|
||||
<MetricCard title="已归档" value={archivedContractCount} note="只保留台账和线下档案索引" />
|
||||
</section>
|
||||
|
||||
<div className="detail-command-panel space-y-4" data-testid="contracts-registry-command">
|
||||
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div>
|
||||
<div className="text-sm text-zinc-500">到期预警</div>
|
||||
<div className="text-sm text-zinc-500">生命周期焦点</div>
|
||||
<div className="mt-1 text-3xl font-semibold text-zinc-900">{expiringQuery.data?.total ?? 0}</div>
|
||||
<div className="text-sm text-zinc-600">份合同将在 {EXPIRING_DAYS} 天内到期</div>
|
||||
<div className="text-sm text-zinc-600">
|
||||
份合同将在 {EXPIRING_DAYS} 天内到期;续约会生成草稿合同,导出仅用于授权复核。
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
@@ -759,7 +782,7 @@ export function ContractsRegistryPage() {
|
||||
|
||||
<div className="space-y-3">
|
||||
{visibleContracts.map((item) => (
|
||||
<div key={item.id} className="rounded-lg border border-zinc-200 p-4">
|
||||
<div key={item.id} className="detail-evidence-card bg-white" data-testid="contracts-registry-row">
|
||||
<div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
|
||||
<div className="space-y-2">
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
@@ -832,7 +855,7 @@ export function ContractsRegistryPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageScaffold>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户