Archived
删掉流程详情的「契约细节」段 —— 页面到节点属性为止
它的三块都已有更好的去处或本就冗余:边映射的问题由异常看板逐条点名(还能点击定位), 调用接口在画布「开始」节点的属性里,触发器有自己的页签。 连带清掉因此变成死代码的整页 EdgesPage(路由类型 / 壳分发 / 出口 / 死 import 一并删, 不留只进不出的入口)。 详情页现在就四件:版本卡 · 流程图 · 异常看板 · 节点属性。 typecheck ✓ lint ✓ depcruise ✓ 506 测试 ✓ Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -26,7 +26,7 @@ import { queryKeys } from './query.ts';
|
||||
import { BlueprintHome } from '@features/gov-blueprint';
|
||||
import { TasksHome, TaskLogPage, TaskPage, WfPage } from '@features/gov-task';
|
||||
import { EmpPage, PeopleHome, RolePage } from '@features/gov-staff';
|
||||
import { EdgesPage, PkgPage, WorkflowsHome } from '@features/gov-process';
|
||||
import { PkgPage, WorkflowsHome } from '@features/gov-process';
|
||||
import { TriggerPage } from '@features/gov-trigger';
|
||||
import { ActPage, LibraryHome } from '@features/gov-activity';
|
||||
import { ToolPage, ToolsHome } from '@features/gov-tool';
|
||||
@@ -308,8 +308,6 @@ function Detail({ route, nav }: { route: Route; nav: Nav }) {
|
||||
return <RolePage id={route.id} />;
|
||||
case 'pkg':
|
||||
return <PkgPage id={route.id} nav={nav} />;
|
||||
case 'edges':
|
||||
return <EdgesPage id={route.id} />;
|
||||
case 'trigger':
|
||||
return <TriggerPage id={route.id} />;
|
||||
case 'act':
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
* feature `gov-process` 唯一出口(编码规范 §9.1)。
|
||||
* 流程包编排画布(diagram-js 画布件平移):连线即映射、画图即校验、不闭合不得发布
|
||||
*/
|
||||
export { WorkflowsHome, PkgPage, EdgesPage } from './internal/panel.tsx';
|
||||
export { WorkflowsHome, PkgPage } from './internal/panel.tsx';
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import { useMemo, useRef, useState, useEffect } from 'react';
|
||||
import { useMutation, useQueryClient, useQuery } from '@tanstack/react-query';
|
||||
import type { ClosureReport, ProcessPackage, ProcessNode } from '@contracts';
|
||||
import { processPackageSchema, saveProcessRoute, setProcessStateRoute, validateProcessRoute, TRIGGER_KIND_LABEL } from '@contracts';
|
||||
import { processPackageSchema, saveProcessRoute, setProcessStateRoute, validateProcessRoute } from '@contracts';
|
||||
import { call } from '@web-shared/typed-fetch.ts';
|
||||
import { Ico } from '@web-shared/Ico.tsx';
|
||||
import type { AskSpec } from '@web-shared/gov/primitives.tsx';
|
||||
@@ -22,23 +22,19 @@ import {
|
||||
AskBand,
|
||||
Card,
|
||||
Chip,
|
||||
Entry,
|
||||
Field,
|
||||
Kpi,
|
||||
ListEdit,
|
||||
NList,
|
||||
NRow,
|
||||
Note,
|
||||
PageHead,
|
||||
Prop,
|
||||
QueryGate,
|
||||
Rows,
|
||||
Sec,
|
||||
Seg,
|
||||
St,
|
||||
} from '@web-shared/gov/primitives.tsx';
|
||||
import type { Nav } from '@web-shared/gov/gov-route.ts';
|
||||
import { useGovActivities, useGovPackages, useGovPkgReports, useGovStaff, useGovTriggers } from '@web-shared/gov/gov-api.ts';
|
||||
import { useGovActivities, useGovPackages, useGovPkgReports, useGovStaff } from '@web-shared/gov/gov-api.ts';
|
||||
import type { GovActivities } from '@web-shared/gov/gov-api.ts';
|
||||
import { FlowFigureBlock } from '@web-shared/gov/flow/FlowFigure.tsx';
|
||||
import { packageToGraph } from '@web-shared/gov/flow/pkg-to-graph.ts';
|
||||
@@ -567,9 +563,6 @@ function FlowLab({
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
const triggersQ = useGovTriggers();
|
||||
const boundTriggers = (triggersQ.data?.triggers ?? []).filter((t) => t.processPackageId === pkg.id);
|
||||
const callInput = pkg.callInterface.input as { required?: string[]; properties?: Record<string, { type?: string }> };
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -990,179 +983,6 @@ function FlowLab({
|
||||
</div>
|
||||
</Sec>
|
||||
|
||||
{/* 声明式契约细节:调用接口 · 边映射 · 触发器 —— 图之下,按查看频率排 */}
|
||||
<Sec ico="file-text" title="契约细节" extra="声明式 —— 图定结构,这里定字段与调起">
|
||||
<div className="g-entries">
|
||||
<Entry
|
||||
ico="git-merge"
|
||||
tone={ec.closed === ec.total ? 'ok' : 'danger'}
|
||||
title="边映射校验"
|
||||
desc="上游输出口 → 下游契约字段 · 类型与必填,逐边核对"
|
||||
meta={
|
||||
<Chip tone={ec.closed === ec.total ? 'ok' : 'danger'}>
|
||||
{ec.closed}/{ec.total}
|
||||
</Chip>
|
||||
}
|
||||
onClick={() => nav.push({ k: 'edges', id: `${pkg.id}@${pkg.version}` })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Card ico="plug" title="调用接口" extra="起单入参 —— 触发器与登记表单都按它装配">
|
||||
{Object.keys(callInput.properties ?? {}).length === 0 ? (
|
||||
<Note>这条流还没声明入参 —— 谁调起它都不用带数据;要带,去画布点「开始」节点声明。</Note>
|
||||
) : (
|
||||
<Rows
|
||||
items={Object.entries(callInput.properties ?? {}).map(([k, v]) => ({
|
||||
k,
|
||||
v: `${v.type ?? 'any'}${(callInput.required ?? []).includes(k) ? ' · 必填' : ''}`,
|
||||
mono: true,
|
||||
}))}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card ico="zap" title="调起它的触发器" extra="平级、不入包版本 —— 增删启停不改本流任何版本">
|
||||
{boundTriggers.length > 0 ? (
|
||||
<NList>
|
||||
{boundTriggers.map((t) => (
|
||||
<NRow
|
||||
key={t.id}
|
||||
tone="warn"
|
||||
ico="zap"
|
||||
title={t.name}
|
||||
sub={
|
||||
t.spec.kind === 'event'
|
||||
? t.spec.rule
|
||||
: t.spec.kind === 'registration'
|
||||
? TRIGGER_KIND_LABEL.registration
|
||||
: TRIGGER_KIND_LABEL.periodic
|
||||
}
|
||||
cols={[{ k: '状态', v: t.enabled ? '已启用' : '已停用' }]}
|
||||
right={
|
||||
<button
|
||||
className="g-btn"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
nav.jump('workflows', 'rules');
|
||||
}}
|
||||
>
|
||||
改规则
|
||||
</button>
|
||||
}
|
||||
onClick={() => nav.jump('workflows', 'rules', { k: 'trigger', id: t.id })}
|
||||
/>
|
||||
))}
|
||||
</NList>
|
||||
) : (
|
||||
<Note>
|
||||
还没有触发器指向这条流 —— 它定义得再完整,也没人调得起来。去<b>触发器</b>页签配一条
|
||||
(周期 / 事件 / 登记三选一)。
|
||||
</Note>
|
||||
)}
|
||||
</Card>
|
||||
</Sec>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/* ---- 边映射校验(整页) ---- */
|
||||
|
||||
export function EdgesPage({ id }: { id: string }) {
|
||||
const [pid, versionText] = id.split('@');
|
||||
const q = useGovPackages();
|
||||
const pkg = (q.data?.packages ?? []).find((p) => p.id === pid && String(p.version) === versionText);
|
||||
const reports = useGovPkgReports(pkg === undefined ? [] : [pkg]);
|
||||
if (pkg === undefined) {
|
||||
return (
|
||||
<QueryGate query={q}>
|
||||
<Note>流程包不存在。</Note>
|
||||
</QueryGate>
|
||||
);
|
||||
}
|
||||
|
||||
const report = reports.data?.get(`${pkg.id}@${pkg.version}`);
|
||||
const ec = edgeCheckOf(pkg, report);
|
||||
const okEdge = ec.closed === ec.total;
|
||||
const labelOf = (nodeId: string) => pkg.nodes.find((n) => n.id === nodeId)?.label ?? nodeId;
|
||||
const sourceText = (m: ProcessPackage['edges'][number]['mapping'][number]) => {
|
||||
const s = m.source;
|
||||
switch (s.from) {
|
||||
case 'initiation':
|
||||
return `起单.${s.path}`;
|
||||
case 'output-port':
|
||||
return `${s.port}.${s.path}`;
|
||||
case 'workflow-var':
|
||||
return `流程变量.${s.name}`;
|
||||
case 'constant':
|
||||
return `常量 ${JSON.stringify(s.value)}`;
|
||||
default:
|
||||
return '表达式';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHead
|
||||
ico="git-merge"
|
||||
tone={okEdge ? 'ok' : 'danger'}
|
||||
kicker={`${pkg.name} v${pkg.version} · 边映射`}
|
||||
title="边映射校验"
|
||||
chips={
|
||||
<St tone={okEdge ? 'ok' : 'danger'} ico={okEdge ? 'circle-check' : 'alert-circle'}>
|
||||
{ec.closed} / {ec.total} 已闭合
|
||||
</St>
|
||||
}
|
||||
desc="连线即映射:每条顺序边都要把上游输出口接到下游契约字段上,必填齐、类型无冲突才算闭合。边不闭合不得保存。"
|
||||
/>
|
||||
{!okEdge && report !== undefined && <Note tone="warn">{report.violations.map((v) => v.message).join(';')}</Note>}
|
||||
<Card ico="git-merge" title="逐边核对" extra="上游输出口 → 下游契约字段">
|
||||
<table className="g-matrix">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>边</th>
|
||||
<th>映射</th>
|
||||
<th>条数</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{pkg.edges.map((e) => {
|
||||
const bad = (report?.violations ?? []).filter((v) => v.edgeId === e.id);
|
||||
return (
|
||||
<tr key={e.id}>
|
||||
<td className="mono">
|
||||
{labelOf(e.from)} → {labelOf(e.to)}
|
||||
{e.label === undefined ? '' : `(${e.label})`}
|
||||
</td>
|
||||
<td>
|
||||
{e.mapping.length === 0
|
||||
? '—'
|
||||
: e.mapping.map((m) => `${sourceText(m)} → data.${m.target}`).join('; ')}
|
||||
{bad.length > 0 && (
|
||||
<>
|
||||
<br />
|
||||
<span className="g-mono">{bad.map((v) => v.message).join(';')}</span>
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
<td className="mono">{e.mapping.length}</td>
|
||||
<td>
|
||||
{bad.length === 0 ? (
|
||||
<St tone="ok" ico="circle-check">
|
||||
已闭合
|
||||
</St>
|
||||
) : (
|
||||
<St tone="danger" ico="alert-circle">
|
||||
未完成
|
||||
</St>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ export type Route =
|
||||
| { k: 'emp'; id: string }
|
||||
| { k: 'role'; id: string }
|
||||
| { k: 'pkg'; id: string }
|
||||
| { k: 'edges'; id: string }
|
||||
| { k: 'trigger'; id: string }
|
||||
| { k: 'act'; id: string }
|
||||
| { k: 'tool'; id: string }
|
||||
@@ -190,8 +189,6 @@ export function routeLabel(r: Route): string {
|
||||
return '岗位授权';
|
||||
case 'pkg':
|
||||
return '流程包';
|
||||
case 'edges':
|
||||
return '边映射';
|
||||
case 'trigger':
|
||||
return '触发器';
|
||||
case 'act':
|
||||
|
||||
Reference in New Issue
Block a user