添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

bpmnjs GitHub 官方案例

bpmn 行业规范 (bpmnjs 是按照该规范进行的)

第五和第六点了解一下当中的 API,不要去关心 bpmn-js-properties-panel 导出的任何东西。

所有的 API 都在官方的 demo 文档中:当前官方的包版本和以前的包版本对比有差别,所以很多导出方式可能会变化

1
2
3
4
5
6
7
8
9
// 当前版本 版本不一样使用方式有出入
"dependencies": {
"bpmn-js": "^12.0.0",
// 下面的按需导入
"@bpmn-io/properties-panel": "^1.7.0",
"bpmn-js-properties-panel": "^1.20.3",
"camunda-bpmn-moddle": "^7.0.1",
"zeebe-bpmn-moddle": "^0.18.0"
},

npm i bpmn-js

官方提供的属性面板(在真实的开发中不需要用到,因为我们自己需要完全的实现整个属性面板)

npm install --save bpmn-js-properties-panel @bpmn-io/properties-panel

一、创建一个流程设计器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import BpmnModeler from "bpmn-js/lib/Modeler";
// 文字样式
import "bpmn-js/dist/assets/bpmn-font/css/bpmn.css";
// 图样式
import "bpmn-js/dist/assets/diagram-js.css";
useEffect(() => {
console.log("useeffect enter");
const bpmnModeler = new BpmnModeler({
container: canvasRef.current as HTMLDivElement,
});
// 记录实例
bpmnModelerRef.current = bpmnModeler;
return () => {
console.log("useeffect out");
// 清除监听事件
bpmnModeler.clear();
// 摧毁dom创建
bpmnModeler.destroy();
};
}, []);

return <div className={styles.canvas} ref={canvasRef} />;

二、导入已有的流程

官方案例: https://github.com/bpmn-io/bpmn-js-examples/tree/master/i18n

1
2
3
4
5
6
7
8
// getMockBpmnData请求xml数据 使用bpmnModeler.importXML(data);导入
getMockBpmnData()
.then((data) => {
return bpmnModeler.importXML(data);
})
.then((ImportXMLResult) => {
console.log(ImportXMLResult, "ImportXMLResult");
});

Mock data 数据展示:

三、i18n 国际化

官方案例: https://github.com/bpmn-io/bpmn-js-examples/tree/master/i18n

translations.js
1
2
3
4
5
6
7
8
9
10
11
/**
* This is a sample file that should be replaced with the actual translation.
*
* Checkout https://github.com/bpmn-io/bpmn-js-i18n for a list of available
* translations and labels to translate.
*/
// 简单来说就是在页面上你见到的所有英文都是这儿的KEY ,value就是你想显示的值
export default {
"Change element": "改变元素",
"Activate the create/remove space tool": "启动创建/删除空间工具",
};
customTranslate.js
1
2
3
4
5
6
7
8
9
10
11
12
13
import translations from "./translations";

export default function customTranslate(template, replacements) {
replacements = replacements || {};

// Translate
template = translations[template] || template;

// Replace
return template.replace(/{([^}]+)}/g, function (_, key) {
return replacements[key] || "{" + key + "}";
});
}
index.ts
1
2
3
4
5
6
7
const customTranslateModule = {
translate: ["value", customTranslate],
};
const bpmnModeler = new BpmnModeler({
container: canvasRef.current as HTMLDivElement,
additionalModules: [customTranslateModule],
});

四、保存数据(XML 和 SVG 数据)

关键代码在加粗段

saveData
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
const downloadFile = (url: string, fileName: string) => {
// 创建一个隐藏的<a>元素
const a = document.createElement("a");
a.style.display = "none";
document.body.appendChild(a);

// 设置<a>元素的href和download属性,并触发点击事件
a.href = url;
a.download = fileName;
a.click();

// 删除<a>元素
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
};
// 获取 xml
const getXML = async () => {
// 在 useeffect 中 new bpmn 的时候保存下来的
const bpmnModeler = bpmnModelerRef.current;
if (!bpmnModeler) return "";
const { xml = "" } = await bpmnModeler.saveXML({ format: true });
return xml;
};
// 获取 svg
const getSVG = async () => {
const bpmnModeler = bpmnModelerRef.current;
if (!bpmnModeler) return "";
const { svg = "" } = await bpmnModeler.saveSVG();
return svg;
};
// 发送请求 保存数据
const handleSave = async () => {
const xml = await getXML();
// todo 业务处理
message.success("保存成功");
};
// 保存 xml 文件
const handleSvaeBpmnFile = async () => {
const data = await getXML();
var encodedData = encodeURIComponent(data);
downloadFile(
"data:application/bpmn20-xml;charset=UTF-8," + encodedData,
"diagram.bpmn"
);
};
// 保存 svg
const handleSvaeSvgFile = async () => {
const svg = await getSVG();
var encodedData = encodeURIComponent(svg);
downloadFile(
"data:application/bpmn20-xml;charset=UTF-8," + encodedData,
"diagram.svg"
);
};

五、接入属性面板-基础

官方案例: https://github.com/bpmn-io/bpmn-js-examples/tree/master/properties-panel

(实际工作中 不要使用 官方提供的 bpmn-js-properties-panel 包)

npm install --save bpmn-js-properties-panel @bpmn-io/properties-panel

1
2
3
4
5
6
7
8
9
.properties-panel {
width: 400px;
position: absolute;
right: 0;
top: 0;
height: 100vh;
border-left: 1px solid #ccc;
background-color: #fcfcfc;
}
1
2
<div className="{styles.canvas}" ref="{canvasRef}" />
<div className={styles["properties-panel"]} ref={propertiesPanelRef} />
1
2
3
4
5
6
7
8
9
10
11
12
13
const {
BpmnPropertiesPanelModule,
BpmnPropertiesProviderModule,
} = require("bpmn-js-properties-panel");
import "bpmn-js-properties-panel/dist/assets/properties-panel.css";
const bpmnModeler = new BpmnModeler({
container: canvasRef.current as HTMLDivElement,
additionalModules: [BpmnPropertiesPanelModule, BpmnPropertiesProviderModule],
// @ts-ignore
propertiesPanel: {
parent: propertiesPanelRef.current,
},
});

六、自定义扩展属性面板(基于 bpmn-js-properties-panel 工作中不要使用)

就是给某一个流程节点添加表单项

官方案例: https://github.com/bpmn-io/bpmn-js-examples/tree/master/properties-panel-extension

自定义扩展属性面板包含了以下几个步骤:

  • 创建一个名为 Magic 的组: provider/MagicPropertiesProvider.ts
  • provider/MagicPropertiesProvider.ts
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    import {
    TextFieldEntry,
    isTextFieldEntryEdited,
    } from "@bpmn-io/properties-panel";
    import { useService } from "bpmn-js-properties-panel";
    import { is } from "bpmn-js/lib/util/ModelUtil";
    const LOW_PRIORITY = 500;
    export default function PropertiesProvider(propertiesPanel, translate) {
    // 注册自己的自定义属性提供程序。
    // 使用较低的优先级 LOW_PRIORITY 确保它在基本 BPMN 属性之后加载。
    propertiesPanel.registerProvider(LOW_PRIORITY, {
    getGroups: (element: React.ReactDOM) => {
    return function (groups: Array<any>) {
    // 判断当前选中节点是不是 bpmn:StartEvent 开始节点
    if (is(element, "bpmn:StartEvent")) {
    // 添加一个分组 magic
    groups.push({
    id: "magic",
    label: translate("Magic properties"),
    // 在当前分组添加多个表单项
    entries: [
    {
    id: "spell",
    element,
    component: Spell,
    isEdited: isTextFieldEntryEdited,
    },
    ],
    });
    }
    return groups;
    };
    },
    });
    }
    // 创建输入组件
    function Spell({ element, id }) {
    // 当前节点的操作对象,内部包含各种方法
    const modeling = useService("modeling");
    const translate = useService("translate");
    const debounce = useService("debounceInput");

    const getValue = () => {
    return element.businessObject.spell || "";
    };
    const setValue = (value) => {
    modeling.updateProperties(element, {
    spell: value,
    });
    };
    // bpmn-js-properties-panel 提供的表单组件
    return TextFieldEntry({
    id,
    element,
    debounce,
    description: translate("Apply a black magic spell"),
    label: translate("Spell"),
    getValue,
    setValue,
    });
    }
  • 导出程序模块: provider/index.ts
  • provider/index.ts
    1
    2
    3
    4
    5
    6
    7
    8
    import MagicPropertiesProvider from "./MagicPropertiesProvider";

    export const magicPropertiesProviderModule = {
    // 初始化 Key
    **init**: ["magicPropertiesProvider1"],
    // 上面对应的 key
    magicPropertiesProvider1: ["type", MagicPropertiesProvider],
    };
  • 创建模组扩展: magic.json
  • 它定义了一个名为"Magic"的自定义属性面板插件,其中包含一个类型扩展"BewitchedStartEvent"。该类型扩展继承自"BPMN:StartEvent",并添加了一个名为"spell"的属性,该属性是一个字符串类型,并使用"isAttr"标志将其标记为属性而不是元素。此外,该插件未指定任何关联或其他类型扩展。

    magic.json
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {
    "name": "Magic",
    "prefix": "magic",
    "uri": "https://magic",
    "xml": {
    "tagAlias": "lowerCase"
    },
    "associations": [],
    "types": [
    {
    "name": "BewitchedStartEvent",
    "extends": ["bpmn:StartEvent"],
    "properties": [
    {
    "name": "spell",
    "isAttr": true,
    "type": "String"
    }
    ]
    }
    ]
    }

    Q:“isAttr”: true,什么意思?

    “isAttr”: true 表示将属性标记为 XML 属性,而不是 XML 元素。在 XML 文档中,属性是与元素相关联的名称-值对,而元素是由开始标记、内容和结束标记组成的结构。将属性标记为 XML 属性可以使它们作为元素的属性而不是子元素出现在 XML 文档中。在上面的代码中,“spell” 属性被标记为 XML 属性,这意味着它会作为 “BewitchedStartEvent” 元素的一个属性出现在 XML 文档中,而不是作为子元素嵌套在 “BewitchedStartEvent” 元素中。

    Q:“xml”: {“tagAlias”: “lowerCase”}?

    “xml” 对象定义了用于该扩展的 XML 映射选项。“tagAlias” 是其中的一个选项,它指定 XML 元素名称在生成 XML 时应该使用的别名。在这里,“lowerCase” 是该别名,这意味着所有生成的 XML 元素名称都将以小写字母表示。例如,在上面的代码中,“BewitchedStartEvent” 类型扩展将生成一个 “bewitchedstartevent” 的元素名称,而不是 “BewitchedStartEvent”。这个选项可以使生成的 XML 更加规范化和易于处理。

    Q: “prefix”: “magic”,“uri”: “ https://magic ”?

    “prefix” 和 “uri” 属性用于指定 XML 命名空间的前缀和 URI。在 XML 中,命名空间用于标识 XML 元素和属性的来源和所属。在 BPMN 中,命名空间用于将自定义元素和属性与标准 BPMN 元素和属性区分开来。在这里,“magic” 是命名空间的前缀,“ https://magic ” 是命名空间的 URI。在 XML 中,命名空间前缀通常与命名空间 URI 相关联,以便标识元素和属性所属的命名空间。例如,在上面的代码中,“BewitchedStartEvent” 类型扩展将被认为属于 “ https://magic ” 命名空间,因为它使用了 “magic” 前缀。这样,可以确保自定义的 BPMN 元素和属性不会与标准的 BPMN 元素和属性冲突,从而实现更好的互操作性和扩展性。

  • 注册 bpmn 的时候添加该模块
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import magicModdleDescriptor from "./descriptors/magic.json";
    import { magicPropertiesProviderModule } from "./provider/index";
    const bpmnModeler = new BpmnModeler({
    container: canvasRef.current as HTMLDivElement,
    // @ts-ignore
    propertiesPanel: {
    parent: propertiesPanelRef.current,
    },
    additionalModules: [
    customTranslateModule,
    BpmnPropertiesPanelModule,
    BpmnPropertiesProviderModule,
    + magicPropertiesProviderModule,
    ],
    + moddleExtensions: {
    + magic: magicModdleDescriptor,
    + },
    });

    上述方式是官方实现方式,并不是理想的实现方式。接入三方组件实现往下看

    七、三方组件实现属性面板-原理

    在写这一点的时候,本来打算是按照官方的案例依次写下来的,但是看了网上各作者的实现方式,豁然开朗。

    在 1-6 的文档说明中,我们使用了部分 API,其中,包括:创建流程设计器、导入、保存、设置字段值、自定义扩展等。

    由于参考了官方实现和三方开发者的开源贡献,得出以下结论:

    在流程设计器中最复杂最繁琐的莫非是属性面板和操作栏的自定义,属性面板设置的值是直接挂载在该元素 DOM 对应的 XML 标签上,那就可以理解为 XML 数据和前端的 DOM 不是强关联,我们只需要告诉 bpmn 该 dom 包含哪些字段和使用 modeling.updateProperties API 给 XML 属性赋值,以至于前端的 DOM 怎么实现就完全脱离了束缚。

    在 6-2 和 6-3 中,我们导出了一个 UI 实现和 JSON,在实现的过程中我也有一个疑问:

    Q: 为什么导出了官方的 UI 的实现,为什么还要导出一个 JSON?UI 组件的实现过程中已经告诉了 bpmn 字段名,为什么 JSON 还要告诉一遍?使用三方的 UI 是否还需要描述文件?

    A:使用官方的 UI 实现是需要 JSON 描述文件的,但自己实现熟悉面板是不需要描述文件的,因为 UI 的实现 bpmn 是不感知的,字段与字段之间的关系是由““6-3.创建模组扩展””的 json 告诉 bpmn 的,可以选择不告诉它,只要保证不使用官方的 UI 实现。也就是说 UI 的实现是完全自由的,只需要在 UI 实现的过程中使用 updateProperties API 设置对应的键值对,而当中的键是必须要在 json 定义的。

    这么讲可能有点干,画个图:
    结论:属性面板的实现可以完全独立于 bpmn,其中的业务逻辑按照我们熟悉的 UI 库写。只要在关键的节点上调用 API 设置表单的值和 bpmnXML 文件的值。

    八、三方组件实现属性面板-实现

    具体实现见 Demo ,提供基本的 API:

  • modeling?.updateProperties 更新属性
  • modeler.on(“selection.changed”,function) 元素变化时触发
  • 实现效果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    // 上一张图片的生成结果
    <?xml ...>
    <bpmn2:process id="Process_1" isExecutable="false" title="bpmn:Process" status="open">
    <bpmn2:startEvent id="Event_0eceo9i" name="开始" title="bpmn:StartEvent" status="open" startElemen="我是开始节点11111">
    <bpmn2:outgoing>Flow_1e1yd6i</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:sequenceFlow id="Flow_1e1yd6i" sourceRef="Event_0eceo9i" targetRef="Gateway_0gyrpxi" />
    <bpmn2:parallelGateway id="Gateway_0gyrpxi" title="bpmn:ParallelGateway" status="open">
    <bpmn2:incoming>Flow_1e1yd6i</bpmn2:incoming>
    <bpmn2:outgoing>Flow_1s4ahir</bpmn2:outgoing>
    <bpmn2:outgoing>Flow_1prys0p</bpmn2:outgoing>
    </bpmn2:parallelGateway>
    <bpmn2:task id="Activity_1h7dpk5" name="网关1" title="bpmn:Task" status="open">
    <bpmn2:incoming>Flow_1s4ahir</bpmn2:incoming>
    <bpmn2:outgoing>Flow_0w5kua8</bpmn2:outgoing>
    </bpmn2:task>
    <bpmn2:sequenceFlow id="Flow_1s4ahir" sourceRef="Gateway_0gyrpxi" targetRef="Activity_1h7dpk5" />
    <bpmn2:task id="Activity_03zx4qv网关1" name="网关1" title="bpmn:Task" status="open">
    <bpmn2:incoming>Flow_1prys0p</bpmn2:incoming>
    <bpmn2:outgoing>Flow_01j42kt</bpmn2:outgoing>
    </bpmn2:task>
    <bpmn2:sequenceFlow id="Flow_1prys0p" sourceRef="Gateway_0gyrpxi" targetRef="Activity_03zx4qv网关1" />
    <bpmn2:endEvent id="Event_0k0pq9f" name="结束" title="bpmn:EndEvent" status="open">
    <bpmn2:incoming>Flow_0w5kua8</bpmn2:incoming>
    <bpmn2:incoming>Flow_01j42kt</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="Flow_0w5kua8" sourceRef="Activity_1h7dpk5" targetRef="Event_0k0pq9f" />
    <bpmn2:sequenceFlow id="Flow_01j42kt" sourceRef="Activity_03zx4qv网关1" targetRef="Event_0k0pq9f" title="bpmn:SequenceFlow" status="open" />
    </bpmn2:process>
    <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    ...这里是描述位置信息的
    </bpmndi:BPMNDiagram>
    </bpmn2:definitions>

    九、属性扩展描述文件规则

    1.bpmn 行业规范

    在 bpmn 中主要分为 4 类元素节点:事件、活动、网关和流

    Events (事件)

  • Start Event (开始事件)
  • Intermediate Event (中间事件)
  • End Event (结束事件)
  • Activities (活动)

  • Task (任务)
  • Sub Process (子流程)
  • Call Activity (调用活动)
  • Gateways (网关)

  • Exclusive Gateway (排他网关)
  • Inclusive Gateway (包容网关)
  • Parallel Gateway (并行网关)
  • Complex Gateway (复杂网关)
  • Event-Based Gateway (基于事件的网关)
  • Sequence Flow (顺序流)
  • Message Flow (消息流)
  • Association (关联)
  • Data Association (数据通讯)
  • 数据来源: https://cloud.trisotech.com/bpmnquickguide/bpmn-quick-guide/bpmn-basics.html

    举例: bpmn:Task 是 BPMN 规范中的一个任务类型,它代表一个需要被执行的工作或活动。下面是 bpmn:Task 的一些常用属性和行为(数据来源 Chat GPT 可能不准确):

  • id :唯一标识符,用于在流程图中引用任务。
  • name :任务的名称。
  • assignee :指定任务的执行者。
  • candidateUsers :可以候选的任务执行者列表。
  • candidateGroups :可以候选的任务执行者所在的组。
  • dueDate :任务的截止日期。
  • priority :任务的优先级。
  • formKey :任务的表单关键字,用于引用任务的表单定义。
  • complete :完成任务,将任务标记为已完成。
  • delegate :委托任务,将任务交给另一个执行者处理。
  • claim :声明任务,将任务标记为已声明并指定执行者。
  • reassign :重新指定任务的执行者。
  • escalate :升级任务,将任务交给更高级别的执行者处理。
  • 需要注意的是,BPMN 规范定义了一组标准的属性和行为,但每个 BPMN 实现可能会根据自己的需求进行扩展或修改。因此,实际使用中可能会存在一些差异。

    在上述内容中,是 bpmn 规范中的基础节点,以下会讲述扩展文件的配置,也就是说我们自己添加节点或者属性:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    {
    "name": "MyBPMN",
    "prefix": "mybpmn",
    "uri": "https://MyBPMN",
    "xml": {
    "tagAlias": "lowerCase"
    },
    "associations": [],
    "types": [
    {
    "name": "CustomServiceTask",
    "extends": ["bpmn:ServiceTask"],
    "properties": [
    {
    "name": "customProperty",
    "type": "String"
    }
    ]
    }
    ]
    }
  • prefix xml 前缀
  • isAttr 代表 customProperty 是以属性的形式存在
  • "tagAlias": "lowerCase" 代表 xml 属性和标签全部使用小写
  • "extends": ["bpmn:ServiceTask"] ,代表 bpmn:ServiceTask 可以使用 CustomServiceTask类型 (不注册也可以使用,但是存储位置不同)
  • 当属性不具有子元素时,可以将其表示为 XML 属性,而不是 XML 元素

    案例二 superClass

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {
    "name": "MyBPMN",
    "prefix": "my",
    "uri": "https://MyBPMN",
    "xml": {
    "tagAlias": "lowerCase"
    },
    "associations": [],
    "types": [
    {
    "name": "CustomTask",
    "superClass": ["bpmn:Task"],
    "properties": [
    {
    "name": "customProperty",
    "type": "String",
    "isAttr": true
    }
    ]
    }
    ]
    }
  • superClass :CustomTask 继承了 Task 的所有属性
  • 案例三 列表属性扩展

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    {
    "name": "MyBPMN",
    "prefix": "my",
    "uri": "https://MyBPMN",
    "xml": {
    "tagAlias": "lowerCase"
    },
    "associations": [],
    "types": [
    {
    "name": "Extensions",
    "superClass": ["Element"],
    "properties": [
    {
    "name": "extensions",
    "isMany": true,
    "type": "Extension"
    }
    ]
    },
    {
    "name": "Extension",
    "properties": [
    {
    "name": "key",
    "isAttr": true,
    "type": "String",
    "default": "默认值"
    }
    ]
    }
    ]
    }
  • 该描述文件定义了一个 Extension 的类型: {key:string}
  • 又定义了一个 Extensions 类型: {"extensions":[{key:string}]}
  • isMany 是一个数组
  • default :默认值
  • 在 bpmnjs 中如果不使用官方提供的 UI 组件,可以不用注册描述文件,也就是说不用提前在 json 文件中声明类型和字段

    需要注意的是如果不在文件中注册字段,数据保存在 Element?.businessObject.$attrs ,注册了的或者原生节点内置的字段存在于 Element?.businessObject

    4.总结和最优解

  • 在实际的开发中,如果不是新的 UI 节点,只是简单的属性那么就不要在描述文件中注册。
  • 只保证在描述文件中注册新的 UI 节点,并且也不要注册属性。
  • 初始值的设置和数据同步
  • 也就是说在定义节点属性的时候你不用照着“ 2.案例 ”写
  • 在节点变化的时候,将节点的值设置进表单。如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    useEffect(() => {
    // 节点变化时触发
    if (!currentElement) return;
    formRef.current?.resetFields();
    const data = currentElement?.businessObject.$attrs;
    formRef.current?.setFieldsValue({
    id: currentElement?.id,
    ...data,
    });
    synchronousXMLData();
    }, [currentElement]);


    const synchronousXMLData = () => {
    const formData = formRef.current?.getFieldsValue();
    modeling?.updateProperties(currentElement, formData);
    };
    // 伪代码 在表单的字段变化是触发
    onFieldsChange={() => {
    synchronousXMLData();
    }}

    属性面板示例,完整的 react 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    import {
    BetaSchemaForm,
    ProFormColumnsType,
    ProFormInstance,
    } from "@ant-design/pro-components";
    import { useContext, useEffect, useRef } from "react";
    import { GlobalContext } from "..";
    export default () => {
    const { bpmnInstance } = useContext(GlobalContext);
    const { currentElement, modeling, modeler } = bpmnInstance || {};
    const { type: currentElementType } = currentElement || {};
    const formRef = useRef<ProFormInstance>();
    useEffect(() => {
    if (!currentElement) return;
    formRef.current?.resetFields();
    console.log(currentElement?.businessObject);
    const data = currentElement?.businessObject.$attrs;
    formRef.current?.setFieldsValue({
    id: currentElement?.id,
    ...data,
    });
    synchronousXMLData();
    }, [currentElement]);
    const columns: ProFormColumnsType<BpmnAPI.record>[] = [
    {
    title: "ID",
    dataIndex: "id",
    formItemProps: {
    rules: [
    {
    required: true,
    message: "此项为必填项",
    },
    ],
    },
    },
    {
    title: "标题",
    dataIndex: "title",
    initialValue: currentElementType,
    formItemProps: {
    rules: [
    {
    required: true,
    message: "此项为必填项",
    },
    ],
    },
    },
    {
    title: "状态",
    initialValue: "open",
    dataIndex: "status",
    valueEnum: {
    open: { text: "开启" },
    close: { text: "关闭" },
    },
    },
    {
    title: "开始节点",
    initialValue: "我是开始节点",
    dataIndex: "startElemen",
    hideInForm: currentElementType !== "bpmn:StartEvent",
    },
    ];

    const synchronousXMLData = () => {
    const formData = formRef.current?.getFieldsValue();
    modeling?.updateProperties(currentElement, formData);
    };
    return (
    <BetaSchemaForm<BpmnAPI.record>
    formRef={formRef}
    onFieldsChange={() => {
    synchronousXMLData();
    }}
    columns={columns}
    />
    );
    };