forked from ChaiHao/BPOS-MVP
三面拆分⑫:UI 打磨 —— 补 10 枚缺失图标,蓝图改因果链,台账收成一个模块
图标注册表缺 arrow-left-right/columns/table/tag 等 10 名,用到即渲染成空 SVG —— 补齐; 蓝图四库改真箭头相连的因果链节点卡(卡即入口,删重复的入库段); 驾驶舱终态分布改五色堆叠条;运营数据收成左轨一枚「台账」,域下沉为 2-3 字短名页签 (受理→寻源→定价→议价→替代→共享,按流程序),进模块自动落首域。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -21,6 +21,7 @@ import type { GovZone, Nav, Route } from '@web-shared/gov/gov-route.ts';
|
||||
import { SURFACE_META, SURFACE_ZONES, canSeeSurface } from '@web-shared/gov/surfaces.ts';
|
||||
import type { Surface } from '@web-shared/gov/surfaces.ts';
|
||||
import { useGovObjects, useGovTasks } from '@web-shared/gov/gov-api.ts';
|
||||
import { domainShort, sortDomains } from '@web-shared/gov/domains.ts';
|
||||
import { queryKeys } from './query.ts';
|
||||
import { BlueprintHome } from '@features/gov-blueprint';
|
||||
import { TasksHome, TaskLogPage, TaskPage, WfPage } from '@features/gov-task';
|
||||
@@ -126,17 +127,19 @@ export function GovShell({ surface }: { surface: Surface }) {
|
||||
(tasks.data?.workflows ?? []).filter((w) => w.state === 'abnormal').length +
|
||||
(tasks.data?.tasks ?? []).filter((t) => t.terminal !== undefined && t.terminal !== 'released').length;
|
||||
|
||||
/** 台账的导航项按**已有数据域**长出来 —— 设计侧一发布新域,运营侧自动多一位 */
|
||||
const domains = [...new Set((objects.data?.objects ?? []).map((o) => o.domain))].sort();
|
||||
/** 台账的域页签按**已有域**长出来 —— 设计侧一发布新域,台账里自动多一个页签 */
|
||||
const domains = sortDomains([...new Set((objects.data?.objects ?? []).map((o) => o.domain))]);
|
||||
// 进台账没带域(左轨点进来)时落到首域 —— 页签与内容同一判定
|
||||
const effectiveTab = zone === 'ledgers' && tab === '' ? (domains[0] ?? '') : tab;
|
||||
|
||||
/* 面包屑:台面 › 区 › 页签 › 下钻各级 */
|
||||
const crumbs: Array<{ label: string; go?: () => void }> = [
|
||||
{ label: SURFACE_META[surface].brand, go: () => goZone(zone) },
|
||||
];
|
||||
crumbs.push({ label: meta.title, go: () => go({ zone, tab, stack: [] }) });
|
||||
// 台账区的页签名就是域名 —— 静态表里查不到,直接用它本身
|
||||
// 台账区的页签名就是域键 —— 静态表里查不到,呈现走人读短名
|
||||
crumbs.push({
|
||||
label: TABS[zone].find((t) => t.key === tab)?.label ?? tab,
|
||||
label: TABS[zone].find((t) => t.key === tab)?.label ?? (zone === 'ledgers' ? domainShort(effectiveTab) : tab),
|
||||
go: () => go({ zone, tab, stack: [] }),
|
||||
});
|
||||
stack.forEach((r, i) =>
|
||||
@@ -154,24 +157,14 @@ export function GovShell({ surface }: { surface: Surface }) {
|
||||
<Ico name={surface === 'design' ? 'route' : 'gauge'} s={20} />
|
||||
</div>
|
||||
<div className="g-nav-brand">{SURFACE_META[surface].brand}</div>
|
||||
{ZONES.filter((z) => SURFACE_ZONES[surface].includes(z.key)).map((z) =>
|
||||
// 台账不是一个固定位,是一段**随数据域生长**的动态位 —— 每域一枚
|
||||
z.key === 'ledgers' ? (
|
||||
<LedgerRail
|
||||
key={z.key}
|
||||
domains={domains}
|
||||
active={zone === 'ledgers' ? tab : undefined}
|
||||
onPick={(domain) => go({ zone: 'ledgers', tab: domain, stack: [] })}
|
||||
/>
|
||||
) : (
|
||||
<button key={z.key} className={'g-nav-b' + (zone === z.key ? ' on' : '')} onClick={() => goZone(z.key)}>
|
||||
<span className="g-nav-ico">
|
||||
<Ico name={z.icon} s={18} />
|
||||
</span>
|
||||
<span>{z.label}</span>
|
||||
</button>
|
||||
),
|
||||
)}
|
||||
{ZONES.filter((z) => SURFACE_ZONES[surface].includes(z.key)).map((z) => (
|
||||
<button key={z.key} className={'g-nav-b' + (zone === z.key ? ' on' : '')} onClick={() => goZone(z.key)}>
|
||||
<span className="g-nav-ico">
|
||||
<Ico name={z.icon} s={18} />
|
||||
</span>
|
||||
<span>{z.label}</span>
|
||||
</button>
|
||||
))}
|
||||
<div className="g-nav-foot">
|
||||
<span className="g-nav-ava">{(me?.name ?? '—').slice(0, 1)}</span>
|
||||
<span>{me?.name ?? '未登录'}</span>
|
||||
@@ -242,18 +235,31 @@ export function GovShell({ surface }: { surface: Surface }) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="g-tabs">
|
||||
{TABS[zone].map((t) => (
|
||||
<button
|
||||
key={t.key}
|
||||
className={'g-tab' + (tab === t.key ? ' on' : '')}
|
||||
onClick={() => go({ zone, tab: t.key, stack: [] })}
|
||||
>
|
||||
<Ico name={t.ico} s={13} />
|
||||
{t.label}
|
||||
</button>
|
||||
))}
|
||||
{zone === 'ledgers'
|
||||
? // 台账的页签是**数据域**(短名 2-3 字),随发布生长 —— 设计产生运营的形状,落在页签上
|
||||
domains.map((d) => (
|
||||
<button
|
||||
key={d}
|
||||
className={'g-tab' + (effectiveTab === d ? ' on' : '')}
|
||||
title={`数据域 · ${d}`}
|
||||
onClick={() => go({ zone, tab: d, stack: [] })}
|
||||
>
|
||||
<Ico name="table" s={13} />
|
||||
{domainShort(d)}
|
||||
</button>
|
||||
))
|
||||
: TABS[zone].map((t) => (
|
||||
<button
|
||||
key={t.key}
|
||||
className={'g-tab' + (tab === t.key ? ' on' : '')}
|
||||
onClick={() => go({ zone, tab: t.key, stack: [] })}
|
||||
>
|
||||
<Ico name={t.ico} s={13} />
|
||||
{t.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<ZoneHome zone={zone} tab={tab} nav={nav} />
|
||||
<ZoneHome zone={zone} tab={effectiveTab} nav={nav} />
|
||||
</>
|
||||
) : (
|
||||
<div className="g-detail" key={`${stack.length}:${top.k}:${top.id}`}>
|
||||
@@ -266,57 +272,6 @@ export function GovShell({ surface }: { surface: Surface }) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 台账段 —— 左轨里唯一的**动态位**:一域一枚,随数据域发布而生长。
|
||||
*
|
||||
* 这是「设计产生运营的形状」最直观的一处:导航本身就是被设计出来的。
|
||||
*/
|
||||
function LedgerRail({
|
||||
domains,
|
||||
active,
|
||||
onPick,
|
||||
}: {
|
||||
domains: readonly string[];
|
||||
active: string | undefined;
|
||||
onPick: (domain: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<div className="g-nav-brand" style={{ marginTop: 8, marginBottom: 4 }}>
|
||||
台账
|
||||
</div>
|
||||
{domains.length === 0 ? (
|
||||
<div className="g-nav-brand" style={{ fontWeight: 600, letterSpacing: 0, lineHeight: 1.5 }}>
|
||||
随数据域
|
||||
<br />
|
||||
发布生长
|
||||
</div>
|
||||
) : (
|
||||
domains.map((d) => (
|
||||
<button
|
||||
key={d}
|
||||
className={'g-nav-b' + (active === d ? ' on' : '')}
|
||||
onClick={() => onPick(d)}
|
||||
title={`台账 · ${d}`}
|
||||
>
|
||||
<span className="g-nav-ico">
|
||||
<Ico name="database" s={18} />
|
||||
</span>
|
||||
{/* 域名是 `活动_环节` 这种复合键,60px 的轨放不下一行 —— 按下划线断行,窄字号铺开 */}
|
||||
<span style={{ fontSize: 10, lineHeight: 1.25, wordBreak: 'break-word' }}>
|
||||
{d.split('_').map((seg, i) => (
|
||||
<span key={seg} style={{ display: 'block' }}>
|
||||
{i === 0 && d.includes('_') ? `${seg}_` : seg}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
</button>
|
||||
))
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ZoneHome({ zone, tab, nav }: { zone: GovZone; tab: string; nav: Nav }) {
|
||||
const P = {
|
||||
// 设计中心
|
||||
|
||||
Reference in New Issue
Block a user