/** * 运营中心 · 工作流 —— **流程包的运行时**(设计中心只负责设计,运营中心管细节)。 * * 两页签: * · 在途:工作流看板(从任务区迁入 —— 工作流一级归这儿,任务区只留活动一级); * · 运行配置:每个包**今天怎么跑**的细节,设计一个字不碰 —— * 触发器:起单开关 / 命中后要不要人工确认 / 谁来确认(触发确认链路第 4 步的配置面) * 节点负责人:流程包的哪个节点归谁 —— 显式指派 > 这里 > 轮值 */ import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query'; import type { TriggerConfig } from '@contracts'; import { listNodeOwnersRoute, setNodeOwnerRoute, setTriggerConfirmRoute, toggleTriggerRoute, } from '@contracts'; import { call } from '@web-shared/typed-fetch.ts'; import { Ico } from '@web-shared/Ico.tsx'; import { Card, Chip, Clear, QueryGate, St } from '@web-shared/gov/primitives.tsx'; import type { Nav } from '@web-shared/gov/gov-route.ts'; import { useGovPackages, useGovStaff, useGovTriggers } from '@web-shared/gov/gov-api.ts'; import { WorkflowBoard } from '@features/gov-task'; export function RunsHome({ tab, nav }: { tab: string; nav: Nav }) { if (tab === 'conf') return ; return ; } /** 治理写动作统一入口:成功即失效相关查询,画面由服务端重投影 */ function useGovAction(mutate: (input: TInput) => Promise, keys: readonly string[]) { const queryClient = useQueryClient(); return useMutation({ mutationFn: mutate, onSuccess: () => { for (const key of keys) void queryClient.invalidateQueries({ queryKey: ['gov', key] }); }, }); } /* ==================== 运行配置:一包一节 ==================== */ function RunConfig() { const pkgQ = useGovPackages(); const trgQ = useGovTriggers(); const staffQ = useGovStaff(); const packages = (pkgQ.data?.packages ?? []).filter((p) => p.state === 'active'); const triggers = trgQ.data?.triggers ?? []; const staff = (staffQ.data?.staff ?? []).filter((s) => s.state === 'active'); return ( {packages.length === 0 ? ( 没有生效的流程包 —— 设计中心激活一版,这里就有它的运行配置。 ) : ( <> {packages.map((p) => ( n.kind === 'activity' || n.kind === 'approval') .map((n) => ({ id: n.id, label: n.label, kind: n.kind }))} triggers={triggers.filter((t) => t.processPackageId === p.id)} staff={staff.map((s) => ({ id: s.id, name: s.name, code: s.position.code }))} /> ))} )} ); } type StaffOpt = { id: string; name: string; code: string }; function PackageConfig({ packageId, name, nodes, triggers, staff, }: { packageId: string; name: string; nodes: ReadonlyArray<{ id: string; label: string; kind: string }>; triggers: readonly TriggerConfig[]; staff: readonly StaffOpt[]; }) { const ownersQ = useQuery({ queryKey: ['gov', 'node-owners', packageId], queryFn: () => call<{ owners: Array<{ nodeId: string; staffId: string }> }>(listNodeOwnersRoute, { params: { id: packageId } }), }); const owners = new Map((ownersQ.data?.owners ?? []).map((o) => [o.nodeId, o.staffId])); const toggle = useGovAction( (input: { id: string; enabled: boolean }) => call(toggleTriggerRoute, { params: { id: input.id }, body: { enabled: input.enabled } }), ['triggers'], ); const confirm = useGovAction( (input: { id: string; required: boolean; staffId?: string }) => call(setTriggerConfirmRoute, { params: { id: input.id }, body: { required: input.required, ...(input.staffId === undefined ? {} : { staffId: input.staffId }) }, }), ['triggers'], ); const setOwner = useGovAction( (input: { nodeId: string; staffId: string | null }) => call(setNodeOwnerRoute, { params: { id: packageId, nodeId: input.nodeId }, body: { staffId: input.staffId } }), ['node-owners'], ); /* * 勾选与下拉也是「点一下就去请求」—— 它们不是按钮,给不了键感, * 那就在办事的这一段把**那一行**点亮(条画在行脚下)。 * 认 `variables` 只亮真正在路上的那一行 —— 三个开关共用一个 mutation, * 不认就变成整表一起闪,等于没说是哪一条在办。 * 条不画在 ` toggle.mutate({ id: t.id, enabled: e.target.checked })} /> 起单 {confirmed && t.confirm.staffId === undefined && 还没指定确认人} ); }) )} {/* ── 节点负责人:显式指派 > 这里 > 轮值 ── */}
节点负责人 —— 留空即轮值(有员工组按组轮,没有按全员最闲者先)
{nodes.map((n) => ( ))}
); }