统一业务台账首屏设计(05-30-unified-page-design-review)

这个提交包含在:
2026-05-30 19:54:22 +08:00
父节点 8423ca63b5
当前提交 d5ef8189f1
共修改 15 个文件,包含 121 行新增和 446 行删除
+4 -40
查看文件
@@ -14,8 +14,7 @@ import { useAuth } from "../auth/AuthContext";
import { hasAnyPermission } from "../auth/permissions";
import { DateTextInput } from "../components/common/DateTextInput";
import { StatusBadge } from "../components/common/StatusBadge";
import { FilterBar, ListWorkspace, StatStrip, WorkspaceHeader, WorkspaceNotice } from "../components/patterns/ListWorkspace";
import { MetricCard } from "../components/patterns/MetricCard";
import { FilterBar, ListWorkspace, WorkspaceHeader, WorkspaceNotice } from "../components/patterns/ListWorkspace";
import { SectionPanel } from "../components/patterns/SectionPanel";
import { Button } from "../components/ui/button";
import { Input } from "../components/ui/input";
@@ -123,20 +122,6 @@ export function ProjectsPage() {
});
}, [projectItems, showTrialData]);
const hiddenTrialProjectCount = projectItems.length - visibleProjectItems.length;
const projectStats = useMemo(() => {
return visibleProjectItems.reduce(
(acc, item) => {
acc.total += 1;
if (item.status === "active") {
acc.active += 1;
}
acc.overdueMilestones += item.counts.overdueMilestones;
acc.openRisks += item.counts.openRisks;
return acc;
},
{ total: 0, active: 0, overdueMilestones: 0, openRisks: 0 },
);
}, [visibleProjectItems]);
const createMutation = useMutation({
mutationFn: () =>
@@ -188,7 +173,6 @@ export function ProjectsPage() {
<ListWorkspace>
<WorkspaceHeader
title="项目"
subtitle="轻项目主档、成员、里程碑、任务与风险统一在 OA 内跟进。"
actions={
<div className="flex gap-2">
<Button asChild variant="secondary" type="button">
@@ -201,15 +185,8 @@ export function ProjectsPage() {
}
/>
<StatStrip columns={4}>
<MetricCard title="我相关项目" value={projectStats.total} note={mine ? "默认只看我负责/我参与" : "已切换到全范围"} />
<MetricCard title="进行中" value={projectStats.active} note="优先跟进活跃项目" />
<MetricCard title="逾期里程碑" value={projectStats.overdueMilestones} note="延期事项待处理" tone={projectStats.overdueMilestones > 0 ? "warning" : "neutral"} />
<MetricCard title="未关闭风险" value={projectStats.openRisks} note="问题留痕建议绑定责任人" tone={projectStats.openRisks > 0 ? "danger" : "neutral"} />
</StatStrip>
{showLedgerTools ? (
<SectionPanel title="台账工具" description="新建项目、显示测试数据和全范围查看放在这里,避免普通首屏变成配置台。">
<SectionPanel title="台账工具">
<div className="grid gap-5 xl:grid-cols-[minmax(0,1fr),260px]">
<div>
{!canManageProjects ? (
@@ -274,14 +251,12 @@ export function ProjectsPage() {
<Button type="button" variant={showTrialData ? "default" : "secondary"} onClick={() => setShowTrialData((value) => !value)}>
{showTrialData ? "隐藏测试数据" : "显示测试数据"}
</Button>
<p>试运行默认隐藏 TEST/SMOKE 项目。全范围查看请使用筛选区的范围按钮,避免普通项目负责人默认进入公司全量项目。</p>
</div>
</div>
</SectionPanel>
) : null}
<div className="page-shell-grid">
<div className="space-y-6">
<div className="space-y-6">
<FilterBar className="grid gap-3 md:grid-cols-[minmax(0,1fr),180px,160px]">
<Input placeholder="搜索项目编号、项目名、客户名" value={q} onChange={(event) => updateSearch({ q: event.target.value })} />
<select className="select-std" value={status} onChange={(event) => updateSearch({ status: event.target.value })}>
@@ -302,10 +277,7 @@ export function ProjectsPage() {
</WorkspaceNotice>
) : null}
<SectionPanel
title="项目列表"
description={visibleProjectItems.length ? `当前显示 ${visibleProjectItems.length} 个项目` : "按项目名、客户或状态快速缩小范围"}
>
<SectionPanel title="项目列表">
<div className="space-y-4">
{listQuery.isLoading ? <div className="text-sm text-zinc-500">项目加载中...</div> : null}
{listQuery.isError ? <div className="text-sm text-red-600">{resolveError(listQuery.error, "加载项目失败")}</div> : null}
@@ -341,14 +313,6 @@ export function ProjectsPage() {
</div>
</div>
</SectionPanel>
</div>
<SectionPanel title="下一步提示" description="项目页只保留轻台账和详情入口。">
<div className="space-y-3 text-sm text-zinc-600">
<p>项目列表用于看状态、风险、负责人和进入详情;成员、里程碑、风险、交付记录和关联业务在详情页处理。</p>
<p>合同、采购、报销和付款只展示摘要与跳转,不在项目列表直接处理对方模块动作。</p>
</div>
</SectionPanel>
</div>
</ListWorkspace>
);