/** * 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 { domainShort, sortDomains } from '@web-shared/gov/domains.ts'; export type HomeProps = { tab: string; nav: Nav }; /* ==================== 设计侧:数据域(定义面) ==================== */ /** * 只呈现**结构**:域分组 → 对象 → 列与主键。 * 行数与行浏览入口不在这里 —— 那在运营中心的台账。 */ export function DataHome({ nav }: HomeProps) { const q = useGovObjects(); const objects = q.data?.objects ?? []; const domains = sortDomains([...new Set(objects.map((o) => o.domain))]); return ( <> 数据不设箱、靠域:域是承载的划分,对象由活动声明物化——最大化合并、只增不破坏。 这里改结构;行数据在运营中心 · 台账看。 {domains.map((d) => (
{domainShort(d)} {d} {objects.filter((o) => o.domain === d).length} 个对象 · 发布即入运营台账
{objects .filter((o) => o.domain === d) .map((o) => ( ))}
))} {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 ( <> {domainShort(domain)}域({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('.'); const objects = useGovObjects(); const page = useGovLedger(domain, object); const known = (objects.data?.objects ?? []).some((o) => o.domain === domain && o.object === object); if (!known) return 对象未物化。; const columns = page.data?.columns ?? []; return ( <> {columns.length} 列 {page.data?.total ?? 0} 行 主键 {(page.data?.primaryKey ?? []).join(', ') || '—'} } desc="声明物化:最大化合并、只增不破坏——不删列、不改窄、不换主键。此处只读正式版,跨任务只见过门的事实。" />
{columns.map((c) => (
{c.name} {c.type}
))}
{(page.data?.rows ?? []).length === 0 ? ( 这张表还没有记录 —— 任务放行后,过门的事实才落进来。 ) : (
{columns.map((c) => ( ))} {(page.data?.rows ?? []).map((row, i) => ( {columns.map((c) => ( ))} ))}
{c.name}
{String(row[c.name] ?? '')}
)}
); }