diff --git a/apps/web/src/app/gov-shell.tsx b/apps/web/src/app/gov-shell.tsx index 8028b72..7aab33a 100644 --- a/apps/web/src/app/gov-shell.tsx +++ b/apps/web/src/app/gov-shell.tsx @@ -38,6 +38,7 @@ import { TestbedHome } from '@features/gov-testbed'; import { CockpitHome } from '@features/gov-memory'; import { ChanPage, LlmPage, ModelsHome, SystemHome } from '@features/gov-system'; import { Btn } from '@web-shared/Btn.tsx'; +import { Card, Note } from '@web-shared/gov/primitives.tsx'; /* ── URL 编解码:`#///[/:…]` ────────────────── */ @@ -292,7 +293,23 @@ export function GovShell({ surface }: { surface: Surface }) { ); } +/** 运营中心四区先摆建设中(2026-08-28 用户定:驾驶舱 / 工作流 / 台账 / 系统)—— + 真面板组件与其下钻路由原样留着,只换区首页;要做回来,把区从这个集合摘掉即可 */ +const WIP_ZONES: ReadonlySet = new Set(['cockpit', 'runs', 'ledgers', 'system']); + +/** 建设中占位:如实说还没有 —— 不摆假数据、不摆点不动的钮(与知识库「待建」页同一条规矩) */ +function WipHome({ zone }: { zone: GovZone }) { + const zm = ZM[zone]; + return ( + + 这一区正在建设中 —— 运营中心目前开放「活动」与「员工」两区,其余区建好后开放。 + + ); +} + function ZoneHome({ zone, tab, nav }: { zone: GovZone; tab: string; nav: Nav }) { + /* 建设中的区先拦在这里(2026-08-28 用户定):真面板组件仍在下方映射里,摘掉集合即恢复 */ + if (WIP_ZONES.has(zone)) return ; const P = { // 设计中心 blueprint: BlueprintHome, diff --git a/apps/web/src/features/gov-task/internal/panel.tsx b/apps/web/src/features/gov-task/internal/panel.tsx index 83cbf81..be70b7a 100644 --- a/apps/web/src/features/gov-task/internal/panel.tsx +++ b/apps/web/src/features/gov-task/internal/panel.tsx @@ -227,7 +227,7 @@ const INSTANCE_PAGE_SIZE = 8; type InstanceFilterKey = 'id' | 'owner' | 'state' | 'time'; const INSTANCE_FILTER_LABEL: Readonly> = { - id: '实例', + id: '活动号', owner: '负责人', state: '状态', time: '启动时间', @@ -240,7 +240,7 @@ type PopAskSpec = { title: string; okLabel: string; danger?: boolean; onOk: () = const errText = (e: unknown): string => (e as { message?: string } | null)?.message ?? '网络错误,请稍后重试'; /** 气泡式二次确认 —— 锚在页头右下的小泡泡:一句话问清,确认/取消两钮;点气泡外的透明罩即取消; - `inrow` = 锚在行内操作格(终止实例用,接口文档 §10),定位由 CSS 的 .inrow 变体给。 + `inrow` = 锚在行内操作格的变体(行内终止撤掉后暂无处用,2026-08-28;变体与 CSS 保留,要用直接传); 2026-08-28 反馈闭环(用户报:终止点了不知道成没成):确认后气泡**不再立即消失** —— 在途时留场转忙碌(两钮禁用、罩不可点),成功由调用方 onSuccess 关掉,失败把原因留在气泡里可重试 */ function PopAsk({ spec, onCancel, inrow, busy, error }: { spec: PopAskSpec; onCancel: () => void; inrow?: boolean; busy?: boolean; error?: string | null }) { @@ -470,7 +470,7 @@ function LaunchDrawer({ activityId, name, onCancel, onLaunch }: { activityId: st ))} - 这次启动的实例归他 —— 负责人验收、数据工单都落到责任人身上;默认是你自己。 + 这次创建的活动归他 —— 负责人验收、数据工单都落到责任人身上;默认是你自己。 {fieldErr['ownerId'] !== undefined && {fieldErr['ownerId']}} @@ -577,8 +577,12 @@ function LaunchDrawer({ activityId, name, onCancel, onLaunch }: { activityId: st * · 启动活动(即创建实例)—— 弹参数抽屉(LaunchDrawer),参数表按活动下发;已禁用不能启动。 * * 2026-08-27 接真接口(docs/API文档-活动模块.md):定义 §2.2 / 清单 §3 / 启动 §4、§5 / 禁用激活终止 §8; - * 沙箱区:view.url + 票据装载,高度听 `jcai:view-height`;view 为 null 藏起整个区块。 + * 沙箱区:view.url + 票据装载,高度听 `jcai:view-height`;2026-08-28 用户定:沙箱区块先隐藏(SHOW_SANDBOX 开关)。 */ + +/** 沙箱区块开关(2026-08-28 用户定:先隐藏)—— 置回 true 即恢复;显式标注 boolean 免得被字面量收窄 */ +const SHOW_SANDBOX: boolean = false; + export function ActvPage({ id, nav }: { id: string; nav: Nav }) { /* 接口对接(2026-08-27,docs/API文档-活动模块.md):定义 §2.2 / 实例清单 §3(检索分页全在服务端)/ 写动作 §5、§8 */ const actQ = useOpsActivity(id); @@ -590,9 +594,9 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) { const [fFrom, setFFrom] = useState(''); const [fTo, setFTo] = useState(''); const [page, setPage] = useState(0); - /* 轻量二次确认(2026-08-26 用户定):头部动作(禁用/激活)走页头气泡;行内「终止活动」走行内气泡(接口文档 §10) */ + /* 轻量二次确认(2026-08-26 用户定):头部动作(禁用/激活)走页头气泡; + 行内「终止活动」与它的气泡已撤(2026-08-28 用户定:终止只留在 L3) */ const [ask, setAsk] = useState(null); - const [rowAsk, setRowAsk] = useState<{ rid: string; no: string } | null>(null); /* 启动抽屉(2026-08-27 用户定) */ const [launch, setLaunch] = useState(false); @@ -632,7 +636,6 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) { const qc = useQueryClient(); const disable = useGovAction(() => call(disableOpsActivityRoute, { params: { activityId: id } }), ['activities']); const activate = useGovAction(() => call(activateOpsActivityRoute, { params: { activityId: id } }), ['activities']); - const terminate = useGovAction((iid: string) => call(terminateOpsInstanceRoute, { params: { activityId: id, instanceId: iid } }), ['activities']); /* 页头写动作的在途/失败态 —— 摆进气泡(2026-08-28 用户报:点了没反馈);同一时刻只有一枚气泡,二选一即可 */ const headBusy = disable.isPending || activate.isPending; const headErr = disable.isError ? errText(disable.error) : activate.isError ? errText(activate.error) : null; @@ -666,7 +669,7 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) { v{a.version} {/* 绑定的 LLM 上头版(2026-08-25 用户定):每个活动一个专属 agent */} {a.llm} - {a.owner?.name ?? '—'} + {/* 负责人小签隐藏(2026-08-28 用户定:活动本身没有负责人;责任人是每次创建的固定参数,在 L3 看) */} 创建于 {atMinute(a.createdAt).slice(0, 5)} } @@ -703,8 +706,8 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) { }); }, }, - /* 启动活动(2026-08-27 接):弹参数抽屉填参启动;已禁用不能启动 —— 先激活,再启动 */ - { label: '启动活动', ico: 'play', primary: st === 'active', disabled: st !== 'active', onClick: () => setLaunch(true) }, + /* 创建活动(2026-08-28 用户改文案,原「启动活动」):弹参数抽屉填参创建实例;已禁用不能创建 —— 先激活 */ + { label: '创建活动', ico: 'play', primary: st === 'active', disabled: st !== 'active', onClick: () => setLaunch(true) }, /* 版本迭代入口先隐藏(2026-08-25 用户定):功能设计回头讨论,要做时把这行加回 acts { label: '版本迭代', ico: 'git-branch', disabled: true }, */ @@ -714,7 +717,8 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) { {launch && setLaunch(false)} onLaunch={onLaunched} />} - + {/* 「历史活动」改「活动列表」(2026-08-28 用户定) */} + {/* 统计带 + 检索条同一行(2026-08-26 用户定:检索放右侧)—— 左:总数 + 五态灰字合计(状态色只进 6px 小圆点);右:条件下拉 + 随条件换控件; 统计数是服务端给的全局 stats,不随检索条件动(2026-08-27 接接口) */} @@ -751,7 +755,7 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) { { setFText(e.target.value); setPage(0); @@ -835,9 +839,10 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) { 2026-08-27 接接口:摆的是 no(人读实例号),寻址(L3/终止)用 id(任务 uuid) */}
- {/* 列序(2026-08-26 用户定):实例 / 负责人 / 输入 / 输出 / 启动时间 / 状态 / 操作(操作栏也有标题) */} + {/* 列序(2026-08-26 用户定):实例 / 负责人 / 输入 / 输出 / 启动时间 / 状态 / 操作(操作栏也有标题); + 表头第一列定名「活动号」(2026-08-28 用户定) */}
- 实例 + 活动号 负责人 输入 输出 @@ -862,38 +867,10 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) { {INSTANCE_STATE_LABEL[r.state]} - {r.state === 'running' && ( - - )} - {/* L3 实例详情(2026-08-26 接):路由 id 带活动与实例两段,详情页按活动找回上下文 */} + {/* 行内「终止活动」撤掉(2026-08-28 用户定):终止只留在 L3 详情页头部,清单行只剩查看 */} - {/* 行内终止的二次确认(接口文档 §10):轻气泡锚在操作格里,触发钮左边; - 确认后留场等结果 —— 成功自动关 + 行状态翻亮,失败原因摆进气泡(2026-08-28 用户报) */} - {rowAsk?.rid === r.id && ( - terminate.mutate(r.id, { onSuccess: () => setRowAsk(null) }), - }} - busy={terminate.isPending} - error={terminate.isError ? errText(terminate.error) : null} - onCancel={() => setRowAsk(null)} - /> - )}
))} @@ -901,7 +878,7 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) { {pageRows.length === 0 && (
- 没有匹配的实例 —— 换个条件,或清空重看全部。 + 没有匹配的活动 —— 换个条件,或清空重看全部。
)}
@@ -923,9 +900,10 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) { {/* * 沙箱(2026-08-25 用户定;2026-08-27 接接口,文档 §2.3):加载服务器返回的活动视图 HTML。 * 隔离:iframe + sandbox="allow-scripts" —— 不透明源,禁同源/表单/弹窗,视图只能渲染与跑自己的脚本; - * src = 活动详情里的 view.url + 装载那一刻现取的票据;view 为 null 时整个区块藏起来。 + * src = 活动详情里的 view.url + 装载那一刻现取的票据。 + * 2026-08-28 用户定:沙箱区块**先隐藏**(SandboxFrame 组件与 view 取数保留,SHOW_SANDBOX 置回 true 即恢复)。 */} - {a.view !== null && ( + {SHOW_SANDBOX && a.view !== null && ( @@ -1104,7 +1082,7 @@ export function ActiPage({ id }: { id: string }) { {d.llm} } - desc="一次运行的完整视图:输入 / 过程 / 输出 / 状态 —— 实例不可编辑、不可删除,进行中的运行可终止。" + desc="一次运行的完整视图:输入 / 过程 / 输出 / 状态 —— 活动不可编辑、不可删除,进行中的运行可终止。" acts={[ /* 唯一动作(2026-08-26 用户定:实例不许删、不许编):进行中可终止,轻气泡二次确认 */ ...(state === 'running' @@ -1117,7 +1095,7 @@ export function ActiPage({ id }: { id: string }) { onClick: () => { terminate.reset(); setAsk({ - title: `确认终止实例 ${d.no}?`, + title: `确认终止活动 ${d.no}?`, okLabel: '确认终止', danger: true, onOk: () => terminate.mutate(undefined, { onSuccess: () => setAsk(null) }), diff --git a/docs/活动模块重构需求-2026-08-25.md b/docs/活动模块重构需求-2026-08-25.md index c5c1e20..bd3d43a 100644 --- a/docs/活动模块重构需求-2026-08-25.md +++ b/docs/活动模块重构需求-2026-08-25.md @@ -98,6 +98,10 @@ L1(模块首页 · 活动清单)需要一个**标签页**切换,因为活 - 2026-08-27 · **启动固定参数「责任人」(用户定)**:每次启动必须指定这次实例的责任人(负责人验收、数据工单都落到他身上)—— 它是**启动契约的固定字段 `ownerId`**,不进按活动下发的参数表。抽屉里已加「固定参数」段:人员下拉来自员工名册真接口(`GET /api/gov/staff`,只列在职),默认当前登录人(标注「我自己」);接口口径见 `docs/API文档-活动模块.md` §5。 - 2026-08-28 · **轻气泡确认的反馈闭环(用户报修)**:禁用/激活/终止实例确认后气泡**不再立即消失** —— 请求在途留场转忙碌(两钮禁用、罩不可点),成功自动关(列表/详情由失效重取刷新,状态翻转有弹出高亮),失败把接口原因留在气泡里可重试。此前确认即关 + mutation 无 onError,失败时全哑,点了不知道成没成。 - 2026-08-28 · **L3 实例详情 7 秒轮询(用户定)**:`useOpsInstance` 加 `refetchInterval` —— 待执行/进行中每 7 秒重取一次;进终态(完成/驳回/失败)自动停,不白打接口。 +- 2026-08-28 · **运营中心四区先摆建设中(用户定)**:驾驶舱 / 工作流 / 台账 / 系统的区首页换建设中占位(gov-shell `WIP_ZONES`),真面板与下钻深链保留,摘掉集合即恢复;目前运营中心只开放「活动」与「员工」。 +- 2026-08-28 · **L2 头部「启动活动」改文案为「创建活动」(用户定)**;同页「活动视图 · 沙箱」区块先隐藏(`SHOW_SANDBOX=false`,SandboxFrame 与 view 取数保留)。 +- 2026-08-28 · **终止收口到 L3(用户定)**:L2 实例清单行内的「终止活动」按钮连同行内气泡一并撤掉(PopAsk 的 inrow 变体与 CSS 保留),终止只在 L3 详情页头部;**界面文案收口(同日两轮定)**:L1 卡底栏仍叫「实例」;L2 区块「历史活动」改「**活动列表**」,表头第一列与检索条件定名「**活动号**」,L3 描述与终止确认气泡里的「实例」改「活动」。工作流区的「节点实例」是另一概念,未动。 +- 2026-08-28 · **L2 头版负责人小签隐藏(用户定)**:活动本身没有负责人——责任人是每次创建的固定参数(`ownerId`),归 L3 实例头版;定义上的 owner 字段保留不渲染。 ## 三、功能表(整理稿)