diff --git a/apps/web/src/app/gov-shell.tsx b/apps/web/src/app/gov-shell.tsx index b1b399a..917a55b 100644 --- a/apps/web/src/app/gov-shell.tsx +++ b/apps/web/src/app/gov-shell.tsx @@ -16,7 +16,6 @@ import type { Session } from '@contracts'; import { logoutRoute, sessionRoute } from '@contracts'; import { call } from '@web-shared/typed-fetch.ts'; import { Ico } from '@web-shared/Ico.tsx'; -import { Note } from '@web-shared/gov/primitives.tsx'; import { TABS, ZM, ZONES, routeLabel } from '@web-shared/gov/gov-route.ts'; import type { GovZone, Nav, Route } from '@web-shared/gov/gov-route.ts'; import { SURFACE_META, SURFACE_ZONES, canSeeSurface } from '@web-shared/gov/surfaces.ts'; @@ -30,7 +29,7 @@ import { EdgesPage, PkgPage, WorkflowsHome } from '@features/gov-process'; import { TriggerPage } from '@features/gov-trigger'; import { ActPage, LibraryHome } from '@features/gov-activity'; import { ToolPage, ToolsHome } from '@features/gov-tool'; -import { DataHome, ObjPage } from '@features/gov-data'; +import { DataHome, LedgersHome, ObjPage } from '@features/gov-data'; import { CockpitHome, KnowledgeHome, MemPage } from '@features/gov-memory'; import { ChanPage, LlmPage, SystemHome } from '@features/gov-system'; @@ -318,11 +317,6 @@ function LedgerRail({ ); } -/** 台账 —— Task 10 接真;导航项已随数据域长出来了,内容随后 */ -function LedgersPlaceholder() { - return 这里将按域列出全部台账对象、行数据与版本。域的结构在设计中心 · 数据域维护。; -} - function ZoneHome({ zone, tab, nav }: { zone: GovZone; tab: string; nav: Nav }) { const P = { // 设计中心 @@ -335,7 +329,7 @@ function ZoneHome({ zone, tab, nav }: { zone: GovZone; tab: string; nav: Nav }) cockpit: CockpitHome, tasks: TasksHome, people: PeopleHome, - ledgers: LedgersPlaceholder, + ledgers: LedgersHome, knowledge: KnowledgeHome, system: SystemHome, }[zone]; diff --git a/apps/web/src/features/gov-data/index.ts b/apps/web/src/features/gov-data/index.ts index 70f026b..e0aa4dc 100644 --- a/apps/web/src/features/gov-data/index.ts +++ b/apps/web/src/features/gov-data/index.ts @@ -1,5 +1,5 @@ /** * feature `gov-data` 唯一出口(编码规范 §9.1)。 - * 数据管理:域 / 对象目录、schema 视图、记录浏览 + * 数据的两副面孔:结构定义在设计中心(数据域),行数据在运营中心(台账 · 某域)。 */ -export { DataHome, ObjPage } from './internal/panel.tsx'; +export { DataHome, LedgersHome, ObjPage } from './internal/panel.tsx'; diff --git a/apps/web/src/features/gov-data/internal/panel.tsx b/apps/web/src/features/gov-data/internal/panel.tsx index 736dbfc..087f684 100644 --- a/apps/web/src/features/gov-data/internal/panel.tsx +++ b/apps/web/src/features/gov-data/internal/panel.tsx @@ -1,61 +1,138 @@ /** - * feature `gov-data` —— 数据管理:域 / 对象目录、schema 视图、记录浏览 + * feature `gov-data` —— 数据的两副面孔,各归其台: + * + * `DataHome`(设计中心 · 数据域):**定义面** —— 域 × 对象的结构:有哪些列、主键是什么、谁声明了它。 + * `LedgersHome`(运营中心 · 台账 · 某域):**运营面** —— 这张表现在有多少行、里面是什么。 + * + * 分开的理由:改 schema 和查行数据是两种心智。 + * 设计者问「这个对象定义全不全」,运营者问「今天这张表有几条」—— + * 摞在一屏,设计页就会变成一个查数据的地方,行数把结构挤到看不见。 * * 自 `~/coding/JCAI_PRD/governance/_components/Governance.tsx` 平移: * DOM 结构、类名(`g-*` + 色调 `t-*`)与文案原样保留,mock 常量换成服务端投影(README 平移规则 1/2/4)。 - * 区首页只负责导航,治理动作一律落在下钻详情页的**就地确认带**(影响预览三必答)。 */ import { Ico } from '@web-shared/Ico.tsx'; import { Card, Chip, Clear, NList, NRow, Note, PageHead, QueryGate, Sec } from '@web-shared/gov/primitives.tsx'; import type { Nav } from '@web-shared/gov/gov-route.ts'; -import { - useGovLedger, - useGovObjects, -} from '@web-shared/gov/gov-api.ts'; +import { useGovLedger, useGovObjects } from '@web-shared/gov/gov-api.ts'; export type HomeProps = { tab: string; nav: Nav }; +/* ==================== 设计侧:数据域(定义面) ==================== */ + +/** + * 只呈现**结构**:域分组 → 对象 → 列与主键。 + * 行数与行浏览入口不在这里 —— 那在运营中心的台账。 + */ export function DataHome({ nav }: HomeProps) { const q = useGovObjects(); const objects = q.data?.objects ?? []; - const domains = [...new Set(objects.map((o) => o.domain))]; + const domains = [...new Set(objects.map((o) => o.domain))].sort(); + return ( - <> - - 数据不设箱、靠域:域是承载的划分,对象由活动声明物化——最大化合并、只增不破坏。 - 数据可以自生长,权力不可以自生长。 - - {domains.map((d) => ( -
-
- - {d} - {objects.filter((o) => o.domain === d).length} 个对象 + <> + + 数据不设箱、靠域:域是承载的划分,对象由活动声明物化——最大化合并、只增不破坏。 + 这里改结构;行数据在运营中心 · 台账看。 + + {domains.map((d) => ( +
+
+ + {d} + {objects.filter((o) => o.domain === d).length} 个对象 · 发布即在运营中心长出一位 +
+ + {objects + .filter((o) => o.domain === d) + .map((o) => ( + + ))} +
- - {objects - .filter((o) => o.domain === d) - .map((o) => ( - nav.push({ k: 'obj', id: `${o.domain}.${o.object}` })} - /> - ))} - -
- ))} - {objects.length === 0 && 尚无物化对象——活动首次写入时按声明物化。} - + ))} + {objects.length === 0 && ( + 还没有物化的对象 —— 活动第一次写入时,系统按它的声明把表建出来,然后出现在这里。 + )} + ); } -/* ---------------- ⑧ 记忆管理 ---------------- */ +/** 一个对象的结构一行:列数 / 主键 —— 点开看完整模式,仍不含行数据 */ +function ObjectSchemaRow({ domain, object, nav }: { domain: string; object: string; nav: Nav }) { + const page = useGovLedger(domain, object); + const columns = page.data?.columns ?? []; + const pk = page.data?.primaryKey ?? []; + return ( + nav.push({ k: 'obj', id: `${domain}.${object}` })} + /> + ); +} + +/* ==================== 运营侧:台账(某一个域) ==================== */ + +/** + * 台账的一个域 —— 壳层把左轨选中的域名当页签传进来。 + * 这里看的是运营事实:这张表现在多少行,点进去逐行看。 + */ +export function LedgersHome({ tab, nav }: HomeProps) { + const q = useGovObjects(); + const domain = tab; + const inDomain = (q.data?.objects ?? []).filter((o) => o.domain === domain); + + return ( + + <> + + {domain} 域的台账:行数据、主键与列结构。结构要改去设计中心 · 数据域; + 这里做的是运营动作 —— 查行、看版本、追引用。 + + {inDomain.length === 0 ? ( + 这个域还没有对象 —— 对象随活动首次写入而物化,建好就出现在这里。 + ) : ( + + {inDomain.map((o) => ( + + ))} + + )} + + + ); +} + +/** 一个台账对象一行:行数是运营侧最想先看到的那个数 */ +function LedgerObjectRow({ domain, object, nav }: { domain: string; object: string; nav: Nav }) { + const page = useGovLedger(domain, object); + const total = page.data?.total; + return ( + 0 ? 'brand' : 'mute'} + ico="table" + title={object} + sub={`${domain}.${object}`} + cols={[ + { k: '行数', v: total === undefined ? '…' : String(total) }, + { k: '列', v: String((page.data?.columns ?? []).length) }, + ]} + right={ 0 ? 'ok' : 'mute'}>{total ?? 0} 行} + onClick={() => nav.push({ k: 'obj', id: `${domain}.${object}` })} + /> + ); +} + +/* ==================== 对象详情(两面共用) ==================== */ export function ObjPage({ id }: { id: string }) { const [domain, object] = id.split('.'); @@ -90,32 +167,33 @@ export function ObjPage({ id }: { id: string }) {
-
- - - - {columns.map((c) => ( - - ))} - - - - {(page.data?.rows ?? []).map((row, i) => ( - + {(page.data?.rows ?? []).length === 0 ? ( + 这张表还没有记录 —— 任务放行后,过门的事实才落进来。 + ) : ( +
+
{c.name}
+ + {columns.map((c) => ( - + ))} - ))} - -
- {String(row[c.name] ?? '')} - {c.name}
-
+ + + {(page.data?.rows ?? []).map((row, i) => ( + + {columns.map((c) => ( + + {String(row[c.name] ?? '')} + + ))} + + ))} + + + + )}
); } - -/* ---------------- 记忆条目 ---------------- */ -