-
{role.name}
- {role.composite &&
组合角色}
-
{`${role.holders} 人持有`}
+
+
+
+ {role.name}
+ {role.composite && 组合角色}
+ {`${role.holders} 人持有`}
+
+
+ {/* 可进端改三槽点位(2026-08-27 第二轮):三个端永远都在,授了的亮、没授的虚灰 —— 列向下扫即读 */}
+
+ {SURFACES.map((s) => (
+
+ {SURFACE_LABEL[s]}
+
+ ))}
+ {!configured && 还没配过}
+
+ {role.description !== undefined && {role.description}}
+
+
+ {!editing && (
+ <>
+ { setDraft(role.ends); setErr(undefined); }}>
+ 改可进端
+
+ {configured && (
+ setAskDel(true)}>
+ 删掉这一行
+
+ )}
+ >
+ )}
+
-
- {role.description !== undefined &&
{role.description}}
- {editing ? (
+ {err !== undefined &&
{err}}
+ {askDel && (
+
remove.mutate(),
+ }}
+ danger
+ onCancel={() => setAskDel(false)}
+ />
+ )}
+ {editing && (
+
{SURFACES.map((s) => (
- ) : (
-
-
- {!configured && 还没配过}
-
- )}
- {err !== undefined &&
{err}}
-
{ setDraft(undefined); setErr(undefined); } },
- {
- label: '保存',
- ico: 'check',
- primary: true,
- busy: save.isPending,
- ask: {
- title: `把「${role.name}」的可进端改成:${shown.length === 0 ? '无' : shown.map((s) => SURFACE_LABEL[s]).join(' / ')}`,
- note: '这张表每个请求都在读 —— 改完下一个请求就按新的判。',
- impact: {
- who: `持有这个角色的 ${role.holders} 个人`,
- undo: '可以改回来 —— 但改完得还有人进得了运营中心,否则服务端会拒',
- when: '立刻;他们手上的 token 不用重新登录也按新表判',
- },
- okLabel: '确认改',
- onOk: () => save.mutate(shown),
- },
+ { setDraft(undefined); setErr(undefined); } },
+ {
+ label: '保存',
+ ico: 'check',
+ primary: true,
+ busy: save.isPending,
+ ask: {
+ title: `把「${role.name}」的可进端改成:${shown.length === 0 ? '无' : shown.map((s) => SURFACE_LABEL[s]).join(' / ')}`,
+ note: '这张表每个请求都在读 —— 改完下一个请求就按新的判。',
+ impact: {
+ who: `持有这个角色的 ${role.holders} 个人`,
+ undo: '可以改回来 —— 但改完得还有人进得了运营中心,否则服务端会拒',
+ when: '立刻;他们手上的 token 不用重新登录也按新表判',
},
- ]
- : [
- { label: '改可进端', ico: 'pencil-line', onClick: () => setDraft(role.ends) },
- ...(configured
- ? [
- {
- label: '删掉这一行',
- ico: 'trash-2',
- danger: true,
- busy: remove.isPending,
- ask: {
- title: `删掉「${role.name}」的端配置`,
- note: '删掉之后这个角色不再命中任何端 —— 与「一个端都不勾」是同一个效果。',
- impact: {
- who: `持有这个角色的 ${role.holders} 个人`,
- undo: '可以再配回来',
- when: '立刻',
- },
- okLabel: '确认删',
- onOk: () => remove.mutate(),
- },
- },
- ]
- : []),
- ]
- }
- />
-
+ okLabel: '确认改',
+ onOk: () => save.mutate(shown),
+ },
+ },
+ ]}
+ />
+
+ )}
);
}
diff --git a/apps/web/src/features/gov-task/internal/panel.tsx b/apps/web/src/features/gov-task/internal/panel.tsx
index 22d77b0..83cbf81 100644
--- a/apps/web/src/features/gov-task/internal/panel.tsx
+++ b/apps/web/src/features/gov-task/internal/panel.tsx
@@ -5,29 +5,53 @@
* DOM 结构、类名(`g-*` + 色调 `t-*`)与文案原样保留,mock 常量换成服务端投影(README 平移规则 1/2/4)。
* 区首页只负责导航,治理动作一律落在下钻详情页的**就地确认带**(影响预览三必答)。
*/
-import { useEffect, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
-import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import {
+ ACTIVITY_STATUS_LABEL,
+ ACTIVITY_VIEW_HEIGHT_MESSAGE,
CONTROL_SEMANTIC_LABEL,
+ INSTANCE_STATE_LABEL,
+ INSTANCE_STATES,
TASK_RUNTIME_STATE_LABEL,
TERMINAL_STATE_LABEL,
WORKFLOW_STATE_LABEL,
+ activateOpsActivityRoute,
+ authTicketRoute,
controlTaskRoute,
+ disableOpsActivityRoute,
+ launchOpsInstanceRoute,
+ meRoute,
reassignTaskRoute,
resumeTaskRoute,
+ terminateOpsInstanceRoute,
} from '@contracts';
-import type { ControlSemantic } from '@contracts';
+import type { ControlSemantic, InstanceState, LaunchInput, Principal } from '@contracts';
+import type { InstanceFile, InstanceStep, LaunchParam } from '@contracts';
import { Ico } from '@web-shared/Ico.tsx';
-import { call } from '@web-shared/typed-fetch.ts';
-import { Ava, Card, Chip, Clear, Entry, Facts, NList, NRow, Note, PageHead, Sec } from '@web-shared/gov/primitives.tsx';
+import { ApiCallError, call } from '@web-shared/typed-fetch.ts';
+import { Ava, Card, Chip, Clear, Entry, Facts, NList, NRow, Note, PageHead, QueryGate, Sec } from '@web-shared/gov/primitives.tsx';
import type { ActSpec } from '@web-shared/gov/primitives.tsx';
import { RUNTIME_ST, TERMINAL_ST, WF_ST } from '@web-shared/gov/tone.ts';
import type { Nav } from '@web-shared/gov/gov-route.ts';
import { FlowFigure } from '@web-shared/gov/flow/FlowFigure.tsx';
import { atFull, atMinute } from '@web-shared/at.ts';
import { Btn } from '@web-shared/Btn.tsx';
-import { useGovStaff, useGovTaskLog, useGovTasks, useGovWorkflow } from '@web-shared/gov/gov-api.ts';
+import { queryKeys } from '@web-shared/query-keys.ts';
+import {
+ ACTIVITY_OPS_KEYS,
+ useGovStaff,
+ useGovTaskLog,
+ useGovTasks,
+ useGovWorkflow,
+ useOpsActivities,
+ useOpsActivity,
+ useOpsInstance,
+ useOpsInstances,
+ useOpsLaunchParams,
+} from '@web-shared/gov/gov-api.ts';
+import type { OpsInstance, OpsInstanceQuery } from '@web-shared/gov/gov-api.ts';
export type HomeProps = { tab: string; nav: Nav };
@@ -128,137 +152,77 @@ export function WorkflowBoard({ nav }: { nav: Nav }) {
* 尾部幽灵卡 = 创建活动入口(创建功能本期不做,纯占位)。
* L2(活动详情 + 实例清单)、L3(实例详情)随后接。
*/
-type DemoActivity = {
- id: string;
- name: string;
- biz: string;
- instances: number;
- owner: string;
- createdAt: string;
- /** 运营态:激活中 / 已禁用(2026-08-25 用户定)—— L2 里可改;发布/审核是设计部分,暂缓 */
- status: 'active' | 'disabled';
- /** 当前版本号 —— 活动无编辑/删除,演进只能靠发新版本(2026-08-25 用户定) */
- version?: string;
- /** 语义图标与业务色相 —— 色即业务,一卡一色(2026-08-25 视觉增强) */
- ico: string;
- hue: 'blue' | 'teal' | 'amber' | 'violet';
- /** 这个活动绑定的 LLM —— 每个活动一个专属 agent,负责把流程跑通(2026-08-25 用户定:L2 要展示) */
- llm: string;
-};
-
-/** 占位静态数据 —— 接口设计定稿后整体换掉(2026-08-25 用户定:先出页面模版看效果) */
-const DEMO_ACTIVITIES: readonly DemoActivity[] = [
- { id: 'quote-intake', name: '询价接收与结构化', biz: '报价业务', instances: 12, owner: '柴浩', createdAt: '08-12', status: 'active', version: '1.3.0', ico: 'inbox', hue: 'blue', llm: 'K3 · quote-intake' },
- { id: 'quote-pricing', name: '报价定价测算', biz: '报价业务', instances: 8, owner: '丁子乔', createdAt: '08-12', status: 'active', version: '1.2.0', ico: 'calculator', hue: 'blue', llm: 'K3 · quote-pricing' },
- { id: 'crm-coverage', name: '关键人触达计划', biz: 'CRM 业务', instances: 5, owner: '冯兵', createdAt: '08-15', status: 'active', version: '1.0.0', ico: 'target', hue: 'teal', llm: 'K3 · crm-coverage' },
- { id: 'competitor-watch', name: '竞品价格监测', biz: '报价业务', instances: 2, owner: '柴浩', createdAt: '08-24', status: 'active', version: '1.1.0', ico: 'eye', hue: 'blue', llm: 'K3 · competitor-watch' },
- { id: 'market-weekly', name: '市场信号周报生成', biz: '市场业务', instances: 3, owner: '林佩萱', createdAt: '08-18', status: 'disabled', version: '2.1.0', ico: 'trending-up', hue: 'amber', llm: 'K3 · market-weekly' },
- { id: 'supplier-verify', name: '供应商资质核验', biz: '采购业务', instances: 1, owner: '丁子乔', createdAt: '08-25', status: 'disabled', version: '1.0.0', ico: 'shield-check', hue: 'violet', llm: 'K3 · supplier-verify' },
-];
-
function RuntimeHome({ tab, nav }: HomeProps) {
- const items = DEMO_ACTIVITIES.filter((a) => a.status === tab);
+ /* 接口对接(2026-08-27):静态占位数组撤了,清单来自 GET /api/gov/activities(docs/API文档-活动模块.md §2.1) */
+ const q = useOpsActivities(tab === 'disabled' ? 'disabled' : 'active');
+ const items = q.data?.activities ?? [];
return (
-
- {items.map((a) => (
-
- {/* 整卡热区 → L2 活动详情(2026-08-25 接) */}
-