重构 Wave3 详情与移动体验(05-28-frontend-ux-wave3-business-mobile)

这个提交包含在:
2026-05-29 07:57:17 +08:00
父节点 85c298e25a
当前提交 2b5fd6b1b7
共修改 17 个文件,包含 1087 行新增和 32 行删除
+54 -4
查看文件
@@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
import { Link, useParams } from "react-router-dom";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { fetchEmployees } from "../api/employees";
import { archiveProject, createConfidentialDocumentRef, createProjectCommunicationLog, createProjectDelivery, createProjectMember, createProjectMilestone, createProjectRisk, createProjectTask, fetchProject, fetchProjectClosureChecklist, fetchProjectCommunicationLogs, fetchProjectDeliveries, fetchProjectDocumentRefs, fetchProjectExpensesSummary, fetchProjectMembers, fetchProjectMilestones, fetchProjectPurchaseSummary, fetchProjectRisks, fetchProjectTasks, fetchProjectTravelSummary, fetchProjectWeeklyReports, patchProject, patchProjectClosureChecklistItem, patchProjectCommunicationLog, patchProjectMember, patchProjectMilestone, patchProjectRisk, patchProjectTask, type ConfidentialityLevel, type DocumentStorageType, type ProjectCommunicationChannel, type ProjectClosureChecklistStatus, type ProjectDeliveryRecordType, type ProjectMilestoneStatus, type ProjectRiskLevel, type ProjectStatus, type ProjectTaskPriority, type ProjectTaskStatus } from "../api/projects";
import { archiveProject, createConfidentialDocumentRef, createProjectCommunicationLog, createProjectDelivery, createProjectMember, createProjectMilestone, createProjectRisk, createProjectTask, fetchProject, fetchProjectClosureChecklist, fetchProjectCommunicationLogs, fetchProjectDeliveries, fetchProjectDocumentRefs, fetchProjectExpensesSummary, fetchProjectMembers, fetchProjectMilestones, fetchProjectPurchaseSummary, fetchProjectRisks, fetchProjectTasks, fetchProjectTravelSummary, fetchProjectWeeklyReports, patchProject, patchProjectClosureChecklistItem, patchProjectCommunicationLog, patchProjectMember, patchProjectMilestone, patchProjectRisk, patchProjectTask, type ConfidentialityLevel, type DocumentStorageType, type ProjectCommunicationChannel, type ProjectClosureChecklistStatus, type ProjectDeliveryRecordType, type ProjectDetail, type ProjectMilestoneStatus, type ProjectRiskLevel, type ProjectStatus, type ProjectTaskPriority, type ProjectTaskStatus } from "../api/projects";
import { fetchContractsRegistry } from "../api/contractsRegistry";
import { mapApiErrorMessage } from "../api/errorMessages";
import { DetailSideMeta } from "../components/patterns/DetailSideMeta";
@@ -136,6 +136,30 @@ function formatDateTime(value: string | null) {
return formatDisplayDateTime(value);
}
function resolveProjectNextAction(detail: ProjectDetail) {
if (detail.status === "archived") {
return "项目已归档,主要用于查询合同、交付、费用和资料引用。";
}
if (detail.closureSummary.pendingCount > 0) {
return `优先补齐 ${detail.closureSummary.pendingCount} 项收口清单,再执行完成或归档。`;
}
if (detail.closureSummary.facts.openRiskCount > 0) {
return `当前有 ${detail.closureSummary.facts.openRiskCount} 个未关闭风险,建议指定责任人和截止时间。`;
}
if (detail.counts.milestones > 0 && detail.status === "active") {
return "按里程碑和任务推进项目,客户沟通、交付记录和周报都在本页留痕。";
}
if (detail.status === "draft") {
return "先补齐负责人、项目经理、时间范围和摘要,再进入执行。";
}
return "当前项目可继续维护执行记录、费用关联和归档资料引用。";
}
function resolveProjectEvidenceText(detail: ProjectDetail) {
const facts = detail.closureSummary.facts;
return `交付 ${facts.deliveryRecordCount} · 合同 ${facts.contractCount} · 资料引用 ${facts.confidentialDocRefCount}`;
}
export function ProjectDetailPage() {
const { projectId = "" } = useParams();
const queryClient = useQueryClient();
@@ -538,13 +562,15 @@ export function ProjectDetailPage() {
const milestoneItems = milestonesQuery.data?.items ?? detail.milestones;
const taskItems = tasksQuery.data?.items ?? detail.tasks;
const riskItems = risksQuery.data?.items ?? detail.risks;
const projectNextAction = resolveProjectNextAction(detail);
const projectEvidenceText = resolveProjectEvidenceText(detail);
return (
<>
<PageScaffold>
<EntityOverviewHero
title={`${detail.projectNo} · ${detail.name}`}
subtitle="项目概览、执行、留痕与归档在同一工作台内查看和处理。"
subtitle="项目状态、下一步、执行留痕和归档边界集中在一个工作台内处理。"
meta={
<>
<StatusBadge status={detail.status} />
@@ -569,7 +595,31 @@ export function ProjectDetailPage() {
}
/>
<section className="grid gap-4 md:grid-cols-2 xl:grid-cols-6">
<section className="detail-command-panel" data-testid="project-current-command">
<div className="grid gap-4 xl:grid-cols-[minmax(0,1.1fr),minmax(260px,0.9fr)]">
<div className="min-w-0">
<div className="text-xs font-semibold text-zinc-500">当前办理焦点</div>
<p className="mt-1 text-sm leading-6 text-zinc-700">{projectNextAction}</p>
<div className="mt-3 flex flex-wrap gap-2">
<span className="info-chip">状态:{projectStatusLabel(detail.status)}</span>
<span className="info-chip">证据:{projectEvidenceText}</span>
{detail.isConfidential ? <span className="badge-warning">涉密资料仅登记引用</span> : null}
</div>
</div>
<div className="detail-fact-grid">
<div className="detail-fact-card">
<div className="detail-fact-card__label">收口待处理</div>
<div className="detail-fact-card__value">{detail.closureSummary.pendingCount}</div>
</div>
<div className="detail-fact-card">
<div className="detail-fact-card__label">未关闭风险</div>
<div className="detail-fact-card__value">{detail.closureSummary.facts.openRiskCount}</div>
</div>
</div>
</div>
</section>
<section className="ops-status-strip grid gap-4 md:grid-cols-2 xl:grid-cols-4">
<MetricCard title="成员" value={counts?.members ?? 0} note="执行角色与观察者" />
<MetricCard title="里程碑" value={counts?.milestones ?? 0} note="用于跟踪关键节点" />
<MetricCard title="任务" value={counts?.tasks ?? 0} note="按优先级与状态推进" />
@@ -580,7 +630,7 @@ export function ProjectDetailPage() {
</section>
{(detail.status === "completed" || detail.status === "archived") && detail.closureSummary.pendingCount > 0 ? (
<div className="card-std border-amber-200 bg-amber-50 p-4" data-testid="project-closure-warning">
<div className="ops-inline-alert border-amber-200 bg-amber-50 text-amber-800" data-testid="project-closure-warning">
<div className="font-medium text-zinc-900">项目已进入收口状态,但仍有 {detail.closureSummary.pendingCount} 项待处理</div>
<div className="mt-1 text-sm text-zinc-600">本轮是软约束:允许完成或归档,但请尽快补齐交付、费用、合同与交接说明。</div>
</div>