@jiangood/open-admin-flowable 2.0.2 → 2.1.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 (26) hide show
  1. package/package.json +20 -20
  2. package/src/components/InstanceView.jsx +86 -0
  3. package/src/components/ProcessImageViewer.jsx +21 -0
  4. package/src/components/flowable/HistoryListPanel.jsx +49 -0
  5. package/src/components/flowable/done-table.jsx +31 -0
  6. package/src/components/flowable/my-table.jsx +34 -0
  7. package/src/components/flowable/todo-table.jsx +28 -0
  8. package/src/constants/api.js +39 -0
  9. package/src/constants/routes.js +8 -0
  10. package/src/pages/flowable/design/index.jsx +38 -13
  11. package/src/pages/flowable/design/provider/properties/AssignmentSection.jsx +4 -3
  12. package/src/pages/flowable/design/provider/properties/ConditionDesign.jsx +2 -3
  13. package/src/pages/flowable/design/provider/properties/DelegateExpressionProps.jsx +2 -1
  14. package/src/pages/flowable/design/provider/properties/FormProps.jsx +2 -1
  15. package/src/pages/flowable/index.jsx +8 -7
  16. package/src/pages/flowable/monitor/{definition.jsx → definition/index.jsx} +2 -1
  17. package/src/pages/flowable/monitor/instance/index.jsx +8 -4
  18. package/src/pages/flowable/monitor/instance/view.jsx +3 -100
  19. package/src/pages/flowable/monitor/{task.jsx → task/index.jsx} +6 -3
  20. package/src/pages/flowable/simulate/FinishedPhase.jsx +59 -0
  21. package/src/pages/flowable/simulate/InitPhase.jsx +54 -0
  22. package/src/pages/flowable/simulate/RunningPhase.jsx +117 -0
  23. package/src/pages/flowable/simulate/index.jsx +59 -243
  24. package/src/pages/flowable/user-task/form.jsx +12 -36
  25. package/src/pages/flowable/user-task/index.jsx +11 -169
  26. package/src/pages/flowable/user-task/instance/view.jsx +3 -83
@@ -1,7 +1,10 @@
1
1
  import React from "react";
2
- import {Button, Card, Empty, Form, Input, message, Modal, Select, Space, Spin, Splitter, Table, Tag, Typography} from "antd";
3
- import {HttpUtils, PageLoading, PageUtils, StringUtils} from "@jiangood/open-admin";
4
- import {CheckCircleOutlined, CloseCircleOutlined, HistoryOutlined, PlayCircleOutlined, ReloadOutlined} from "@ant-design/icons";
2
+ import {Button, Card, message, Modal, Space, Spin} from "antd";
3
+ import {HttpUtils, PageLoading, PageUtils} from "@jiangood/open-admin";
4
+ import InitPhase from "./InitPhase";
5
+ import RunningPhase from "./RunningPhase";
6
+ import FinishedPhase from "./FinishedPhase";
7
+ import {SIMULATE_GET, SIMULATE_USERS, SIMULATE_LIST, SIMULATE_START, SIMULATE_STATUS, SIMULATE_TASK_HANDLE, SIMULATE_DELETE} from "@/constants/api";
5
8
 
6
9
  const PHASE_INIT = 'init';
7
10
  const PHASE_RUNNING = 'running';
@@ -14,17 +17,14 @@ export default class extends React.Component {
14
17
  loading: false,
15
18
  submitting: false,
16
19
 
17
- // init phase
18
20
  model: undefined,
19
21
  users: [],
20
22
  historyList: [],
21
23
  historyLoading: false,
22
24
 
23
- // running / finished phase
24
25
  instanceId: null,
25
26
  status: null,
26
27
 
27
- // task form values: { [taskId]: { assignee, comment } }
28
28
  taskFormValues: {},
29
29
  }
30
30
 
@@ -32,17 +32,15 @@ export default class extends React.Component {
32
32
  const params = PageUtils.currentParams();
33
33
  const id = this.id = params.id;
34
34
 
35
- // 加载模型元数据
36
- HttpUtils.get('admin/flowable/simulate/get', {id}).then(rs => {
35
+ HttpUtils.get(SIMULATE_GET, {id}).then(rs => {
37
36
  this.setState({model: rs}, this.loadHistory);
38
37
  });
39
38
 
40
- // 加载用户列表
41
39
  this.loadUsers();
42
40
  }
43
41
 
44
42
  loadUsers = (searchText) => {
45
- HttpUtils.get('admin/flowable/simulate/users', {searchText}).then(rs => {
43
+ HttpUtils.get(SIMULATE_USERS, {searchText}).then(rs => {
46
44
  this.setState({users: rs || []});
47
45
  });
48
46
  }
@@ -51,36 +49,32 @@ export default class extends React.Component {
51
49
  const {model} = this.state;
52
50
  if (!model?.key) return;
53
51
  this.setState({historyLoading: true});
54
- HttpUtils.get('admin/flowable/simulate/list', {key: model.key}).then(rs => {
52
+ HttpUtils.get(SIMULATE_LIST, {key: model.key}).then(rs => {
55
53
  this.setState({historyList: rs || [], historyLoading: false});
56
- }).catch(() => {
54
+ }).catch(e => {
55
+ message.error(e?.message || '加载历史记录失败');
57
56
  this.setState({historyLoading: false});
58
57
  });
59
58
  }
60
59
 
61
- // ========== Init Phase ==========
62
-
63
60
  handleStart = values => {
64
61
  this.setState({submitting: true});
65
- HttpUtils.post('admin/flowable/simulate/start', values).then(rs => {
66
- const instanceId = rs.instanceId;
62
+ HttpUtils.post(SIMULATE_START, values).then(rs => {
67
63
  message.success('仿真流程已启动');
68
- this.loadStatus(instanceId);
64
+ this.loadStatus(rs.instanceId);
69
65
  }).catch(e => {
70
- message.error(e);
66
+ message.error(e?.message || '启动仿真失败');
71
67
  this.setState({submitting: false});
72
68
  });
73
69
  }
74
70
 
75
- // ========== Running / Finished Phase ==========
76
-
77
71
  loadStatus = (instanceId) => {
78
72
  this.setState({loading: true, instanceId, phase: PHASE_RUNNING, taskFormValues: {}});
79
- HttpUtils.get('admin/flowable/simulate/status', {instanceId}).then(rs => {
73
+ HttpUtils.get(SIMULATE_STATUS, {instanceId}).then(rs => {
80
74
  const phase = rs.finished ? PHASE_FINISHED : PHASE_RUNNING;
81
75
  this.setState({status: rs, phase, loading: false, submitting: false});
82
76
  }).catch(e => {
83
- message.error(e);
77
+ message.error(e?.message || '获取状态失败');
84
78
  this.setState({loading: false, submitting: false});
85
79
  });
86
80
  }
@@ -115,7 +109,7 @@ export default class extends React.Component {
115
109
  }
116
110
 
117
111
  this.setState({submitting: true});
118
- HttpUtils.post('admin/flowable/simulate/task/handle', {
112
+ HttpUtils.post(SIMULATE_TASK_HANDLE, {
119
113
  taskId,
120
114
  action,
121
115
  comment: formValue.comment || '',
@@ -124,7 +118,7 @@ export default class extends React.Component {
124
118
  message.success(action === 'APPROVE' ? '已同意' : '已拒绝');
125
119
  this.loadStatus(this.state.instanceId);
126
120
  }).catch(e => {
127
- message.error(e);
121
+ message.error(e?.message || '操作失败');
128
122
  this.setState({submitting: false});
129
123
  });
130
124
  }
@@ -152,7 +146,7 @@ export default class extends React.Component {
152
146
  okType: 'danger',
153
147
  cancelText: '取消',
154
148
  onOk: () => {
155
- HttpUtils.post('admin/flowable/simulate/delete', {instanceId}).then(() => {
149
+ HttpUtils.post(SIMULATE_DELETE, {instanceId}).then(() => {
156
150
  message.success('仿真记录已删除');
157
151
  this.loadHistory();
158
152
  });
@@ -160,18 +154,6 @@ export default class extends React.Component {
160
154
  });
161
155
  }
162
156
 
163
- onImgClick = () => {
164
- Modal.info({
165
- title: '流程图',
166
- width: '70vw',
167
- content: <div style={{width: '100%', overflow: 'auto', maxHeight: '80vh'}}>
168
- <img src={this.state.status?.img} style={{maxWidth: '100%'}}/>
169
- </div>
170
- });
171
- }
172
-
173
- // ========== Render ==========
174
-
175
157
  render() {
176
158
  const {model} = this.state;
177
159
 
@@ -179,222 +161,56 @@ export default class extends React.Component {
179
161
  return <PageLoading/>;
180
162
  }
181
163
 
182
- const {phase, loading, submitting, status} = this.state;
164
+ const {phase, loading, submitting, status, users, historyList, historyLoading, taskFormValues} = this.state;
183
165
 
184
166
  return (
185
167
  <Card title={'流程仿真 / 【' + model.name + '】 / ' + model.key}
186
168
  extra={phase === PHASE_FINISHED ? (
187
169
  <Space>
188
- <Button icon={<HistoryOutlined/>} onClick={this.handleReset}>历史记录</Button>
189
- <Button icon={<ReloadOutlined/>} onClick={this.handleReset}>重新仿真</Button>
170
+ <Button onClick={this.handleReset}>重新仿真</Button>
190
171
  </Space>
191
172
  ) : null}>
192
- {phase === PHASE_INIT && this.renderInitPhase()}
193
- {phase !== PHASE_INIT && (
173
+ {phase === PHASE_INIT && (
174
+ <InitPhase
175
+ model={model}
176
+ users={users}
177
+ submitting={submitting}
178
+ historyList={historyList}
179
+ historyLoading={historyLoading}
180
+ onStart={this.handleStart}
181
+ onLoadUsers={this.loadUsers}
182
+ onViewHistory={this.handleViewHistory}
183
+ onDeleteHistory={this.handleDeleteHistory}
184
+ />
185
+ )}
186
+ {phase === PHASE_RUNNING && (
187
+ loading ? <Spin style={{display: 'block', margin: '80px auto'}}/> :
188
+ status ? (
189
+ <RunningPhase
190
+ status={status}
191
+ submitting={submitting}
192
+ taskFormValues={taskFormValues}
193
+ users={users}
194
+ onLoadUsers={this.loadUsers}
195
+ onAssigneeChange={this.handleAssigneeChange}
196
+ onCommentChange={this.handleCommentChange}
197
+ onTask={this.handleTask}
198
+ />
199
+ ) : null
200
+ )}
201
+ {phase === PHASE_FINISHED && (
194
202
  loading ? <Spin style={{display: 'block', margin: '80px auto'}}/> :
195
- status ? this.renderRunningPhase() : null
203
+ status ? (
204
+ <FinishedPhase
205
+ historyList={historyList}
206
+ historyLoading={historyLoading}
207
+ onReset={this.handleReset}
208
+ onViewHistory={this.handleViewHistory}
209
+ onDeleteHistory={this.handleDeleteHistory}
210
+ />
211
+ ) : null
196
212
  )}
197
213
  </Card>
198
214
  );
199
215
  }
200
-
201
- renderInitPhase = () => {
202
- const {model, users, submitting, historyList, historyLoading} = this.state;
203
- return (
204
- <Splitter>
205
- <Splitter.Panel defaultSize="55%">
206
- <Form onFinish={this.handleStart} layout="vertical">
207
- <Form.Item name="key" noStyle initialValue={model.key}/>
208
-
209
- <Form.Item label="业务标识" name="id"
210
- rules={[{required: true, message: '请输入业务标识'}]}
211
- initialValue={StringUtils.random(16)}>
212
- <Input/>
213
- </Form.Item>
214
-
215
- <Form.Item label="发起人" name="initiatorId"
216
- rules={[{required: true, message: '请选择发起人'}]}>
217
- <Select showSearch placeholder="搜索并选择用户"
218
- filterOption={false}
219
- onSearch={this.loadUsers}
220
- options={users.map(u => ({label: u.name, value: u.id}))}
221
- style={{width: 300}}/>
222
- </Form.Item>
223
-
224
- {model.variables?.map(item => (
225
- <Form.Item key={item.name} name={['variables', item.name]} label={item.label}>
226
- <Input/>
227
- </Form.Item>
228
- ))}
229
-
230
- <Form.Item>
231
- <Button type="primary" htmlType="submit" icon={<PlayCircleOutlined/>}
232
- loading={submitting}>
233
- 启动仿真
234
- </Button>
235
- </Form.Item>
236
- </Form>
237
- </Splitter.Panel>
238
- <Splitter.Panel defaultSize="45%">
239
- {this.renderHistoryList(historyList, historyLoading)}
240
- </Splitter.Panel>
241
- </Splitter>
242
- );
243
- }
244
-
245
- renderHistoryList = (list, loading) => (
246
- <div style={{paddingLeft: 16}}>
247
- <Typography.Title level={5}>
248
- <HistoryOutlined/> 仿真历史
249
- </Typography.Title>
250
- {loading ? <Spin/> : list.length === 0 ? (
251
- <Empty description="暂无仿真记录" image={Empty.PRESENTED_IMAGE_SIMPLE}/>
252
- ) : (
253
- <Table dataSource={list}
254
- size="small" pagination={false} rowKey="instanceId"
255
- columns={[
256
- {
257
- title: '实例', dataIndex: 'name', ellipsis: true,
258
- render: (text, record) => (
259
- <a onClick={() => this.handleViewHistory(record.instanceId)}>{text}</a>
260
- ),
261
- },
262
- {title: '发起人', dataIndex: 'starter', width: 80},
263
- {
264
- title: '状态', dataIndex: 'finished', width: 80,
265
- render: (finished, record) => finished ? (
266
- record.deleteReason ? (
267
- <Tag color="error">已终止</Tag>
268
- ) : (
269
- <Tag color="success">已完成</Tag>
270
- )
271
- ) : (
272
- <Tag color="processing">运行中</Tag>
273
- ),
274
- },
275
- {title: '发起时间', dataIndex: 'startTime', width: 100},
276
- {
277
- title: '操作', width: 60,
278
- render: (_, record) => (
279
- <Button type="link" danger size="small"
280
- onClick={() => this.handleDeleteHistory(record.instanceId)}>
281
- 删除
282
- </Button>
283
- ),
284
- },
285
- ]}/>
286
- )}
287
- </div>
288
- );
289
-
290
- renderRunningPhase = () => {
291
- const {status, submitting, taskFormValues, users} = this.state;
292
- const {img, commentList, tasks, finished, deleteReason} = status;
293
-
294
- return (
295
- <Splitter>
296
- <Splitter.Panel defaultSize="60%">
297
- <div style={{paddingRight: 16}}>
298
- <Typography.Title level={5}>流程图</Typography.Title>
299
- <img src={img} style={{maxWidth: '100%', cursor: 'pointer'}}
300
- onClick={this.onImgClick}/>
301
- <div style={{marginTop: 16}}>
302
- <Typography.Title level={5}>处理记录</Typography.Title>
303
- <Table dataSource={commentList || []}
304
- size="small" pagination={false} rowKey="time"
305
- columns={[
306
- {dataIndex: 'content', title: '操作'},
307
- {dataIndex: 'user', title: '处理人', width: 120},
308
- {dataIndex: 'time', title: '处理时间', width: 160},
309
- ]}/>
310
- </div>
311
- </div>
312
- </Splitter.Panel>
313
- <Splitter.Panel defaultSize="40%">
314
- <div style={{paddingLeft: 16}}>
315
- <Space style={{marginBottom: 12}}>
316
- <Typography.Text strong>实例名称:</Typography.Text>
317
- <Typography.Text>{status.name}</Typography.Text>
318
- </Space>
319
- <br/>
320
- <Space style={{marginBottom: 12}}>
321
- <Typography.Text strong>业务标识:</Typography.Text>
322
- <Typography.Text>{status.businessKey}</Typography.Text>
323
- </Space>
324
- <br/>
325
- <Space style={{marginBottom: 12}}>
326
- <Typography.Text strong>发起人:</Typography.Text>
327
- <Typography.Text>{status.starter}</Typography.Text>
328
- </Space>
329
- <br/>
330
- <Space style={{marginBottom: 12}}>
331
- <Typography.Text strong>发起时间:</Typography.Text>
332
- <Typography.Text>{status.startTime}</Typography.Text>
333
- </Space>
334
- <br/>
335
- <Space style={{marginBottom: 16}}>
336
- <Typography.Text strong>状态:</Typography.Text>
337
- {finished ? (
338
- deleteReason ? (
339
- <Tag icon={<CloseCircleOutlined/>} color="error">已终止</Tag>
340
- ) : (
341
- <Tag icon={<CheckCircleOutlined/>} color="success">已完成</Tag>
342
- )
343
- ) : (
344
- <Tag color="processing">运行中</Tag>
345
- )}
346
- </Space>
347
-
348
- {finished && deleteReason && (
349
- <Card size="small" title="终止原因" style={{marginBottom: 16}}>
350
- <Typography.Text type="secondary">{deleteReason}</Typography.Text>
351
- </Card>
352
- )}
353
-
354
- {!finished && tasks?.map(task => (
355
- <Card key={task.taskId} size="small" title={task.taskName}
356
- style={{marginBottom: 12}}>
357
- <Space direction="vertical" style={{width: '100%'}}>
358
- <div>
359
- <Typography.Text strong>处理人:</Typography.Text>
360
- <Select showSearch placeholder="选择处理人"
361
- value={(taskFormValues[task.taskId] || {}).assignee}
362
- style={{width: '100%'}}
363
- filterOption={false}
364
- onSearch={this.loadUsers}
365
- onChange={value => this.handleAssigneeChange(task.taskId, value)}
366
- options={users.map(u => ({label: u.name, value: u.id}))}/>
367
- </div>
368
- <div>
369
- <Typography.Text strong>审批意见:</Typography.Text>
370
- <Input.TextArea rows={2}
371
- value={(taskFormValues[task.taskId] || {}).comment}
372
- onChange={e => this.handleCommentChange(task.taskId, e)}/>
373
- </div>
374
- <Space>
375
- <Button type="primary"
376
- icon={<CheckCircleOutlined/>}
377
- loading={submitting}
378
- onClick={() => this.handleTask(task.taskId, 'APPROVE')}>
379
- 同意
380
- </Button>
381
- <Button danger
382
- icon={<CloseCircleOutlined/>}
383
- loading={submitting}
384
- onClick={() => this.handleTask(task.taskId, 'REJECT')}>
385
- 不同意
386
- </Button>
387
- </Space>
388
- </Space>
389
- </Card>
390
- ))}
391
-
392
- {!finished && (!tasks || tasks.length === 0) && (
393
- <Empty description="暂无活跃任务"/>
394
- )}
395
- </div>
396
- </Splitter.Panel>
397
- </Splitter>
398
- );
399
- }
400
216
  }
@@ -1,8 +1,8 @@
1
1
  import React from "react";
2
- import {Button, Card, Empty, Form, Input, message, Modal, Radio, Spin, Splitter, Table, Tabs, Typography,} from "antd";
3
- import {history} from "umi";
2
+ import {Button, Card, Empty, Form, Input, message, Radio, Spin, Splitter, Table, Typography} from "antd";
3
+ import ProcessImageViewer from "@/components/ProcessImageViewer";
4
4
  import {FormRegistryUtils, Gap, HttpUtils, Page, PageUtils} from "@jiangood/open-admin";
5
- import {FormOutlined, ShareAltOutlined} from "@ant-design/icons";
5
+ import {USER_TASK_GET_INSTANCE_INFO_BY_TASK_ID, USER_TASK_HANDLE_TASK} from "@/constants/api";
6
6
 
7
7
  export default class extends React.Component {
8
8
 
@@ -31,9 +31,10 @@ export default class extends React.Component {
31
31
  const {taskId} = PageUtils.currentParams()
32
32
 
33
33
 
34
- HttpUtils.get("admin/flowable/user-task/getInstanceInfoByTaskId", {taskId}).then(rs => {
34
+ HttpUtils.get(USER_TASK_GET_INSTANCE_INFO_BY_TASK_ID, {taskId}).then(rs => {
35
35
  this.setState({data: rs})
36
36
  }).catch(e => {
37
+ message.error(e?.message || '获取任务信息失败');
37
38
  this.setState({errorMsg: e})
38
39
  }).finally(() => {
39
40
  this.setState({loading: false})
@@ -42,16 +43,6 @@ export default class extends React.Component {
42
43
 
43
44
  }
44
45
 
45
- onImgClick = () => {
46
- Modal.info({
47
- title: '流程图',
48
- width: '70vw',
49
- content: <div style={{width: '100%', overflow: 'auto', maxHeight: '80vh'}}>
50
- <img src={this.state.data.img}/>
51
- </div>
52
- })
53
- };
54
-
55
46
 
56
47
  handleTask = async value => {
57
48
  this.setState({submitLoading: true});
@@ -60,11 +51,11 @@ export default class extends React.Component {
60
51
  value.formData = this.state.formData
61
52
  }
62
53
  value.taskId = this.state.data.taskId
63
- await HttpUtils.post("admin/flowable/user-task/handleTask", value)
54
+ await HttpUtils.post(USER_TASK_HANDLE_TASK, value)
64
55
 
65
56
  PageUtils.closeCurrent()
66
57
  } catch (error) {
67
- message.error(error)
58
+ message.error(error?.message || '操作失败')
68
59
  } finally {
69
60
  this.setState({submitLoading: false})
70
61
  }
@@ -86,25 +77,9 @@ export default class extends React.Component {
86
77
  <Typography.Title level={4}>{data.name}</Typography.Title>
87
78
  <Typography.Text type="secondary">{data.starter} &nbsp;&nbsp; {data.startTime}</Typography.Text>
88
79
  <Gap></Gap>
89
- <Tabs
90
- items={[
91
- {
92
- key: '1',
93
- label: '表单',
94
- icon: <FormOutlined/>,
95
- children: this.renderForm()
96
- },
97
- {
98
- key: '2',
99
- label: '流程图',
100
- icon: <ShareAltOutlined/>,
101
- children: this.renderProcess(img, commentList)
102
- }
103
- ]}>
104
-
105
- </Tabs>
80
+ {this.renderForm()}
106
81
  </Splitter.Panel>
107
- <Splitter.Panel defaultSize={400}>
82
+ <Splitter.Panel>
108
83
  <Card title='审批意见'>
109
84
  <Form
110
85
  layout='vertical'
@@ -129,6 +104,8 @@ export default class extends React.Component {
129
104
  </div>
130
105
  </Form>
131
106
  </Card>
107
+ <Gap></Gap>
108
+ {this.renderProcess(img, commentList)}
132
109
  </Splitter.Panel>
133
110
 
134
111
  </Splitter>
@@ -140,8 +117,7 @@ export default class extends React.Component {
140
117
  }
141
118
 
142
119
  renderProcess = (img, commentList) => <Card title='处理记录'>
143
- <img src={img} style={{maxWidth: '100%'}}
144
- onClick={this.onImgClick}/>
120
+ <ProcessImageViewer imageUrl={img}/>
145
121
  <Gap></Gap>
146
122
  <Table dataSource={commentList}
147
123