活动模块接真接口 + 员工模块界面改版
CI / ci (push) Failing after 11s

- contracts 补建 activity-ops(schema + 11 条路由);L1/L2/L3 全真接口对接(清单/详情/实例/启动抽屉/沙箱)
- 启动抽屉:参数表服务端下发、幂等键防重、400 逐格报错;combo「新增」提为候选第一项
- 终止/禁用/激活轻气泡反馈闭环:在途留场转忙碌、成功自闭合并刷新、失败原因留气泡可重试
- L3 实例详情 7 秒轮询(待执行/进行中),终态自动停
- 员工模块四页改版:目录行+首字圆块、权限三槽点位、组织树/空态、成员详情 hero+角色开关;卡内容区内边距补齐
This commit is contained in:
shanqin
2026-08-27 22:52:29 +08:00
parent 27d2fe6bb4
commit 7f32a7e450
13 changed files with 2061 additions and 1030 deletions
+86 -1
View File
@@ -27,10 +27,12 @@ import type {
ProcessPackage,
GovLedgerByActivityOutput,
} from '@contracts';
import type { ApprovalTemplate } from '@contracts';
import type { ApprovalTemplate, ActivityStatus, InstanceState } from '@contracts';
import {
browseLedgerRoute,
ledgerByActivityRoute,
getOpsActivityRoute,
getOpsInstanceRoute,
govMetricsRoute,
govTaskLogRoute,
govWorkflowRoute,
@@ -44,6 +46,8 @@ import {
listLedgerObjectsRoute,
listLlmInstancesRoute,
listLlmProvidersRoute,
listOpsActivitiesRoute,
listOpsInstancesRoute,
listProcessPackagesRoute,
getOrgUserRoute,
listOrgGroupMembersRoute,
@@ -55,6 +59,7 @@ import {
listToolsRoute,
listTriggersRoute,
listWorkGroupsRoute,
opsLaunchParamsRoute,
validateProcessRoute,
} from '@contracts';
import { call } from '@web-shared/typed-fetch.ts';
@@ -259,3 +264,83 @@ export const useOrgGroupMembers = (groupId: string | undefined): UseQueryResult<
/** 角色 → 端 —— 这张表是**本地**的(ADR-012),不是 Keycloak 的字段 */
export const useRoleAccess = (): UseQueryResult<GovRoleAccess> =>
useQuery({ queryKey: ['gov', 'org', 'role-access'], queryFn: () => call<GovRoleAccess>(listRoleAccessRoute) });
/* ── 活动模块(运营中心 · 活动区,契约见 docs/API文档-活动模块.md)──────────────── */
/**
* 查询键统一挂在 `['gov', 'activities', …]` 下 —— 写路由(启动/禁用/激活/终止)成功后
* 服务端广播 `{kind:'system', id:'gov'}` 会失效整个 `['gov']` 前缀,自动重取;
* 前端 mutation 落地时也按这个前缀失效(不等服务端那一下)。
*/
export const ACTIVITY_OPS_KEYS = ['gov', 'activities'] as const;
export type OpsActivities = RouteOutput<typeof listOpsActivitiesRoute>;
export type OpsActivity = RouteOutput<typeof getOpsActivityRoute>;
export type OpsInstances = RouteOutput<typeof listOpsInstancesRoute>;
export type OpsLaunchParams = RouteOutput<typeof opsLaunchParamsRoute>;
export type OpsInstance = RouteOutput<typeof getOpsInstanceRoute>;
/** L1 清单:两个页签(激活中 / 已禁用)各取一份 */
export const useOpsActivities = (status: ActivityStatus): UseQueryResult<OpsActivities> =>
useQuery({
queryKey: [...ACTIVITY_OPS_KEYS, 'list', status],
queryFn: () => call<OpsActivities>(listOpsActivitiesRoute, { query: { status } }),
});
/** L2 活动定义 */
export const useOpsActivity = (activityId: string): UseQueryResult<OpsActivity> =>
useQuery({
queryKey: [...ACTIVITY_OPS_KEYS, 'activity', activityId],
queryFn: () => call<OpsActivity>(getOpsActivityRoute, { params: { activityId } }),
});
/** 实例清单的检索条件(2026-08-26 用户定的四条件);值 undefined 的键不发 */
export type OpsInstanceQuery = {
id?: string | undefined;
ownerId?: string | undefined;
state?: InstanceState | undefined;
startedFrom?: string | undefined;
startedTo?: string | undefined;
/** 0 起;发出去时 +1(服务端 page 从 1 起) */
page: number;
size: number;
};
/** L2 实例清单:检索与分页全在服务端,前端不再本地过滤 */
export const useOpsInstances = (activityId: string, q: OpsInstanceQuery): UseQueryResult<OpsInstances> =>
useQuery({
queryKey: [...ACTIVITY_OPS_KEYS, 'instances', activityId, q],
queryFn: () =>
call<OpsInstances>(listOpsInstancesRoute, {
params: { activityId },
query: {
id: q.id,
ownerId: q.ownerId,
state: q.state,
startedFrom: q.startedFrom,
startedTo: q.startedTo,
page: String(q.page + 1),
size: String(q.size),
},
}),
});
/** 启动参数表:开抽屉那一刻才取(`enabled` 由调用方把着) */
export const useOpsLaunchParams = (activityId: string, enabled: boolean): UseQueryResult<OpsLaunchParams> =>
useQuery({
queryKey: [...ACTIVITY_OPS_KEYS, 'launch-params', activityId],
queryFn: () => call<OpsLaunchParams>(opsLaunchParamsRoute, { params: { activityId } }),
enabled,
});
/** L3 实例详情 —— 7 秒轮询拿最新(2026-08-28 用户定):只在做工时轮(待执行/进行中),
进了终态(完成/驳回/失败 —— 手动终止也归 failed)数据不再变,轮询自动停 */
export const useOpsInstance = (activityId: string, instanceId: string): UseQueryResult<OpsInstance> =>
useQuery({
queryKey: [...ACTIVITY_OPS_KEYS, 'instance', activityId, instanceId],
queryFn: () => call<OpsInstance>(getOpsInstanceRoute, { params: { activityId, instanceId } }),
refetchInterval: (q) => {
const s = q.state.data?.state;
return s === 'pending' || s === 'running' ? 7000 : false;
},
});