forked from ChaiHao/BPOS-MVP
清掉「节点决定数据契约」的三处遗留 —— 编排面从此一个字段都不碰
讨论定的界线:节点只决定四件(身份 · 类型 · 链接哪个活动 · 出边条件),
数据契约全归活动自己(它的 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:
@@ -495,8 +495,6 @@ export declare const listProcessPackagesRoute: {
|
|||||||
}, z.core.$strip>, z.ZodObject<{
|
}, z.core.$strip>, z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"activity">;
|
kind: z.ZodLiteral<"activity">;
|
||||||
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
|
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>;
|
activityVersion: z.ZodOptional<z.ZodString>;
|
||||||
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"terminate">;
|
kind: z.ZodLiteral<"terminate">;
|
||||||
@@ -624,8 +622,6 @@ export declare const saveProcessRoute: {
|
|||||||
}, z.core.$strip>, z.ZodObject<{
|
}, z.core.$strip>, z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"activity">;
|
kind: z.ZodLiteral<"activity">;
|
||||||
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
|
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>;
|
activityVersion: z.ZodOptional<z.ZodString>;
|
||||||
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"terminate">;
|
kind: z.ZodLiteral<"terminate">;
|
||||||
@@ -737,8 +733,6 @@ export declare const saveProcessRoute: {
|
|||||||
}, z.core.$strip>, z.ZodObject<{
|
}, z.core.$strip>, z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"activity">;
|
kind: z.ZodLiteral<"activity">;
|
||||||
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
|
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>;
|
activityVersion: z.ZodOptional<z.ZodString>;
|
||||||
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"terminate">;
|
kind: z.ZodLiteral<"terminate">;
|
||||||
@@ -883,8 +877,6 @@ export declare const setProcessStateRoute: {
|
|||||||
}, z.core.$strip>, z.ZodObject<{
|
}, z.core.$strip>, z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"activity">;
|
kind: z.ZodLiteral<"activity">;
|
||||||
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
|
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>;
|
activityVersion: z.ZodOptional<z.ZodString>;
|
||||||
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"terminate">;
|
kind: z.ZodLiteral<"terminate">;
|
||||||
@@ -1008,8 +1000,6 @@ export declare const deleteProcessVersionRoute: {
|
|||||||
}, z.core.$strip>, z.ZodObject<{
|
}, z.core.$strip>, z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"activity">;
|
kind: z.ZodLiteral<"activity">;
|
||||||
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
|
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>;
|
activityVersion: z.ZodOptional<z.ZodString>;
|
||||||
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"terminate">;
|
kind: z.ZodLiteral<"terminate">;
|
||||||
@@ -1134,8 +1124,6 @@ export declare const renameProcessRoute: {
|
|||||||
}, z.core.$strip>, z.ZodObject<{
|
}, z.core.$strip>, z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"activity">;
|
kind: z.ZodLiteral<"activity">;
|
||||||
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
|
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>;
|
activityVersion: z.ZodOptional<z.ZodString>;
|
||||||
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"terminate">;
|
kind: z.ZodLiteral<"terminate">;
|
||||||
@@ -1251,8 +1239,6 @@ export declare const validateProcessRoute: {
|
|||||||
}, z.core.$strip>, z.ZodObject<{
|
}, z.core.$strip>, z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"activity">;
|
kind: z.ZodLiteral<"activity">;
|
||||||
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
|
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>;
|
activityVersion: z.ZodOptional<z.ZodString>;
|
||||||
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"terminate">;
|
kind: z.ZodLiteral<"terminate">;
|
||||||
@@ -2380,8 +2366,6 @@ export declare const govWorkflowRoute: {
|
|||||||
}, z.core.$strip>, z.ZodObject<{
|
}, z.core.$strip>, z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"activity">;
|
kind: z.ZodLiteral<"activity">;
|
||||||
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
|
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>;
|
activityVersion: z.ZodOptional<z.ZodString>;
|
||||||
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"terminate">;
|
kind: z.ZodLiteral<"terminate">;
|
||||||
|
|||||||
@@ -135,8 +135,6 @@ export declare const processNodeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|||||||
}, z.core.$strip>, z.ZodObject<{
|
}, z.core.$strip>, z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"activity">;
|
kind: z.ZodLiteral<"activity">;
|
||||||
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
|
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>;
|
activityVersion: z.ZodOptional<z.ZodString>;
|
||||||
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"terminate">;
|
kind: z.ZodLiteral<"terminate">;
|
||||||
@@ -249,8 +247,6 @@ export declare const processPackageSchema: z.ZodObject<{
|
|||||||
}, z.core.$strip>, z.ZodObject<{
|
}, z.core.$strip>, z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"activity">;
|
kind: z.ZodLiteral<"activity">;
|
||||||
activityId: z.core.$ZodBranded<z.ZodString, "ActivityId", "out">;
|
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>;
|
activityVersion: z.ZodOptional<z.ZodString>;
|
||||||
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
failure: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
||||||
kind: z.ZodLiteral<"terminate">;
|
kind: z.ZodLiteral<"terminate">;
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ import {
|
|||||||
type ShapeLayout,
|
type ShapeLayout,
|
||||||
} from "./flow-layout";
|
} from "./flow-layout";
|
||||||
import { arrowHeadPoints, CAPTION_STYLE, FLOW_MONO, paintNode, svgEl as svg, TONE_COLOR } from "./flow-node-paint";
|
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";
|
import "diagram-js/assets/diagram-js.css";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,9 +143,9 @@ export interface FlowDiagramProps {
|
|||||||
/** 点击连线(选中边,按边号/两端认);不传则边不可点。 */
|
/** 点击连线(选中边,按边号/两端认);不传则边不可点。 */
|
||||||
onEdgeClick?: ((edge: FlowEdge) => void) | undefined;
|
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 业务能力)——传了则执行字段只能下拉选缩写,不开自由填。 */
|
/** 执行人候选(L1 业务能力)——传了则执行字段只能下拉选缩写,不开自由填。 */
|
||||||
ownerOptions?: SpPosition[] | undefined;
|
|
||||||
ariaLabel: string;
|
ariaLabel: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +155,7 @@ interface NodeEdit {
|
|||||||
top: number;
|
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 containerRef = useRef<HTMLDivElement>(null);
|
||||||
const editRef = useRef<HTMLDivElement>(null);
|
const editRef = useRef<HTMLDivElement>(null);
|
||||||
const clickRef = useRef(onNodeClick);
|
const clickRef = useRef(onNodeClick);
|
||||||
@@ -170,7 +168,7 @@ export function FlowDiagram({ graph, variant, onNodeClick, onEdgeClick, onEditNo
|
|||||||
const drillTimer = useRef<number | null>(null);
|
const drillTimer = useRef<number | null>(null);
|
||||||
// 活动文字就地编辑浮层(双击活动)。
|
// 活动文字就地编辑浮层(双击活动)。
|
||||||
const [nodeEdit, setNodeEdit] = useState<NodeEdit | 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 [editBusy, setEditBusy] = useState(false);
|
||||||
const [editErr, setEditErr] = useState<string | null>(null);
|
const [editErr, setEditErr] = useState<string | null>(null);
|
||||||
|
|
||||||
@@ -254,7 +252,7 @@ export function FlowDiagram({ graph, variant, onNodeClick, onEdgeClick, onEditNo
|
|||||||
window.clearTimeout(drillTimer.current);
|
window.clearTimeout(drillTimer.current);
|
||||||
drillTimer.current = null;
|
drillTimer.current = null;
|
||||||
}
|
}
|
||||||
setEditDraft({ label: bo.node.label, owner: bo.node.owner ?? "" });
|
setEditDraft({ label: bo.node.label });
|
||||||
setEditErr(null);
|
setEditErr(null);
|
||||||
setEditBusy(false);
|
setEditBusy(false);
|
||||||
setNodeEdit({ node: bo.node, ...overlayPos(event.originalEvent, 300, 190) });
|
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>
|
<span className="lb">标题</span>
|
||||||
<input value={editDraft.label} maxLength={16} onChange={(e) => setEditDraft((d) => ({ ...d, label: e.target.value }))} />
|
<input value={editDraft.label} maxLength={16} onChange={(e) => setEditDraft((d) => ({ ...d, label: e.target.value }))} />
|
||||||
</div>
|
</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>}
|
{editErr && <p className="gp-basis muted" style={{ color: "#8f3330" }}>{editErr}</p>}
|
||||||
<div className="gp-btns">
|
<div className="gp-btns">
|
||||||
<button type="button" className="be-btn ghost" disabled={editBusy} onClick={() => setNodeEdit(null)}>
|
<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);
|
setEditBusy(true);
|
||||||
setEditErr(null);
|
setEditErr(null);
|
||||||
editNodeRef
|
editNodeRef
|
||||||
.current?.(nodeEdit.node.id, {
|
.current?.(nodeEdit.node.id, { label: editDraft.label.trim() })
|
||||||
label: editDraft.label.trim(),
|
|
||||||
...(editDraft.owner.trim() === "" ? {} : { owner: editDraft.owner.trim() }),
|
|
||||||
})
|
|
||||||
.then(() => setNodeEdit(null))
|
.then(() => setNodeEdit(null))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setEditErr(err instanceof Error ? err.message : "保存失败");
|
setEditErr(err instanceof Error ? err.message : "保存失败");
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
import { Ico } from "@ui";
|
import { Ico } from "@ui";
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import type { FlowEdge, FlowGraph, FlowNode, FlowNodeKind } from "./flow-layout.ts";
|
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 { createFlowEditor, syncElementLabel, type FlowEditorHandles } from "./flow-editor-core.ts";
|
||||||
import "diagram-js/assets/diagram-js.css";
|
import "diagram-js/assets/diagram-js.css";
|
||||||
|
|
||||||
@@ -26,8 +24,6 @@ export interface FlowEditorProps {
|
|||||||
/** idMap = 编辑期活动改号映射(自动顺序编号),服务端据此重键 operations。 */
|
/** idMap = 编辑期活动改号映射(自动顺序编号),服务端据此重键 operations。 */
|
||||||
onSave: (graph: FlowGraph, idMap: Record<string, string>) => Promise<void>;
|
onSave: (graph: FlowGraph, idMap: Record<string, string>) => Promise<void>;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
/** 执行人候选(L1 业务能力)——传了则活动属性卡的执行字段只能下拉选缩写。 */
|
|
||||||
positions?: SpPosition[] | undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PropState {
|
interface PropState {
|
||||||
@@ -37,7 +33,7 @@ interface PropState {
|
|||||||
top: number;
|
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 canvasRef = useRef<HTMLDivElement>(null);
|
||||||
const wrapRef = useRef<HTMLDivElement>(null);
|
const wrapRef = useRef<HTMLDivElement>(null);
|
||||||
const editorRef = useRef<FlowEditorHandles | null>(null);
|
const editorRef = useRef<FlowEditorHandles | null>(null);
|
||||||
@@ -239,8 +235,6 @@ export function FlowEditor({ initial, drilledIds, onSave, onCancel, positions }:
|
|||||||
<PropCard
|
<PropCard
|
||||||
key={`${prop.element.id}-${prop.top}`}
|
key={`${prop.element.id}-${prop.top}`}
|
||||||
prop={prop}
|
prop={prop}
|
||||||
registry={editorRef.current?.diagram.get("elementRegistry")}
|
|
||||||
positions={positions}
|
|
||||||
onClose={() => setProp(null)}
|
onClose={() => setProp(null)}
|
||||||
onApply={(patch) => {
|
onApply={(patch) => {
|
||||||
const editor = editorRef.current;
|
const editor = editorRef.current;
|
||||||
@@ -277,14 +271,10 @@ const KIND_NAME: Record<FlowNodeKind, string> = {
|
|||||||
|
|
||||||
function PropCard({
|
function PropCard({
|
||||||
prop,
|
prop,
|
||||||
registry,
|
|
||||||
positions,
|
|
||||||
onClose,
|
onClose,
|
||||||
onApply,
|
onApply,
|
||||||
}: {
|
}: {
|
||||||
prop: PropState;
|
prop: PropState;
|
||||||
registry: Any;
|
|
||||||
positions?: SpPosition[] | undefined;
|
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onApply: (patch: Record<string, unknown>) => void;
|
onApply: (patch: Record<string, unknown>) => void;
|
||||||
}) {
|
}) {
|
||||||
@@ -292,57 +282,30 @@ function PropCard({
|
|||||||
const edge = prop.isEdge ? (prop.element.businessObject as FlowEdge) : undefined;
|
const edge = prop.isEdge ? (prop.element.businessObject as FlowEdge) : undefined;
|
||||||
|
|
||||||
const [label, setLabel] = useState(node?.label ?? edge?.label ?? "");
|
const [label, setLabel] = useState(node?.label ?? edge?.label ?? "");
|
||||||
const [owner, setOwner] = useState(node?.owner ?? "");
|
|
||||||
|
|
||||||
const [flavor, setFlavor] = useState<"sop" | "case" | "approval">(
|
const [flavor, setFlavor] = useState<"sop" | "case" | "approval">(
|
||||||
node?.flavor === "case" ? "case" : node?.flavor === "approval" ? "approval" : "sop",
|
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 ?? "");
|
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 = () => {
|
const save = () => {
|
||||||
if (node) {
|
if (node) {
|
||||||
const patch: Record<string, unknown> = {};
|
const patch: Record<string, unknown> = {};
|
||||||
const text = label.trim();
|
const text = label.trim();
|
||||||
if (text) patch.label = text.slice(0, 16);
|
if (text) patch.label = text.slice(0, 16);
|
||||||
if (node.kind === "activity") {
|
if (node.kind === "activity") {
|
||||||
patch.owner = owner.trim() ? owner.trim().slice(0, 16) : undefined;
|
|
||||||
patch.flavor = flavor === "sop" ? undefined : flavor;
|
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);
|
* **节点不声明数据、不带岗位**(2026-07-30):进/出/查与执行四行已撤 ——
|
||||||
const din = lines(dataIn);
|
* 数据契约归活动自己(它的 manifest),负责人归运营时分配。
|
||||||
const dout = lines(dataOut);
|
* 这四样连同更早退役的 wait/per/assign,保存时一并清掉遗留值。
|
||||||
const duse = lines(dataUse);
|
*/
|
||||||
patch.in = din.length ? din : undefined;
|
patch.owner = undefined;
|
||||||
patch.out = dout.length ? dout : undefined;
|
patch.in = undefined;
|
||||||
patch.use = duse.length ? duse : undefined;
|
patch.out = undefined;
|
||||||
// 活动调用整体退役(2026-07 终拍,user「所有节点都是内置节点」):保存即清遗留 wait。
|
patch.use = undefined;
|
||||||
patch.wait = undefined;
|
patch.wait = undefined;
|
||||||
patch.per = undefined;
|
patch.per = undefined;
|
||||||
patch.assign = 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);
|
onApply(patch);
|
||||||
} else if (edge) {
|
} else if (edge) {
|
||||||
@@ -382,64 +345,12 @@ function PropCard({
|
|||||||
<option value="approval">审批点(琥珀 · 人裁节点)</option>
|
<option value="approval">审批点(琥珀 · 人裁节点)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</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 && (
|
{edge && (
|
||||||
<div className="gp-edit-row">
|
<div className="gp-edit-row">
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { Ico } from '@ui';
|
|||||||
import { FlowDiagram } from './FlowDiagram.tsx';
|
import { FlowDiagram } from './FlowDiagram.tsx';
|
||||||
import { FlowEditor } from './FlowEditor.tsx';
|
import { FlowEditor } from './FlowEditor.tsx';
|
||||||
import type { FlowEdge, FlowGraph } from './flow-layout.ts';
|
import type { FlowEdge, FlowGraph } from './flow-layout.ts';
|
||||||
import type { SpPosition } from './support.tsx';
|
|
||||||
import { packageToGraph } from './pkg-to-graph.ts';
|
import { packageToGraph } from './pkg-to-graph.ts';
|
||||||
import type { InstanceMark } from './pkg-to-graph.ts';
|
import type { InstanceMark } from './pkg-to-graph.ts';
|
||||||
import './flow-canvas.css';
|
import './flow-canvas.css';
|
||||||
@@ -24,7 +23,6 @@ export function FlowFigureBlock({
|
|||||||
oneLiner,
|
oneLiner,
|
||||||
graph,
|
graph,
|
||||||
drilledIds,
|
drilledIds,
|
||||||
positions,
|
|
||||||
canEdit,
|
canEdit,
|
||||||
onNodeClick,
|
onNodeClick,
|
||||||
onEdgeClick,
|
onEdgeClick,
|
||||||
@@ -36,13 +34,12 @@ export function FlowFigureBlock({
|
|||||||
graph: FlowGraph | null;
|
graph: FlowGraph | null;
|
||||||
/** 已建立下钻内容的节点 id(删除这些节点时编辑器会二次确认) */
|
/** 已建立下钻内容的节点 id(删除这些节点时编辑器会二次确认) */
|
||||||
drilledIds: string[];
|
drilledIds: string[];
|
||||||
positions?: SpPosition[] | undefined;
|
|
||||||
canEdit: boolean;
|
canEdit: boolean;
|
||||||
onNodeClick: (nodeId: string) => void;
|
onNodeClick: (nodeId: string) => void;
|
||||||
/** 点击连线选中边(属性卡随之切到该边);不传则边不可点 */
|
/** 点击连线选中边(属性卡随之切到该边);不传则边不可点 */
|
||||||
onEdgeClick?: ((edge: FlowEdge) => void) | undefined;
|
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,宿主用它重键下钻内容) */
|
/** 手工绘图:整图保存(idMap = 本次编辑会话 原id→现id,宿主用它重键下钻内容) */
|
||||||
onSaveFlowGraph?: ((graph: FlowGraph, idMap: Record<string, string>) => Promise<void>) | undefined;
|
onSaveFlowGraph?: ((graph: FlowGraph, idMap: Record<string, string>) => Promise<void>) | undefined;
|
||||||
}) {
|
}) {
|
||||||
@@ -71,7 +68,6 @@ export function FlowFigureBlock({
|
|||||||
<FlowEditor
|
<FlowEditor
|
||||||
initial={graph}
|
initial={graph}
|
||||||
drilledIds={drilledIds}
|
drilledIds={drilledIds}
|
||||||
positions={positions}
|
|
||||||
onSave={async (next, idMap) => {
|
onSave={async (next, idMap) => {
|
||||||
await onSaveFlowGraph(next, idMap);
|
await onSaveFlowGraph(next, idMap);
|
||||||
setEditing(false);
|
setEditing(false);
|
||||||
@@ -93,7 +89,6 @@ export function FlowFigureBlock({
|
|||||||
onNodeClick={onNodeClick}
|
onNodeClick={onNodeClick}
|
||||||
onEdgeClick={onEdgeClick}
|
onEdgeClick={onEdgeClick}
|
||||||
onEditNode={canEdit ? onEditNodeText : undefined}
|
onEditNode={canEdit ? onEditNodeText : undefined}
|
||||||
ownerOptions={positions}
|
|
||||||
ariaLabel={`${title}业务流程图`}
|
ariaLabel={`${title}业务流程图`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -111,8 +106,8 @@ export function FlowFigureBlock({
|
|||||||
* 读起来像图之外的第二块内容 —— 挪进框内,一眼看出它说的就是这张图上的记号。
|
* 读起来像图之外的第二块内容 —— 挪进框内,一眼看出它说的就是这张图上的记号。
|
||||||
* 图标与绘图工具(元素盘)同款 SVG,蓝/琥珀由图标自己带,不必再写一遍「蓝 = …」。
|
* 图标与绘图工具(元素盘)同款 SVG,蓝/琥珀由图标自己带,不必再写一遍「蓝 = …」。
|
||||||
*
|
*
|
||||||
* 「责任」那两格删了:编排**不带岗位**(负责人是运营时分配的),
|
* 「责任」那两格删了:编排**不带岗位**(负责人是运营时分配的)——
|
||||||
* 图上早已不画责任人角标 —— 图例里留着就是指着一件不存在的东西。
|
* 责任人角标同轮一并从画笔里撤掉,图上不再画它。
|
||||||
*/
|
*/
|
||||||
function Legend() {
|
function Legend() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -37,16 +37,15 @@ export interface FlowNode {
|
|||||||
tone?: "ok" | "bad";
|
tone?: "ok" | "bad";
|
||||||
/** 仅卡点:去向节点 id(回跳返工/前跳直达)——渲染时框内标 →id。 */
|
/** 仅卡点:去向节点 id(回跳返工/前跳直达)——渲染时框内标 →id。 */
|
||||||
to?: string | undefined;
|
to?: string | undefined;
|
||||||
/** 责任下沉:活动=主办(A);网关=判定人。活动框内角标,网关随判据进浮层。 */
|
/*
|
||||||
owner?: string | undefined;
|
* (已退役 2026-07-30)owner / in / out / use —— 责任人角标与「进/出/查」数据线。
|
||||||
|
* 编排**不带岗位**(谁来干是运营时分配的),**也不声明数据**(契约归活动自己)。
|
||||||
|
* 属性卡里那四行早已写了不生效(owner/use 连 ProcessNode 都没有),一并撤掉。
|
||||||
|
*/
|
||||||
/** 判据下沉:仅网关——判定标准(节点数据;画布不再弹窗,导出派生表可见)。 */
|
/** 判据下沉:仅网关——判定标准(节点数据;画布不再弹窗,导出派生表可见)。 */
|
||||||
basis?: string | undefined;
|
basis?: string | undefined;
|
||||||
/** 仅网关:x=判断(×,默认)/ and=并行(+,所有出口同时走)。 */
|
/** 仅网关:x=判断(×,默认)/ and=并行(+,所有出口同时走)。 */
|
||||||
mode?: "x" | "and" | undefined;
|
mode?: "x" | "and" | undefined;
|
||||||
/** 数据线下沉(仅活动):进/出=流转,查=底座——L2 图后「数据 · 台账与加工」段由此派生。 */
|
|
||||||
in?: string[] | undefined;
|
|
||||||
out?: string[] | undefined;
|
|
||||||
use?: string[] | undefined;
|
|
||||||
/** 执行基数(exec-spec):item=逐条办;缺省=跟单办。 */
|
/** 执行基数(exec-spec):item=逐条办;缺省=跟单办。 */
|
||||||
per?: "item";
|
per?: "item";
|
||||||
/** 人机分工:auto 自动 / assist 帮我做 / human 我来做(缺省)。 */
|
/** 人机分工: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),
|
svgEl("text", { x: 6, y: 11, "font-family": FLOW_MONO, "font-size": 8.5, fill: "#8A93A0" }, node.code ?? node.id),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// 责任下沉:执行人角标在右上——图即责任表。
|
/* 责任人角标已退役(2026-07-30):编排不带岗位 —— 谁来干是运营时分配的,不画在图上。 */
|
||||||
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 user「删除 AI/系统/人 选项配置」):步骤图不再画 assign 角标。
|
// 分工角标已随词签退役(2026-07 user「删除 AI/系统/人 选项配置」):步骤图不再画 assign 角标。
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,8 +82,6 @@ export function mergeGraphIntoPackage(
|
|||||||
// 契约不带负责人岗位:分派归运营(运行时默认全员轮值;审批点的 approver 是功能语义,另算)
|
// 契约不带负责人岗位:分派归运营(运行时默认全员轮值;审批点的 approver 是功能语义,另算)
|
||||||
failure: priorAct?.failure ?? { kind: 'terminate' as const },
|
failure: priorAct?.failure ?? { kind: 'terminate' as const },
|
||||||
...(priorAct?.message === undefined ? {} : { message: priorAct.message }),
|
...(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,
|
...base,
|
||||||
// 契约不带负责人岗位 —— 角标不再标岗位,链接的实现(装载面)以 act 携带
|
// 契约不带负责人岗位 —— 角标不再标岗位,链接的实现(装载面)以 act 携带
|
||||||
act: n.activityId,
|
act: n.activityId,
|
||||||
...(n.in === undefined ? {} : { in: [...n.in] }),
|
|
||||||
...(n.out === undefined ? {} : { out: [...n.out] }),
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -147,13 +147,12 @@ export const processNodeSchema = z.discriminatedUnion('kind', [
|
|||||||
kind: z.literal('activity'),
|
kind: z.literal('activity'),
|
||||||
/** 以声明入流:引用活动库中已安装的活动 */
|
/** 以声明入流:引用活动库中已安装的活动 */
|
||||||
activityId: activityIdSchema,
|
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(),
|
activityVersion: z.string().optional(),
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user