diff --git a/apps/web/src/app/gov-shell.tsx b/apps/web/src/app/gov-shell.tsx index ddbf56f..b1b399a 100644 --- a/apps/web/src/app/gov-shell.tsx +++ b/apps/web/src/app/gov-shell.tsx @@ -23,6 +23,7 @@ import { SURFACE_META, SURFACE_ZONES, canSeeSurface } from '@web-shared/gov/surf import type { Surface } from '@web-shared/gov/surfaces.ts'; import { useGovObjects, useGovTasks } from '@web-shared/gov/gov-api.ts'; import { queryKeys } from './query.ts'; +import { BlueprintHome } from '@features/gov-blueprint'; import { TasksHome, TaskLogPage, TaskPage, WfPage } from '@features/gov-task'; import { EmpPage, PeopleHome, RolePage } from '@features/gov-staff'; import { EdgesPage, PkgPage, WorkflowsHome } from '@features/gov-process'; @@ -317,16 +318,6 @@ function LedgerRail({ ); } -/** 企业蓝图 —— Task 6 接真;在那之前先给设计者一句「这里将有什么」 */ -function BlueprintPlaceholder() { - return ( - - 这里将把四库串成一条因果链:流程包(战略)→ 活动库(方法)→ 工具箱(能力)→ 数据域(对象), - 并把「哪里还没定义完」聚成一张待补清单。先从左轨进各库编排。 - - ); -} - /** 台账 —— Task 10 接真;导航项已随数据域长出来了,内容随后 */ function LedgersPlaceholder() { return 这里将按域列出全部台账对象、行数据与版本。域的结构在设计中心 · 数据域维护。; @@ -335,7 +326,7 @@ function LedgersPlaceholder() { function ZoneHome({ zone, tab, nav }: { zone: GovZone; tab: string; nav: Nav }) { const P = { // 设计中心 - blueprint: BlueprintPlaceholder, + blueprint: BlueprintHome, workflows: WorkflowsHome, library: LibraryHome, tools: ToolsHome, diff --git a/apps/web/src/features/gov-blueprint/index.ts b/apps/web/src/features/gov-blueprint/index.ts new file mode 100644 index 0000000..860cf43 --- /dev/null +++ b/apps/web/src/features/gov-blueprint/index.ts @@ -0,0 +1,5 @@ +/** + * feature `gov-blueprint` 唯一出口(编码规范 §9.1)。 + * 企业蓝图 —— 设计中心首页:四库因果链与待补清单。 + */ +export { BlueprintHome } from './internal/panel.tsx'; diff --git a/apps/web/src/features/gov-blueprint/internal/panel.tsx b/apps/web/src/features/gov-blueprint/internal/panel.tsx new file mode 100644 index 0000000..16b9b81 --- /dev/null +++ b/apps/web/src/features/gov-blueprint/internal/panel.tsx @@ -0,0 +1,169 @@ +/** + * feature `gov-blueprint` —— 设计中心首页:企业蓝图。 + * + * 一屏回答两个问题:**这家企业被定义成什么样**,**哪里还没定义完**。 + * + * 因果链:流程包(战略)→ 活动库(方法)→ 工具箱(能力)→ 数据域(对象)。 + * 四库各自有页,但「为什么是这四库、它们怎么相互决定」得有一屏能讲 —— 就是这一屏。 + * + * 这里**不出现任何运行态数字**(在跑几单、30 天起单数、死信数)。 + * 设计者看的是定义完不完整,不是今天忙不忙 —— 混进运行数字,这页就退化成第二个驾驶舱。 + */ +import { Clear, Entry, Kpi, Sec, Todo } from '@web-shared/gov/primitives.tsx'; +import type { Nav } from '@web-shared/gov/gov-route.ts'; +import { + useGovActivities, + useGovObjects, + useGovPackages, + useGovPkgReports, + useGovTools, + useGovTriggers, +} from '@web-shared/gov/gov-api.ts'; + +export type HomeProps = { tab: string; nav: Nav }; + +export function BlueprintHome({ nav }: HomeProps) { + const pkgs = useGovPackages(); + const acts = useGovActivities(); + const tools = useGovTools(); + const objs = useGovObjects(); + const trgs = useGovTriggers(); + + const packages = pkgs.data?.packages ?? []; + const reports = useGovPkgReports(packages); + const reportOf = (id: string, version: number) => reports.data?.get(`${id}@${version}`); + + const drafts = packages.filter((p) => p.state !== 'published'); + const draftBad = drafts.filter((p) => reportOf(p.id, p.version)?.publishable === false); + const activities = acts.data?.activities ?? []; + const toolRows = tools.data?.tools ?? []; + const objectRows = objs.data?.objects ?? []; + const domains = [...new Set(objectRows.map((o) => o.domain))]; + const triggers = trgs.data?.triggers ?? []; + + /** 编排里引用了、但活动库装不出来的活动 —— 图画得通,活儿没人干 */ + const referenced = [ + ...new Set( + packages.flatMap((p) => + p.nodes.flatMap((n) => (n.kind === 'activity' && n.activityId !== undefined ? [n.activityId] : [])), + ), + ), + ]; + const missingActivities = referenced.filter((id) => !activities.some((a) => a.activityId === id)); + + /** 没有任何触发器指向的流程包 —— 定义完整,但没人调得起来 */ + const uncalled = [...new Set(packages.map((p) => p.id))].filter( + (id) => !triggers.some((t) => t.processPackageId === id), + ); + + const gaps = draftBad.length + missingActivities.length + uncalled.length; + + // 因果链那句话写在区说明里(ZM.blueprint.desc),此处不重复 —— 直接上四库读数 + return ( + <> +
+ + + t.effect === 'read').length} · 写类 ${toolRows.filter((t) => t.effect === 'write').length}`} + /> + +
+ + + {gaps === 0 ? ( + 没有缺口 —— 流程包全部闭合,引用的活动都装好了,每条流也都有触发器调得起来。 + ) : ( +
+ {draftBad.map((p) => ( + v.message).join(';') || + '存在未通过闭合校验的声明' + } + acts={[ + { label: '去编排', ico: 'route', onClick: () => nav.push({ k: 'pkg', id: `${p.id}@${p.version}` }) }, + ]} + /> + ))} + {missingActivities.map((id) => ( + nav.jump('library', 'shelf') }]} + /> + ))} + {uncalled.map((id) => ( + p.id === id)?.name ?? id} 没有任何调起规则`} + desc="流程定义完整,但没人调得起来 —— 给它配一条周期 / 事件 / 登记触发器。" + acts={[{ label: '去配规则', ico: 'zap', onClick: () => nav.jump('workflows', 'rules') }]} + /> + ))} +
+ )} +
+ + +
+ nav.jump('workflows', 'pkgs')} + /> + nav.jump('library', 'shelf')} + /> + nav.jump('tools', 'catalog')} + /> + nav.jump('data', 'domains')} + /> +
+
+ + ); +}