运营中心收口:四区(驾驶舱/工作流/台账/系统)摆建设中占位;L2「启动活动」改「创建活动」、沙箱区块隐藏、行内终止撤掉收口 L3、负责人小签隐藏;文案定名活动列表/活动号
CI / ci (push) Failing after 12s

This commit is contained in:
shanqin
2026-08-28 10:39:13 +08:00
parent f41bd76b0e
commit 21d41f5859
3 changed files with 47 additions and 48 deletions
@@ -227,7 +227,7 @@ const INSTANCE_PAGE_SIZE = 8;
type InstanceFilterKey = 'id' | 'owner' | 'state' | 'time';
const INSTANCE_FILTER_LABEL: Readonly<Record<InstanceFilterKey, string>> = {
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
</option>
))}
</select>
<small>这次启动的实例归他 —— 负责人验收、数据工单都落到责任人身上;默认是你自己。</small>
<small>这次创建的活动归他 —— 负责人验收、数据工单都落到责任人身上;默认是你自己。</small>
{fieldErr['ownerId'] !== undefined && <small className="warn">{fieldErr['ownerId']}</small>}
</label>
</section>
@@ -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<PopAskSpec | null>(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 }) {
<Chip ico="tag">v{a.version}</Chip>
{/* 绑定的 LLM 上头版(2026-08-25 用户定):每个活动一个专属 agent */}
<Chip ico="cpu">{a.llm}</Chip>
<Chip ico="user">{a.owner?.name ?? '—'}</Chip>
{/* 负责人小签隐藏(2026-08-28 用户定:活动本身没有负责人;责任人是每次创建的固定参数,在 L3 看) */}
<Chip ico="calendar-days">创建于 {atMinute(a.createdAt).slice(0, 5)}</Chip>
</>
}
@@ -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 && <LaunchDrawer activityId={a.id} name={a.name} onCancel={() => setLaunch(false)} onLaunch={onLaunched} />}
</div>
<Sec ico="repeat" title="历史活动" extra={`共 ${list?.stats.total ?? '…'} 个 · 查看详情进 L3`}>
{/* 「历史活动」改「活动列表」(2026-08-28 用户定) */}
<Sec ico="repeat" title="活动列表" extra={`共 ${list?.stats.total ?? '…'} 个 · 查看详情进 L3`}>
{/* 统计带 + 检索条同一行(2026-08-26 用户定:检索放右侧)——
左:总数 + 五态灰字合计(状态色只进 6px 小圆点);右:条件下拉 + 随条件换控件;
统计数是服务端给的全局 stats,不随检索条件动(2026-08-27 接接口) */}
@@ -751,7 +755,7 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) {
<input
className="g-iselect-input"
value={fText}
placeholder="按实例号模糊搜,如 INS-2417"
placeholder="按活动号模糊搜,如 INS-2417"
onChange={(e) => {
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) */}
<QueryGate query={instQ}>
<div className="g-itab">
{/* 列序(2026-08-26 用户定):实例 / 负责人 / 输入 / 输出 / 启动时间 / 状态 / 操作(操作栏也有标题) */}
{/* 列序(2026-08-26 用户定):实例 / 负责人 / 输入 / 输出 / 启动时间 / 状态 / 操作(操作栏也有标题);
表头第一列定名「活动号」(2026-08-28 用户定) */}
<div className="g-itab-h">
<span>实例</span>
<span>活动号</span>
<span>负责人</span>
<span>输入</span>
<span>输出</span>
@@ -862,38 +867,10 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) {
{INSTANCE_STATE_LABEL[r.state]}
</span>
<span className="a">
{r.state === 'running' && (
<button
type="button"
className="g-rowbtn danger"
onClick={() => {
terminate.reset();
setRowAsk({ rid: r.id, no: r.no });
}}
>
终止活动
</button>
)}
{/* L3 实例详情(2026-08-26 接):路由 id 带活动与实例两段,详情页按活动找回上下文 */}
{/* 行内「终止活动」撤掉(2026-08-28 用户定):终止只留在 L3 详情页头部,清单行只剩查看 */}
<button type="button" className="g-rowbtn" onClick={() => nav.push({ k: 'acti', id: `${a.id}:${r.id}` })}>
查看详情
</button>
{/* 行内终止的二次确认(接口文档 §10):轻气泡锚在操作格里,触发钮左边;
确认后留场等结果 —— 成功自动关 + 行状态翻亮,失败原因摆进气泡(2026-08-28 用户报) */}
{rowAsk?.rid === r.id && (
<PopAsk
inrow
spec={{
title: `确认终止实例 ${rowAsk.no}?`,
okLabel: '确认终止',
danger: true,
onOk: () => terminate.mutate(r.id, { onSuccess: () => setRowAsk(null) }),
}}
busy={terminate.isPending}
error={terminate.isError ? errText(terminate.error) : null}
onCancel={() => setRowAsk(null)}
/>
)}
</span>
</div>
))}
@@ -901,7 +878,7 @@ export function ActvPage({ id, nav }: { id: string; nav: Nav }) {
{pageRows.length === 0 && (
<div className="g-irow-empty">
<Ico name="search" s={14} />
没有匹配的实例 —— 换个条件,或清空重看全部。
没有匹配的活动 —— 换个条件,或清空重看全部。
</div>
)}
</div>
@@ -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 && (
<Sec ico="dashboard" title="活动视图 · 沙箱" extra="服务器返回的 HTML · 隔离渲染">
<SandboxFrame activityId={a.id} viewUrl={a.view.url} name={a.name} />
</Sec>
@@ -1104,7 +1082,7 @@ export function ActiPage({ id }: { id: string }) {
<Chip ico="cpu">{d.llm}</Chip>
</>
}
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) }),