清掉「节点决定数据契约」的三处遗留 —— 编排面从此一个字段都不碰

讨论定的界线:节点只决定四件(身份 · 类型 · 链接哪个活动 · 出边条件),
数据契约全归活动自己(它的 manifest,随活动升版),流程包的 callInterface 是
「这条流被调用时的签名」另算。照此清三处化石:

  ① `ProcessNode.in/out` —— schema 上还留着「这个节点**声明**吃什么、吐什么」,
     注释还写着「闭合校验以**边映射**为准」;边映射上一轮已退役,这两行是上个模型的化石。
     连同图↔包的两处映射一并撤。
  ② 手绘编辑器属性卡的 **进 / 出 / 查 / 执行** 四行 —— 其中「查」(use)与「执行」(owner)
     连 ProcessNode 都没有,填了保存就丢:写了不生效的输入框。
     同卡里**网关**与**卡点**两支也一并删(两种要素早已从模型里去掉,分支不可达,
     而且正是网关那支在写 owner)。
  ③ 只读图双击卡的 **执行** 行、`onEditNode` 的 owner 形参、画笔里的**责任人角标** ——
     编排不带岗位,图上不该画它。上一轮图例注释说「图上早已不画责任人角标」其实说错了:
     画笔一直画着,只是没人写得进 owner;这次真的撤了,注释也改对。

连带:`FlowNode` 的 owner/in/out/use 四个字段退役;岗位选择器 support.tsx 成了孤儿
(depcruise no-orphans 当场逮住)—— 编排不带岗位,它没有归宿,删。

四闸绿;契约快照随本提交入库。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
2026-07-29 16:44:30 +08:00
co-authored by Claude Opus 5
parent c9ac058bdf
commit 5414e99778
11 changed files with 33 additions and 234 deletions
-16
View File
@@ -495,8 +495,6 @@ export declare const listProcessPackagesRoute: {
}, z.core.$strip>, z.ZodObject<{
kind: z.ZodLiteral<"activity">;
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
in: z.ZodOptional<z.ZodArray<z.ZodString>>;
out: z.ZodOptional<z.ZodArray<z.ZodString>>;
activityVersion: z.ZodOptional<z.ZodString>;
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
kind: z.ZodLiteral<"terminate">;
@@ -624,8 +622,6 @@ export declare const saveProcessRoute: {
}, z.core.$strip>, z.ZodObject<{
kind: z.ZodLiteral<"activity">;
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
in: z.ZodOptional<z.ZodArray<z.ZodString>>;
out: z.ZodOptional<z.ZodArray<z.ZodString>>;
activityVersion: z.ZodOptional<z.ZodString>;
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
kind: z.ZodLiteral<"terminate">;
@@ -737,8 +733,6 @@ export declare const saveProcessRoute: {
}, z.core.$strip>, z.ZodObject<{
kind: z.ZodLiteral<"activity">;
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
in: z.ZodOptional<z.ZodArray<z.ZodString>>;
out: z.ZodOptional<z.ZodArray<z.ZodString>>;
activityVersion: z.ZodOptional<z.ZodString>;
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
kind: z.ZodLiteral<"terminate">;
@@ -883,8 +877,6 @@ export declare const setProcessStateRoute: {
}, z.core.$strip>, z.ZodObject<{
kind: z.ZodLiteral<"activity">;
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
in: z.ZodOptional<z.ZodArray<z.ZodString>>;
out: z.ZodOptional<z.ZodArray<z.ZodString>>;
activityVersion: z.ZodOptional<z.ZodString>;
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
kind: z.ZodLiteral<"terminate">;
@@ -1008,8 +1000,6 @@ export declare const deleteProcessVersionRoute: {
}, z.core.$strip>, z.ZodObject<{
kind: z.ZodLiteral<"activity">;
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
in: z.ZodOptional<z.ZodArray<z.ZodString>>;
out: z.ZodOptional<z.ZodArray<z.ZodString>>;
activityVersion: z.ZodOptional<z.ZodString>;
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
kind: z.ZodLiteral<"terminate">;
@@ -1134,8 +1124,6 @@ export declare const renameProcessRoute: {
}, z.core.$strip>, z.ZodObject<{
kind: z.ZodLiteral<"activity">;
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
in: z.ZodOptional<z.ZodArray<z.ZodString>>;
out: z.ZodOptional<z.ZodArray<z.ZodString>>;
activityVersion: z.ZodOptional<z.ZodString>;
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
kind: z.ZodLiteral<"terminate">;
@@ -1251,8 +1239,6 @@ export declare const validateProcessRoute: {
}, z.core.$strip>, z.ZodObject<{
kind: z.ZodLiteral<"activity">;
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
in: z.ZodOptional<z.ZodArray<z.ZodString>>;
out: z.ZodOptional<z.ZodArray<z.ZodString>>;
activityVersion: z.ZodOptional<z.ZodString>;
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
kind: z.ZodLiteral<"terminate">;
@@ -2380,8 +2366,6 @@ export declare const govWorkflowRoute: {
}, z.core.$strip>, z.ZodObject<{
kind: z.ZodLiteral<"activity">;
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
in: z.ZodOptional<z.ZodArray<z.ZodString>>;
out: z.ZodOptional<z.ZodArray<z.ZodString>>;
activityVersion: z.ZodOptional<z.ZodString>;
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
kind: z.ZodLiteral<"terminate">;
@@ -135,8 +135,6 @@ export declare const processNodeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
}, z.core.$strip>, z.ZodObject<{
kind: z.ZodLiteral<"activity">;
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
in: z.ZodOptional<z.ZodArray<z.ZodString>>;
out: z.ZodOptional<z.ZodArray<z.ZodString>>;
activityVersion: z.ZodOptional<z.ZodString>;
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
kind: z.ZodLiteral<"terminate">;
@@ -249,8 +247,6 @@ export declare const processPackageSchema: z.ZodObject<{
}, z.core.$strip>, z.ZodObject<{
kind: z.ZodLiteral<"activity">;
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
in: z.ZodOptional<z.ZodArray<z.ZodString>>;
out: z.ZodOptional<z.ZodArray<z.ZodString>>;
activityVersion: z.ZodOptional<z.ZodString>;
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
kind: z.ZodLiteral<"terminate">;
+6 -19
View File
@@ -17,8 +17,6 @@ import {
type ShapeLayout,
} from "./flow-layout";
import { arrowHeadPoints, CAPTION_STYLE, FLOW_MONO, paintNode, svgEl as svg, TONE_COLOR } from "./flow-node-paint";
import { OwnerSelect } from "./support";
import type { SpPosition } from "./support";
import "diagram-js/assets/diagram-js.css";
/**
@@ -145,9 +143,9 @@ export interface FlowDiagramProps {
/** 点击连线(选中边,按边号/两端认);不传则边不可点。 */
onEdgeClick?: ((edge: FlowEdge) => void) | undefined;
/** 双击活动节点就地改文字(标题/小字/主办);不传则不可改。 */
onEditNode?: ((nodeId: string, patch: { label: string; owner?: string }) => Promise<void>) | undefined;
/** 双击活动就地改**名字** —— 图上可编辑的只有名字(数据归活动、岗位归运营) */
onEditNode?: ((nodeId: string, patch: { label: string }) => Promise<void>) | undefined;
/** 执行人候选(L1 业务能力)——传了则执行字段只能下拉选缩写,不开自由填。 */
ownerOptions?: SpPosition[] | undefined;
ariaLabel: string;
}
@@ -157,7 +155,7 @@ interface NodeEdit {
top: number;
}
export function FlowDiagram({ graph, variant, onNodeClick, onEdgeClick, onEditNode, ownerOptions, ariaLabel }: FlowDiagramProps) {
export function FlowDiagram({ graph, variant, onNodeClick, onEdgeClick, onEditNode, ariaLabel }: FlowDiagramProps) {
const containerRef = useRef<HTMLDivElement>(null);
const editRef = useRef<HTMLDivElement>(null);
const clickRef = useRef(onNodeClick);
@@ -170,7 +168,7 @@ export function FlowDiagram({ graph, variant, onNodeClick, onEdgeClick, onEditNo
const drillTimer = useRef<number | null>(null);
// 活动文字就地编辑浮层(双击活动)。
const [nodeEdit, setNodeEdit] = useState<NodeEdit | null>(null);
const [editDraft, setEditDraft] = useState({ label: "", owner: "" });
const [editDraft, setEditDraft] = useState({ label: "" });
const [editBusy, setEditBusy] = useState(false);
const [editErr, setEditErr] = useState<string | null>(null);
@@ -254,7 +252,7 @@ export function FlowDiagram({ graph, variant, onNodeClick, onEdgeClick, onEditNo
window.clearTimeout(drillTimer.current);
drillTimer.current = null;
}
setEditDraft({ label: bo.node.label, owner: bo.node.owner ?? "" });
setEditDraft({ label: bo.node.label });
setEditErr(null);
setEditBusy(false);
setNodeEdit({ node: bo.node, ...overlayPos(event.originalEvent, 300, 190) });
@@ -317,14 +315,6 @@ export function FlowDiagram({ graph, variant, onNodeClick, onEdgeClick, onEditNo
<span className="lb">标题</span>
<input value={editDraft.label} maxLength={16} onChange={(e) => setEditDraft((d) => ({ ...d, label: e.target.value }))} />
</div>
<div className="gp-edit-row">
<span className="lb">执行</span>
{ownerOptions ? (
<OwnerSelect value={editDraft.owner} positions={ownerOptions} onChange={(v) => setEditDraft((d) => ({ ...d, owner: v }))} />
) : (
<input value={editDraft.owner} maxLength={16} placeholder="执行人岗位缩写(可空)" onChange={(e) => setEditDraft((d) => ({ ...d, owner: e.target.value }))} />
)}
</div>
{editErr && <p className="gp-basis muted" style={{ color: "#8f3330" }}>{editErr}</p>}
<div className="gp-btns">
<button type="button" className="be-btn ghost" disabled={editBusy} onClick={() => setNodeEdit(null)}>
@@ -338,10 +328,7 @@ export function FlowDiagram({ graph, variant, onNodeClick, onEdgeClick, onEditNo
setEditBusy(true);
setEditErr(null);
editNodeRef
.current?.(nodeEdit.node.id, {
label: editDraft.label.trim(),
...(editDraft.owner.trim() === "" ? {} : { owner: editDraft.owner.trim() }),
})
.current?.(nodeEdit.node.id, { label: editDraft.label.trim() })
.then(() => setNodeEdit(null))
.catch((err) => {
setEditErr(err instanceof Error ? err.message : "保存失败");
+12 -101
View File
@@ -2,8 +2,6 @@
import { Ico } from "@ui";
import { useEffect, useMemo, useRef, useState } from "react";
import type { FlowEdge, FlowGraph, FlowNode, FlowNodeKind } from "./flow-layout.ts";
import type { SpPosition } from "./support.tsx";
import { OwnerSelect } from "./support.tsx";
import { createFlowEditor, syncElementLabel, type FlowEditorHandles } from "./flow-editor-core.ts";
import "diagram-js/assets/diagram-js.css";
@@ -26,8 +24,6 @@ export interface FlowEditorProps {
/** idMap = 编辑期活动改号映射(自动顺序编号),服务端据此重键 operations。 */
onSave: (graph: FlowGraph, idMap: Record<string, string>) => Promise<void>;
onCancel: () => void;
/** 执行人候选(L1 业务能力)——传了则活动属性卡的执行字段只能下拉选缩写。 */
positions?: SpPosition[] | undefined;
}
interface PropState {
@@ -37,7 +33,7 @@ interface PropState {
top: number;
}
export function FlowEditor({ initial, drilledIds, onSave, onCancel, positions }: FlowEditorProps) {
export function FlowEditor({ initial, drilledIds, onSave, onCancel }: FlowEditorProps) {
const canvasRef = useRef<HTMLDivElement>(null);
const wrapRef = useRef<HTMLDivElement>(null);
const editorRef = useRef<FlowEditorHandles | null>(null);
@@ -239,8 +235,6 @@ export function FlowEditor({ initial, drilledIds, onSave, onCancel, positions }:
<PropCard
key={`${prop.element.id}-${prop.top}`}
prop={prop}
registry={editorRef.current?.diagram.get("elementRegistry")}
positions={positions}
onClose={() => setProp(null)}
onApply={(patch) => {
const editor = editorRef.current;
@@ -277,14 +271,10 @@ const KIND_NAME: Record<FlowNodeKind, string> = {
function PropCard({
prop,
registry,
positions,
onClose,
onApply,
}: {
prop: PropState;
registry: Any;
positions?: SpPosition[] | undefined;
onClose: () => void;
onApply: (patch: Record<string, unknown>) => void;
}) {
@@ -292,57 +282,30 @@ function PropCard({
const edge = prop.isEdge ? (prop.element.businessObject as FlowEdge) : undefined;
const [label, setLabel] = useState(node?.label ?? edge?.label ?? "");
const [owner, setOwner] = useState(node?.owner ?? "");
const [flavor, setFlavor] = useState<"sop" | "case" | "approval">(
node?.flavor === "case" ? "case" : node?.flavor === "approval" ? "approval" : "sop",
);
const [mode, setMode] = useState<"x" | "and">(node?.mode === "and" ? "and" : "x");
const [basis, setBasis] = useState(node?.basis ?? "");
const [to, setTo] = useState(node?.to ?? "");
const [tone, setTone] = useState<string>(edge?.tone ?? "");
// 数据线(进/出/查:台账规范名顿号分隔)——L2 只讲流转与数据对象;
// 过程细节(分工/逐条)属 L3 指导书内部,节点级 per/assign 已退役(保存即清遗留值)。
const [dataIn, setDataIn] = useState((node?.in ?? []).join("、"));
const [dataOut, setDataOut] = useState((node?.out ?? []).join("、"));
const [dataUse, setDataUse] = useState((node?.use ?? []).join("、"));
const targets: string[] = useMemo(() => {
if (!registry) return [];
const ids: string[] = [];
for (const el of registry.getAll() as Any[]) {
const bo = el.businessObject as FlowNode | undefined;
if (el.parent && !el.waypoints && bo?.id && bo.id !== node?.id) ids.push(bo.id);
}
return ids.sort();
}, [registry, node?.id]);
const save = () => {
if (node) {
const patch: Record<string, unknown> = {};
const text = label.trim();
if (text) patch.label = text.slice(0, 16);
if (node.kind === "activity") {
patch.owner = owner.trim() ? owner.trim().slice(0, 16) : undefined;
patch.flavor = flavor === "sop" ? undefined : flavor;
const lines = (raw: string) =>
raw.split(/[、,,\n]+/).map((part) => part.trim().slice(0, 20)).filter(Boolean).slice(0, 5);
const din = lines(dataIn);
const dout = lines(dataOut);
const duse = lines(dataUse);
patch.in = din.length ? din : undefined;
patch.out = dout.length ? dout : undefined;
patch.use = duse.length ? duse : undefined;
// 活动调用整体退役(2026-07 终拍,user「所有节点都是内置节点」):保存即清遗留 wait。
/*
* **节点不声明数据、不带岗位**(2026-07-30):进/出/查与执行四行已撤 ——
* 数据契约归活动自己(它的 manifest),负责人归运营时分配。
* 这四样连同更早退役的 wait/per/assign,保存时一并清掉遗留值。
*/
patch.owner = undefined;
patch.in = undefined;
patch.out = undefined;
patch.use = undefined;
patch.wait = undefined;
patch.per = undefined;
patch.assign = undefined;
} else if (node.kind === "gateway") {
patch.owner = owner.trim() ? owner.trim().slice(0, 12) : undefined;
patch.mode = mode === "and" ? "and" : undefined;
patch.basis = mode === "x" && basis.trim() ? basis.trim().slice(0, 120) : undefined;
} else if (node.kind === "callout") {
patch.to = to || undefined;
}
onApply(patch);
} else if (edge) {
@@ -382,64 +345,12 @@ function PropCard({
<option value="approval">审批点(琥珀 · 人裁节点)</option>
</select>
</div>
<div className="gp-edit-row">
<span className="lb">执行</span>
{positions ? (
<OwnerSelect value={owner} positions={positions} onChange={setOwner} />
) : (
<input value={owner} maxLength={16} placeholder="执行人岗位缩写(可空)" onChange={(e) => setOwner(e.target.value)} />
)}
</div>
<div className="gp-edit-row">
<span className="lb">进</span>
<input value={dataIn} placeholder="吃进的流转件,顿号分隔 ≤5(规范名)" onChange={(e) => setDataIn(e.target.value)} />
</div>
<div className="gp-edit-row">
<span className="lb">出</span>
<input value={dataOut} placeholder="产出的流转件;可带状态词:单据名(已受理)" onChange={(e) => setDataOut(e.target.value)} />
</div>
<div className="gp-edit-row">
<span className="lb">查</span>
<input value={dataUse} placeholder="照着查/判的底座与规则,顿号分隔" onChange={(e) => setDataUse(e.target.value)} />
</div>
</>
)}
{node?.kind === "gateway" && (
<>
<div className="gp-edit-row">
<span className="lb">类型</span>
<select value={mode} onChange={(e) => setMode(e.target.value as "x" | "and")}>
<option value="x">判断 ×(按判据走一条)</option>
<option value="and">并行 +(全部同时走)</option>
</select>
</div>
{mode === "x" && (
<div className="gp-edit-row">
<span className="lb">判据</span>
<textarea value={basis} maxLength={120} rows={2} placeholder="白话可判的一句;卡点写死后果" onChange={(e) => setBasis(e.target.value)} />
</div>
)}
<div className="gp-edit-row">
<span className="lb">判定</span>
<input value={owner} maxLength={12} placeholder={mode === "and" ? "(并行可空)" : "判定人缩写"} onChange={(e) => setOwner(e.target.value)} />
</div>
</>
)}
{node?.kind === "callout" && (
<div className="gp-edit-row">
<span className="lb">去向</span>
<select value={to} onChange={(e) => setTo(e.target.value)}>
<option value="">(不标去向)</option>
{targets.map((id) => (
<option key={id} value={id}>
{id}
</option>
))}
</select>
</div>
)}
{edge && (
<div className="gp-edit-row">
+4 -9
View File
@@ -14,7 +14,6 @@ import { Ico } from '@ui';
import { FlowDiagram } from './FlowDiagram.tsx';
import { FlowEditor } from './FlowEditor.tsx';
import type { FlowEdge, FlowGraph } from './flow-layout.ts';
import type { SpPosition } from './support.tsx';
import { packageToGraph } from './pkg-to-graph.ts';
import type { InstanceMark } from './pkg-to-graph.ts';
import './flow-canvas.css';
@@ -24,7 +23,6 @@ export function FlowFigureBlock({
oneLiner,
graph,
drilledIds,
positions,
canEdit,
onNodeClick,
onEdgeClick,
@@ -36,13 +34,12 @@ export function FlowFigureBlock({
graph: FlowGraph | null;
/** 已建立下钻内容的节点 id(删除这些节点时编辑器会二次确认) */
drilledIds: string[];
positions?: SpPosition[] | undefined;
canEdit: boolean;
onNodeClick: (nodeId: string) => void;
/** 点击连线选中边(属性卡随之切到该边);不传则边不可点 */
onEdgeClick?: ((edge: FlowEdge) => void) | undefined;
/** 双击流程图活动就地改文字(标题 / 主办) */
onEditNodeText?: ((nodeId: string, patch: { label: string; owner?: string }) => Promise<void>) | undefined;
/** 双击流程图活动就地改**名字** —— 图上可编辑的只有名字 */
onEditNodeText?: ((nodeId: string, patch: { label: string }) => Promise<void>) | undefined;
/** 手工绘图:整图保存(idMap = 本次编辑会话 原id→现id,宿主用它重键下钻内容) */
onSaveFlowGraph?: ((graph: FlowGraph, idMap: Record<string, string>) => Promise<void>) | undefined;
}) {
@@ -71,7 +68,6 @@ export function FlowFigureBlock({
<FlowEditor
initial={graph}
drilledIds={drilledIds}
positions={positions}
onSave={async (next, idMap) => {
await onSaveFlowGraph(next, idMap);
setEditing(false);
@@ -93,7 +89,6 @@ export function FlowFigureBlock({
onNodeClick={onNodeClick}
onEdgeClick={onEdgeClick}
onEditNode={canEdit ? onEditNodeText : undefined}
ownerOptions={positions}
ariaLabel={`${title}业务流程图`}
/>
</div>
@@ -111,8 +106,8 @@ export function FlowFigureBlock({
* 读起来像图之外的第二块内容 —— 挪进框内,一眼看出它说的就是这张图上的记号。
* 图标与绘图工具(元素盘)同款 SVG,蓝/琥珀由图标自己带,不必再写一遍「蓝 = …」。
*
* 「责任」那两格删了:编排**不带岗位**(负责人是运营时分配的),
* 图上早已不画责任人角标 —— 图例里留着就是指着一件不存在的东西。
* 「责任」那两格删了:编排**不带岗位**(负责人是运营时分配的)——
* 责任人角标同轮一并从画笔里撤掉,图上不再画它。
*/
function Legend() {
return (
+5 -6
View File
@@ -37,16 +37,15 @@ export interface FlowNode {
tone?: "ok" | "bad";
/** 仅卡点:去向节点 id(回跳返工/前跳直达)——渲染时框内标 →id。 */
to?: string | undefined;
/** 责任下沉:活动=主办(A);网关=判定人。活动框内角标,网关随判据进浮层。 */
owner?: string | undefined;
/*
* (已退役 2026-07-30)owner / in / out / use —— 责任人角标与「进/出/查」数据线。
* 编排**不带岗位**(谁来干是运营时分配的),**也不声明数据**(契约归活动自己)。
* 属性卡里那四行早已写了不生效(owner/use 连 ProcessNode 都没有),一并撤掉。
*/
/** 判据下沉:仅网关——判定标准(节点数据;画布不再弹窗,导出派生表可见)。 */
basis?: string | undefined;
/** 仅网关:x=判断(×,默认)/ and=并行(+,所有出口同时走)。 */
mode?: "x" | "and" | undefined;
/** 数据线下沉(仅活动):进/出=流转,查=底座——L2 图后「数据 · 台账与加工」段由此派生。 */
in?: string[] | undefined;
out?: string[] | undefined;
use?: string[] | undefined;
/** 执行基数(exec-spec):item=逐条办;缺省=跟单办。 */
per?: "item";
/** 人机分工:auto 自动 / assist 帮我做 / human 我来做(缺省)。 */
@@ -159,25 +159,7 @@ export function paintNode(group: SVGElement, node: FlowNode, w: number, h: numbe
svgEl("text", { x: 6, y: 11, "font-family": FLOW_MONO, "font-size": 8.5, fill: "#8A93A0" }, node.code ?? node.id),
);
}
// 责任下沉:执行人角标在右上——图即责任表。
if (node.owner && !ghost) {
const ownerFit = fitLine(node.owner, w * 0.55, 8.5, 7);
group.append(
svgEl(
"text",
{
x: w - 6,
y: 11,
"text-anchor": "end",
"font-family": FLOW_SANS,
"font-size": ownerFit.size,
"font-weight": 600,
fill: skin.stroke,
},
ownerFit.text,
),
);
}
/* 责任人角标已退役(2026-07-30):编排不带岗位 —— 谁来干是运营时分配的,不画在图上。 */
// 分工角标已随词签退役(2026-07 user「删除 AI/系统/人 选项配置」):步骤图不再画 assign 角标。
}
}
@@ -82,8 +82,6 @@ export function mergeGraphIntoPackage(
// 契约不带负责人岗位:分派归运营(运行时默认全员轮值;审批点的 approver 是功能语义,另算)
failure: priorAct?.failure ?? { kind: 'terminate' as const },
...(priorAct?.message === undefined ? {} : { message: priorAct.message }),
...(n.in === undefined || n.in.length === 0 ? {} : { in: [...n.in] }),
...(n.out === undefined || n.out.length === 0 ? {} : { out: [...n.out] }),
};
});
@@ -98,8 +98,6 @@ export function packageToGraph(pkg: ProcessPackage, marks: readonly InstanceMark
...base,
// 契约不带负责人岗位 —— 角标不再标岗位,链接的实现(装载面)以 act 携带
act: n.activityId,
...(n.in === undefined ? {} : { in: [...n.in] }),
...(n.out === undefined ? {} : { out: [...n.out] }),
};
});
-50
View File
@@ -1,50 +0,0 @@
/**
* 抽离配套件:画布组件对宿主应用仅有的两个依赖,原样搬来使之自足。
* - SpPosition 来自 apps/web/src/lib/process-types.ts(业务能力/岗位台账行)
* - OwnerSelect 来自 apps/web/src/components/workspace/process/BlockEdit.tsx
* 执行人(owner)是按"能力缩写"的软引用:活动框右上角标、属性卡的执行下拉都用它。
*/
/** 能力岗位:工作总览里的角色台账。 */
export interface SpPosition {
id: string;
name: string;
abbr: string;
icon?: string;
/** 绑定的负责人账号 userId(1:1)。 */
account?: string;
}
/** 执行人下拉:只能选能力缩写,空=不标;遗留值保留为一项防静默改值。 */
export function OwnerSelect({
value,
onChange,
positions,
emptyLabel = "(不标执行人)",
}: {
value: string;
onChange: (v: string) => void;
positions: SpPosition[] | undefined;
emptyLabel?: string;
}) {
// owner 是按缩写的软引用——同缩写只保留一条(否则重复能力会在下拉里重影)。
const seenAbbr = new Set<string>();
const options = (positions ?? []).filter((p) => {
const abbr = p.abbr.trim();
if (!abbr || seenAbbr.has(abbr)) return false;
seenAbbr.add(abbr);
return true;
});
const legacy = value && !options.some((p) => p.abbr === value) ? value : null;
return (
<select value={value} onChange={(e) => onChange(e.target.value)}>
<option value="">{emptyLabel}</option>
{legacy && <option value={legacy}>{legacy}(遗留)</option>}
{options.map((p) => (
<option key={p.id} value={p.abbr}>
{p.abbr} · {p.name}
</option>
))}
</select>
);
}
+5 -6
View File
@@ -147,13 +147,12 @@ export const processNodeSchema = z.discriminatedUnion('kind', [
kind: z.literal('activity'),
/** 以声明入流:引用活动库中已安装的活动 */
activityId: activityIdSchema,
/**
* 契约注记(编排定契约,AM 03):这个节点**声明**吃什么、吐什么。
* 是「导出依赖文件」的骨架 —— 开发者对着声明在系统外开发活动;
* 闭合校验的机检仍以边映射与活动契约面为准,注记是给人读的那一层。
/*
* **节点不声明数据**(2026-07-30 用户定):这里曾有 in/out 两行「这个节点吃什么、吐什么」——
* 那是上一个模型的化石(那时闭合校验以边映射为准)。现在数据契约**全归活动自己**:
* 吃什么、吐什么写在它的 manifest 里,随活动升版;编排面只读它、显示它,一个字段都不碰。
* 节点只决定四件:身份、类型(活动点/审批点)、链接哪个活动、出边条件(往哪走)。
*/
in: z.array(z.string()).optional(),
out: z.array(z.string()).optional(),
/** 版本约束:留空 = 装配时取最新生效版 */
activityVersion: z.string().optional(),
/*