@jiangood/open-admin-flowable 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/package.json +38 -0
  2. package/src/layouts/index.jsx +13 -0
  3. package/src/pages/flowable/design/PropertiesPanel.jsx +167 -0
  4. package/src/pages/flowable/design/contextPad.js +50 -0
  5. package/src/pages/flowable/design/customTranslate/customTranslate.js +16 -0
  6. package/src/pages/flowable/design/customTranslate/translations-properties-panel.js +10 -0
  7. package/src/pages/flowable/design/customTranslate/translations.js +144 -0
  8. package/src/pages/flowable/design/descriptors/flowable.json +1109 -0
  9. package/src/pages/flowable/design/index.css +7 -0
  10. package/src/pages/flowable/design/index.jsx +163 -0
  11. package/src/pages/flowable/design/provider/properties/AssignmentSection.jsx +74 -0
  12. package/src/pages/flowable/design/provider/properties/ConditionDesign.jsx +292 -0
  13. package/src/pages/flowable/design/provider/properties/ConditionExpressionUtils.js +53 -0
  14. package/src/pages/flowable/design/provider/properties/ConditionProps.jsx +66 -0
  15. package/src/pages/flowable/design/provider/properties/DelegateExpressionProps.jsx +29 -0
  16. package/src/pages/flowable/design/provider/properties/FormProps.jsx +28 -0
  17. package/src/pages/flowable/design/provider/properties/GeneralSection.jsx +21 -0
  18. package/src/pages/flowable/design/provider/properties/MultiInstanceProps.jsx +37 -0
  19. package/src/pages/flowable/index.jsx +87 -0
  20. package/src/pages/flowable/monitor/definition.jsx +87 -0
  21. package/src/pages/flowable/monitor/instance/index.jsx +82 -0
  22. package/src/pages/flowable/monitor/instance/view.jsx +102 -0
  23. package/src/pages/flowable/monitor/task.jsx +98 -0
  24. package/src/pages/flowable/simulate/index.jsx +400 -0
  25. package/src/pages/flowable/user-task/form.jsx +183 -0
  26. package/src/pages/flowable/user-task/index.jsx +184 -0
  27. package/src/pages/flowable/user-task/instance/view.jsx +85 -0
@@ -0,0 +1,7 @@
1
+
2
+ /* 隐藏bpmnjs的logo */
3
+ .bjs-powered-by {
4
+ display: none;
5
+ }
6
+
7
+
@@ -0,0 +1,163 @@
1
+ import React from "react";
2
+ import {Button, Card, message, Modal, Space, Splitter} from "antd";
3
+
4
+ import 'bpmn-js/dist/assets/diagram-js.css'
5
+ import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
6
+ import BpmnModeler from 'bpmn-js/lib/Modeler'
7
+
8
+ import './index.css'
9
+ import customTranslate from "./customTranslate/customTranslate";
10
+ import contextPad from "./contextPad";
11
+ import {CloudUploadOutlined, SaveOutlined} from "@ant-design/icons";
12
+ import {HttpUtils, PageLoading, PageUtils, ProTable} from "@jiangood/open-admin";
13
+ import 'bpmn-js/dist/assets/bpmn-js.css';
14
+ import flowableJson from './descriptors/flowable';
15
+ import PropertiesPanel from './PropertiesPanel';
16
+
17
+ export default class extends React.Component {
18
+
19
+
20
+ state = {
21
+ id: null,
22
+ model: null,
23
+ bpmnModeler: null,
24
+ deployedModal: false
25
+ }
26
+
27
+ bpmRef = React.createRef()
28
+
29
+
30
+ async componentDidMount() {
31
+ const params = PageUtils.currentParams()
32
+ const rs = await HttpUtils.get('admin/flowable/model/detail', {id: params.id})
33
+ this.setState({model: rs, id: params.id}, this.initBpmn)
34
+ }
35
+
36
+ initBpmn = () => {
37
+ let container = this.bpmRef.current;
38
+ let xml = this.state.model.content;
39
+
40
+ this.bpmnModeler = new BpmnModeler({
41
+ container: container,
42
+ additionalModules: [
43
+ {translate: ['value', customTranslate]},
44
+ contextPad,
45
+ ],
46
+ moddleExtensions: {
47
+ flowable: flowableJson
48
+ }
49
+ });
50
+
51
+ console.log('导入的xml内容如下')
52
+ console.log(xml)
53
+ this.bpmnModeler.importXML(xml)
54
+ this.bpmnModeler.on('element.contextmenu', e => e.preventDefault()) // 关闭右键,影响操作
55
+ this.setState({bpmnModeler: this.bpmnModeler});
56
+ };
57
+
58
+
59
+ showXML = () => {
60
+ this.bpmnModeler.saveXML({format: true}).then(res => {
61
+ Modal.info({content: <pre style={{overflowX: "auto", height: '64vh'}}>{res.xml}</pre>, width: 1024})
62
+ })
63
+ }
64
+
65
+
66
+ handleSave = async () => {
67
+ let id = this.state.id;
68
+ const hide = message.loading('正在保存...', 0)
69
+ try {
70
+ const res = await this.bpmnModeler.saveXML();
71
+ await HttpUtils.post('admin/flowable/model/saveContent', {id, content: res.xml});
72
+ } finally {
73
+ hide()
74
+ }
75
+
76
+ }
77
+ handleDeploy = async () => {
78
+ let id = this.state.id;
79
+ const hide = message.loading('正在部署...', 0)
80
+ try {
81
+ const res = await this.bpmnModeler.saveXML();
82
+ await HttpUtils.post('admin/flowable/model/deploy', {id, content: res.xml});
83
+ } finally {
84
+ hide()
85
+ }
86
+ }
87
+
88
+
89
+ render() {
90
+ if (this.state.model == null) {
91
+ return <PageLoading />
92
+ }
93
+ return <Card title={'流程设计 ' + this.state.model?.name}
94
+ extra={<Space>
95
+ <Button type='primary' icon={<SaveOutlined/>} onClick={this.handleSave}>暂存</Button>
96
+ <Button type='primary' danger icon={<CloudUploadOutlined/>}
97
+ onClick={this.handleDeploy}>部署</Button>
98
+ <Button onClick={this.showXML}>XML</Button>
99
+ <Button
100
+ onClick={() => PageUtils.open('/flowable/simulate?id=' + this.state.id, "流程仿真")}> 仿真 </Button>
101
+
102
+ <Button title='查看已部署的历史版本' onClick={() => {
103
+ this.setState({deployedModal: true})
104
+ }}>历史版本</Button>
105
+ </Space>}>
106
+
107
+
108
+ <Splitter style={{minHeight: 'calc(100vh - 200px)'}}>
109
+ <Splitter.Panel>
110
+ <div ref={this.bpmRef} style={{width: '100%', height: '100%'}}></div>
111
+ </Splitter.Panel>
112
+
113
+ <Splitter.Panel defaultSize={300}>
114
+ <PropertiesPanel modeler={this.state.bpmnModeler} />
115
+ </Splitter.Panel>
116
+ </Splitter>
117
+
118
+
119
+ <Modal title='已部署版本' width={800} footer={null}
120
+ open={this.state.deployedModal}
121
+ destroyOnHidden
122
+ onCancel={() => this.setState({deployedModal: false})}>
123
+
124
+ <ProTable
125
+ columns={[
126
+ {
127
+ dataIndex: 'key',
128
+ title: '编码'
129
+ },
130
+ {
131
+ dataIndex: 'name',
132
+ title: '名称'
133
+ },
134
+ {
135
+ dataIndex: 'version',
136
+ title: '版本号'
137
+ }, {
138
+ title: '操作',
139
+ dataIndex:'id',
140
+ render:(_, record)=> {
141
+ return <Button type='primary' onClick={()=>{
142
+ HttpUtils.get('admin/flowable/model/getDefinitionContent',{id: record.id}).then(xml=>{
143
+ this.bpmnModeler.importXML(xml)
144
+ this.setState({deployedModal:false})
145
+ })
146
+ }}>加载</Button>
147
+ }
148
+ }
149
+ ]}
150
+ request={params => {
151
+ params.key = this.state.model.key
152
+ return HttpUtils.get('admin/flowable/model/definitionPage', params)
153
+ }}>
154
+
155
+ </ProTable>
156
+
157
+ </Modal>
158
+
159
+ </Card>
160
+ }
161
+
162
+
163
+ }
@@ -0,0 +1,74 @@
1
+ import {Form, Radio} from "antd";
2
+ import {FieldRemoteSelect, FieldRemoteSelectMultipleInline, StringUtils} from "@jiangood/open-admin";
3
+ import React from "react";
4
+
5
+ export default function AssignmentSection(props) {
6
+ const {element, modeling} = props
7
+ const businessObject = element.businessObject
8
+
9
+ const getMode = () => {
10
+ if (businessObject.assignee) return 'assignee'
11
+ if (businessObject.candidateGroups) return 'candidateGroups'
12
+ if (businessObject.candidateUsers) return 'candidateUsers'
13
+ return 'assignee'
14
+ }
15
+
16
+ const [mode, setMode] = React.useState(getMode())
17
+ const [formKey, setFormKey] = React.useState(0)
18
+
19
+ const initialValues = {
20
+ assignee: businessObject.assignee,
21
+ candidateGroups: businessObject.candidateGroups,
22
+ candidateUsers: StringUtils.split(businessObject.candidateUsers, ','),
23
+ }
24
+
25
+ const handleModeChange = (e) => {
26
+ const newMode = e.target.value
27
+ if (newMode === mode) return
28
+
29
+ // 切换模式时清空所有指派字段,确保三选一
30
+ modeling.updateProperties(element, {
31
+ assignee: undefined,
32
+ candidateGroups: undefined,
33
+ candidateUsers: undefined,
34
+ })
35
+
36
+ setMode(newMode)
37
+ setFormKey(k => k + 1)
38
+ }
39
+
40
+ return (<div style={{padding: 8}}>
41
+ <Radio.Group
42
+ optionType="button"
43
+ buttonStyle="solid"
44
+ value={mode}
45
+ onChange={handleModeChange}
46
+ options={[
47
+ {label: '直接指派', value: 'assignee'},
48
+ {label: '候选组', value: 'candidateGroups'},
49
+ {label: '候选人', value: 'candidateUsers'},
50
+ ]}
51
+ style={{marginBottom: 12, display: 'flex'}}
52
+ />
53
+ <Form key={formKey} layout='vertical' initialValues={initialValues}
54
+ onValuesChange={(changedValues) => {
55
+ modeling.updateProperties(element, changedValues);
56
+ }}>
57
+ {mode === 'assignee' && (
58
+ <Form.Item label="办理人" name='assignee'>
59
+ <FieldRemoteSelect url='admin/flowable/model/assigneeOptions'/>
60
+ </Form.Item>
61
+ )}
62
+ {mode === 'candidateGroups' && (
63
+ <Form.Item label="候选组" name='candidateGroups'>
64
+ <FieldRemoteSelect url='admin/flowable/model/candidateGroupsOptions'/>
65
+ </Form.Item>
66
+ )}
67
+ {mode === 'candidateUsers' && (
68
+ <Form.Item label="候选人" name='candidateUsers'>
69
+ <FieldRemoteSelectMultipleInline url='admin/flowable/model/candidateUsersOptions'/>
70
+ </Form.Item>
71
+ )}
72
+ </Form>
73
+ </div>)
74
+ }
@@ -0,0 +1,292 @@
1
+ import {Button, Input, InputNumber, Modal, Select} from "antd";
2
+ import {Component} from "react";
3
+ import {FieldBoolean, FieldTable, HttpUtils, ObjectUtils, StringUtils, ThemeUtils} from "@jiangood/open-admin";
4
+ import {ConditionExpressionUtils} from "./ConditionExpressionUtils";
5
+
6
+
7
+ // 字符串的双引号
8
+ const QUOTE = '"';
9
+
10
+
11
+ const OPERATOR_DEFINITIONS = [
12
+ {
13
+ type: 'STRING',
14
+ label: '等于',
15
+ key: '==',
16
+ component: Input,
17
+ },
18
+ {
19
+ type: 'STRING',
20
+ label: '不等于',
21
+ key: '!=',
22
+ component: Input,
23
+ },
24
+ {
25
+ type: 'STRING',
26
+ label: '包含',
27
+ key: '.contains',
28
+ component: Input,
29
+ },
30
+ {
31
+ type: 'STRING',
32
+ label: '开头等于',
33
+ key: '.startWith',
34
+ component: Input,
35
+ },
36
+ {
37
+ type: 'STRING',
38
+ label: '结尾等于',
39
+ key: '.endWith',
40
+ component: Input,
41
+ },
42
+
43
+ // =================== 数字 ================
44
+
45
+ {
46
+ type: 'NUMBER',
47
+ label: '等于',
48
+ key: '==',
49
+ component: Input,
50
+ },
51
+ {
52
+ type: 'NUMBER',
53
+ label: '不等于',
54
+ key: '!=',
55
+ component: Input,
56
+ },
57
+
58
+ {
59
+ type: 'NUMBER',
60
+ label: '大于',
61
+ key: '>',
62
+ component: InputNumber,
63
+ },
64
+ {
65
+ type: 'NUMBER',
66
+ label: '小于',
67
+ key: '<',
68
+ component: InputNumber,
69
+ },
70
+ {
71
+ type: 'NUMBER',
72
+ label: '大于等于',
73
+ key: '>=',
74
+ component: InputNumber,
75
+ },
76
+ {
77
+ type: 'NUMBER',
78
+ label: '小于等于',
79
+ key: '<=',
80
+ component: InputNumber,
81
+ },
82
+
83
+
84
+ // ===================== 布尔值 =======================
85
+
86
+ {
87
+ type: 'BOOLEAN',
88
+ label: '等于',
89
+ key: '==',
90
+ component: FieldBoolean,
91
+ },
92
+
93
+ ]
94
+
95
+
96
+ function encode(data) {
97
+ let {left, op, right} = data;
98
+ if (left == null || op == null || right == null) {
99
+ return null
100
+ }
101
+
102
+ const isFun = op.startsWith('.')
103
+ if (isFun) {
104
+ return left + op + '("' + right + '")';
105
+ }
106
+ const isStr = right.startsWith('"')
107
+ if (isStr) {
108
+ right = '"' + right + '"';
109
+ }
110
+
111
+ return left + op + right;
112
+ }
113
+
114
+ function decode(expression) {
115
+ const isFun = ConditionExpressionUtils.isFunction(expression);
116
+ if (isFun) {
117
+ return ConditionExpressionUtils.parseStrFunction(expression)
118
+ }
119
+
120
+ return ConditionExpressionUtils.parse(expression)
121
+ }
122
+
123
+
124
+ export class ConditionDesignButton extends Component {
125
+
126
+ state = {
127
+ open: false,
128
+ varList: [],
129
+ varOptions: [],
130
+ editingArr: [], // 弹窗内本地编辑状态,点击确定后才提交
131
+ }
132
+
133
+ componentDidMount() {
134
+ const {processId} = this.props;
135
+ console.log('流程id', processId)
136
+
137
+ HttpUtils.get('admin/flowable/model/varList', {code: processId}).then(rs => {
138
+ const options = rs.map(r => {
139
+ return {
140
+ label: r.label,
141
+ value: r.name
142
+ }
143
+ })
144
+ this.setState({varList: rs, varOptions: options})
145
+ })
146
+ }
147
+
148
+ // 弹窗内实时更新本地状态,不提交到模型
149
+ handleTableChange = arr => {
150
+ this.setState({ editingArr: arr })
151
+ };
152
+
153
+ // 确定:将本地状态提交到模型
154
+ handleOk = () => {
155
+ const str = this.convertArrToStr(this.state.editingArr)
156
+ this.props.setValue(str, this.props.element, this.props.modeling, this.props.bpmnFactory)
157
+ this.setState({ open: false })
158
+ }
159
+
160
+ // 取消:丢弃本地修改
161
+ handleCancel = () => {
162
+ this.setState({ open: false })
163
+ }
164
+
165
+ // 打开弹窗时,从当前元素读取最新表达式
166
+ handleOpen = () => {
167
+ let value = this.props.getValue(this.props.element);
168
+ let arrValue = this.convertStrToArr(value);
169
+ this.setState({ open: true, editingArr: arrValue })
170
+ }
171
+
172
+ getOptionsByItem = (record) => {
173
+ let options = []
174
+ let {varList} = this.state;
175
+ let varItem = varList.find(t => t.name === record.left)
176
+
177
+ if (varItem) {
178
+ const {valueType} = varItem;
179
+ const os = OPERATOR_DEFINITIONS.filter(o => o.type === valueType)
180
+ for (let o of os) {
181
+ options.push({
182
+ label: o.label,
183
+ value: o.key
184
+ })
185
+ }
186
+ }
187
+
188
+ return options;
189
+ }
190
+
191
+ columns = [
192
+ {
193
+ dataIndex: 'left', title: '变量名称',
194
+ render: () => {
195
+ return <Select options={this.state.varOptions} style={{width: 200}}></Select>
196
+ }
197
+ },
198
+ {
199
+ dataIndex: 'op', title: '操作符',
200
+ render: (v, record) => {
201
+ const options = this.getOptionsByItem(record)
202
+
203
+ return <Select options={options} style={{width: 100}}></Select>
204
+ }
205
+ },
206
+ {dataIndex: 'right', title: '值', width: 200},
207
+ ];
208
+
209
+ render() {
210
+ const { editingArr } = this.state
211
+ const previewExpression = this.convertArrToStr(editingArr)
212
+
213
+ return <div style={{display: 'flex', justifyContent: 'right', padding: 8}}>
214
+ <Button type='primary'
215
+ size='small'
216
+
217
+ styles={{
218
+ root: {
219
+ backgroundColor: ThemeUtils.getColor('primary-color')
220
+ }
221
+ }}
222
+
223
+ onClick={this.handleOpen}
224
+
225
+ >条件编辑器</Button>
226
+
227
+
228
+ <Modal title='条件编辑器'
229
+ open={this.state.open}
230
+ width={600}
231
+ onOk={this.handleOk}
232
+ onCancel={this.handleCancel}
233
+ mask={{blur: false}}
234
+ destroyOnHidden
235
+ >
236
+ <FieldTable
237
+ columns={this.columns}
238
+ value={editingArr}
239
+ onChange={this.handleTableChange}
240
+ />
241
+
242
+ <div style={{
243
+ marginTop: 8,
244
+ color: '#999',
245
+ fontSize: 12,
246
+ }}>
247
+ 提示:暂不支持复杂表达式,复杂表达式请手动编辑
248
+ </div>
249
+
250
+ <div style={{
251
+ marginTop: 16,
252
+ padding: '8px 12px',
253
+ background: '#f6f8fa',
254
+ borderRadius: 6,
255
+ border: '1px solid #d9d9d9',
256
+ fontFamily: 'monospace',
257
+ fontSize: 13,
258
+ wordBreak: 'break-all',
259
+ minHeight: 36,
260
+ display: 'flex',
261
+ alignItems: 'center',
262
+ }}>
263
+ <span style={{ color: '#999', marginRight: 8, flexShrink: 0 }}>表达式预览:</span>
264
+ <span style={{ color: previewExpression ? '#1a1a1a' : '#bbb' }}>
265
+ {previewExpression || '(空)'}
266
+ </span>
267
+ </div>
268
+ </Modal>
269
+
270
+ </div>
271
+ }
272
+
273
+ convertStrToArr(value) {
274
+ if (value) {
275
+ value = StringUtils.removePrefixAndSuffix(value, "${", "}")
276
+ const strArr = StringUtils.split(value, '&&');
277
+ return strArr.map(decode).filter(t => t != null)
278
+ }
279
+ return [];
280
+ }
281
+
282
+
283
+ convertArrToStr = arrValue => {
284
+ const str = arrValue.map(encode).join('&&')
285
+
286
+ return "${" + str + "}"
287
+ };
288
+
289
+
290
+ }
291
+
292
+
@@ -0,0 +1,53 @@
1
+ import {StringUtils} from "@jiangood/open-admin";
2
+
3
+
4
+ export class ConditionExpressionUtils {
5
+
6
+ /**
7
+ * 判断表达式是否是函数
8
+ * @param expr 如 name.contains("aa")
9
+ */
10
+ static isFunction(expr) {
11
+ const regExp = /\w+\.\w+\(.*\)/;
12
+ return regExp.test(expr);
13
+ }
14
+
15
+ /**
16
+ * 解析函数, 得到变量,函数名,参数
17
+ * 注意:只支持单参数函数
18
+ * @param expr
19
+ */
20
+ static parseStrFunction(expr) {
21
+ const regExp = /^(\w+)(\.\w+)\("(.*)"\)$/;
22
+ const match = expr.match(regExp);
23
+ if (!match) {
24
+ return null;
25
+ }
26
+ const [, left, op, right] = match;
27
+ return { left, op,right };
28
+ }
29
+
30
+ /**
31
+ * 解析普通表达式, 如 a==1, b>5, c=="你好"
32
+ *
33
+ */
34
+ static parse(expr) {
35
+ // 支持的比较操作符, 注意顺序,先比较的操作符长的
36
+ const operators = ['==', '!=', '<=', '>=', '<', '>'];
37
+
38
+ const op = operators.find(op => StringUtils.contains(expr, op))
39
+ if (!op) {
40
+ return null
41
+ }
42
+
43
+ const index = expr.indexOf(op);
44
+ const left = expr.substring(0, index).trim();
45
+ let right = expr.substring(index + op.length).trim();
46
+ right = StringUtils.removePrefixAndSuffix(right, '"', '"')
47
+
48
+ return {left, op, right};
49
+ }
50
+
51
+
52
+ }
53
+
@@ -0,0 +1,66 @@
1
+ import { Input } from 'antd';
2
+ import { useRef, useState } from 'react';
3
+ import { ConditionDesignButton } from './ConditionDesign';
4
+
5
+ function getExpressionValue(element) {
6
+ const condition = element.businessObject?.conditionExpression;
7
+ return condition ? condition.body : '';
8
+ }
9
+
10
+ function setExpressionValue(value, element, modeling, moddle) {
11
+ const businessObject = element.businessObject;
12
+ let conditionExpression = businessObject.conditionExpression;
13
+
14
+ if (!value) {
15
+ modeling.updateProperties(element, { conditionExpression: undefined });
16
+ return;
17
+ }
18
+
19
+ if (!conditionExpression) {
20
+ conditionExpression = moddle.create('bpmn:FormalExpression');
21
+ modeling.updateProperties(element, { conditionExpression });
22
+ }
23
+
24
+ modeling.updateModdleProperties(element, conditionExpression, { body: value });
25
+ }
26
+
27
+ export default function ConditionSection({ element, modeling, moddle, processId }) {
28
+ const [value, setValue] = useState(getExpressionValue(element));
29
+ const timerRef = useRef(null);
30
+
31
+ const handleChange = (e) => {
32
+ const v = e.target.value;
33
+ setValue(v);
34
+ clearTimeout(timerRef.current);
35
+ timerRef.current = setTimeout(() => {
36
+ setExpressionValue(v, element, modeling, moddle);
37
+ }, 300);
38
+ };
39
+
40
+ return (
41
+ <div style={{ padding: 8 }}>
42
+ <div style={{ marginBottom: 4, fontSize: 12, color: '#666' }}>
43
+ 条件表达式(JUEL)
44
+ </div>
45
+ <Input.TextArea
46
+ value={value}
47
+ onChange={handleChange}
48
+ placeholder="条件表达式(JUEL)"
49
+ rows={3}
50
+ />
51
+ <div style={{ display: 'flex', justifyContent: 'right', marginTop: 8 }}>
52
+ <ConditionDesignButton
53
+ element={element}
54
+ modeling={modeling}
55
+ bpmnFactory={moddle}
56
+ processId={processId}
57
+ getValue={getExpressionValue}
58
+ setValue={(v, el, mod, mdl) => {
59
+ setExpressionValue(v, el || element, mod || modeling, mdl || moddle);
60
+ setValue(v);
61
+ }}
62
+ />
63
+ </div>
64
+ </div>
65
+ );
66
+ }
@@ -0,0 +1,29 @@
1
+ import {Select} from 'antd';
2
+ import {useEffect, useState} from 'react';
3
+ import {HttpUtils} from "@jiangood/open-admin";
4
+
5
+ export default function DelegateExpressionField({element, modeling}) {
6
+ const [options, setOptions] = useState([]);
7
+
8
+ useEffect(() => {
9
+ HttpUtils.get('admin/flowable/model/javaDelegateOptions').then(rs => {
10
+ setOptions((rs || []).map(o => typeof o === 'string' ? {label: o, value: o} : o));
11
+ });
12
+ }, []);
13
+
14
+ const value = element.businessObject?.delegateExpression || '';
15
+
16
+ return (
17
+ <div style={{padding: 8}}>
18
+ <div style={{marginBottom: 4, fontSize: 12, color: '#666'}}>delegateExpression</div>
19
+ <Select
20
+ style={{width: '100%'}}
21
+ value={value || undefined}
22
+ placeholder="实现JavaDelegate接口的Bean名称, 如 ${demoDelegate}"
23
+ options={options}
24
+ onChange={(val) => modeling.updateProperties(element, {delegateExpression: val || ''})}
25
+ allowClear
26
+ />
27
+ </div>
28
+ );
29
+ }