@lambo-design/workflow-approve 1.0.0-beta.14 → 1.0.0-beta.141

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.
@@ -1,332 +1,980 @@
1
- <template>
2
- <div>
3
-
4
- <Collapse v-show="showChart" v-model="showDefaultExpand">
5
- <Panel name="0">
6
- 流程跟踪图
7
- <div slot="content">
8
- <div class="containers" :style="'height: '+canvasHeight+ 'px'">
9
- <div class="canvas" ref="canvas"></div>
10
- </div>
11
- </div>
12
- </Panel>
13
- </Collapse>
14
-
15
- <Collapse v-show="showTable" v-model="showDefaultExpand">
16
- <Panel name="1">
17
- {{ tableName }}
18
- <div slot="content">
19
- <EditTable :columns="tableColumns" v-model="hisAudit" :show-table-option="false">
20
- </EditTable>
21
- </div>
22
- </Panel>
23
- </Collapse>
24
-
25
-
26
- <Modal v-model="showDetail" :width="modelWidth" :footer-hide='true' title="审批详情">
27
- <Card v-for="(item,index) in detailDones" :key="index" style="margin-bottom: 10px">
28
- <Form :model="item" :label-width="140" class="compactForm">
29
- <Row>
30
- <i-col span="12" offset="0">
31
- <FormItem label="审批节点:">
32
- {{ item.taskName }}
33
- </FormItem>
34
- </i-col>
35
- <i-col span="12" offset="0">
36
- <FormItem label="审批人:">
37
- <Tooltip placement="bottom-end">
38
- <span class="tooltip-text">{{ item.auditName }}</span>
39
- <div slot="content" style="white-space: normal;">
40
- {{ item.auditName }}
41
- </div>
42
- </Tooltip>
43
-
44
- </FormItem>
45
- </i-col>
46
-
47
- </Row>
48
- <Row>
49
- <i-col span="12" offset="0">
50
- <FormItem label="审批结果:">
51
- <Tag color="success" v-if="item.auditResult == '30'">通过</Tag>
52
- <Tag color="error" v-else-if="item.auditResult == '40'">驳回到上一节点</Tag>
53
- <Tag color="error" v-else-if="item.auditResult=='50'">驳回到原点</Tag>
54
- <Tag color="error" v-else-if="item.auditResult=='51'">流程终止</Tag>
55
- <Tag color="error" v-else-if="item.auditResult == '60'">撤回</Tag>
56
- <Tag color="success" v-else-if="item.auditResult == '80'">跳转到指定节点</Tag>
57
- <Tag color="success" v-else-if="item.auditResult == '90'">驳回到指定节点</Tag>
58
- <Tag color="success" v-else>待审核</Tag>
59
- </FormItem>
60
- </i-col>
61
- <i-col span="12" offset="0">
62
- <FormItem v-if="item.startDate" label="发起时间:">
63
- {{ item.startDate }}
64
- </FormItem>
65
- <FormItem v-else label="审批时间:">
66
- {{ item.auditDate }}
67
- </FormItem>
68
- </i-col>
69
- </Row>
70
- <Row>
71
- <i-col span="24" offset="0">
72
- <FormItem v-if="item.auditComment" label="审批意见:">
73
- {{ item.auditComment }}
74
- </FormItem>
75
- </i-col>
76
- </Row>
77
- </Form>
78
- </Card>
79
- </Modal>
80
-
81
-
82
- </div>
83
- </template>
84
-
85
- <script>
86
- import ajax from "@lambo-design/shared/utils/ajax";
87
- import zoomScroll from "@lambo-design/shared/utils/zoomScroll";
88
- import BpmnViewer from "bpmn-js/lib/Viewer";
89
- import ModelingModule from 'bpmn-js/lib/features/modeling';
90
- import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas';
91
- import EditTable from "@lambo-design/edit-table";
92
-
93
- export default {
94
- components: {
95
- EditTable
96
- },
97
- props: {
98
- canvasHeight: {
99
- type: Number,
100
- default: 350
101
- },
102
- modelWidth: {
103
- type: Number,
104
- default: 693
105
- },
106
- modelTitle: {
107
- type: String,
108
- default: "审批详情"
109
- },
110
- showChart: {
111
- type: Boolean,
112
- default: true
113
- },
114
- showTable: {
115
- type: Boolean,
116
- default: true
117
- },
118
- tableName: {
119
- type: String,
120
- default: "审批流程跟踪"
121
- },
122
- tableColumns: {
123
- type: Array,
124
- default: () => {
125
- return [
126
- {title: '序号', type: 'index', width: 80},
127
- {title: '审批节点', key: 'taskName', minWidth: 130},
128
- {title: '办理人', key: 'auditName', minWidth: 180, tooltip: true},
129
- {
130
- title: '审批结果', key: 'auditResult', minWidth: 100,
131
- render: (h, {row, column, index}) => {
132
- let label = "";
133
- if (row.auditResult == '30') label = '通过';
134
- else if (row.auditResult == '40') label = '驳回到上一级';
135
- else if (row.auditResult == '50') label = '驳回到原点';
136
- else if (row.auditResult == '51') label = '流程终止';
137
- else if (row.auditResult == '60') label = '撤回';
138
- else if (row.auditResult == '80') label = '跳转到指定节点';
139
- else if (row.auditResult == '90') label = '驳回到指定节点';
140
- else label = '待审核';
141
- return h('Label', {
142
- props: {
143
- value: row.auditResult
144
- }
145
- }, label)
146
- }
147
- },
148
- {
149
- title: '审批时间', key: 'auditTime', minWidth: 150,
150
- render: (h, {row}) => {
151
- const displayDate = row.startDate || row.auditDate;
152
- return h('span', displayDate);
153
- }
154
- },
155
- {
156
- title: '审批意见', key: 'auditComment', minWidth: 180, tooltip: true,
157
- render: (h, {row, column, index}) => {
158
- let label = "";
159
- if (row.auditComment == '' || row.auditComment == null) label = '无';
160
- else label = row.auditComment;
161
- return h('Label', {
162
- props: {
163
- value: row.auditComment
164
- }
165
- }, label)
166
- }
167
- }
168
- ];
169
- }
170
- },
171
- hisAudit: {
172
- type: Array,
173
- default: () => {
174
- return []
175
- }
176
- },
177
- tableData: {
178
- type: Array,
179
- default: () => {
180
- return []
181
- }
182
- },
183
- applyId: {
184
- type: String,
185
- default: ""
186
- },
187
- instanceId: {
188
- type: String,
189
- default: ""
190
- },
191
- procId: {
192
- type: String,
193
- default: ""
194
- },
195
- smartFlowServerContext: {
196
- type: String,
197
- default: '/api/smart-flow-server',
198
- },
199
- },
200
- data() {
201
- return {
202
- detailDones: [],
203
- showDetail: false,
204
- showDefaultExpand: this.getShowDefaultExpand(),
205
- }
206
- },
207
- computed: {},
208
- methods: {
209
- getShowDefaultExpand() {
210
- let expand = [];
211
- if (this.showChart) {
212
- expand.push('0');
213
- }
214
- if (this.showTable) {
215
- expand.push('1');
216
- }
217
- return expand;
218
- },
219
-
220
- //画流程图
221
- workflowInit() {
222
- const canvas = this.$refs.canvas;
223
- // this.bpmnViewer.destroy();
224
- // if (this.bpmnViewer) return
225
- if (this.bpmnViewer){
226
- this.bpmnViewer.destroy()
227
- }
228
- this.bpmnViewer = new BpmnViewer({
229
- container: canvas,
230
- additionalModules: [
231
- // MoveModule, // 可以调整元素
232
- ModelingModule, // 基础工具 MoveModule、SetColor 等依赖于此
233
- MoveCanvasModule, // 移动整个画布
234
- zoomScroll // 放大缩小
235
- ]
236
- });
237
-
238
- ajax.get(this.smartFlowServerContext + "/manage/processTodo/printData", {
239
- params: {
240
- applyId: this.applyId,
241
- instanceId: this.instanceId,
242
- procId: this.procId
243
- }
244
- }).then(resp => {
245
- if (resp.data.code == '200') {
246
- const bpmnXmlStr = resp.data.data.processXml;
247
- this.bpmnViewer.importXML(bpmnXmlStr);
248
- setTimeout(() => {
249
- const printData = resp.data.data.historicData;
250
- this.doPrint(printData);
251
- }, 700);
252
- } else {
253
- this.$Message.error("加载流程xml失败," + resp.data.data);
254
- }
255
- });
256
- },
257
- doPrint(printData) {
258
- var self = this;
259
- const {doneLightSet, donePointSet, doneTaskSet, todoPointSet} =
260
- printData;
261
- const canvas = this.bpmnViewer.get("canvas");
262
- const elementRegistry = this.bpmnViewer.get('elementRegistry');
263
- const userTaskList = elementRegistry.filter(
264
- (item) => item.type === 'bpmn:UserTask'
265
- );
266
- //循环节点查找multiple
267
- self.notMultiple = true;
268
- canvas.zoom("fit-viewport", "auto");
269
- var eventBus = this.bpmnViewer.get("eventBus");
270
- var dones = this.tableData;
271
- eventBus.on('element.click', function (evt) {
272
- self.detailDones = [];
273
- for (var item of dones) {
274
- if (item.taskNode == evt.element.id) {
275
- self.detailDones.push(item);
276
- self.showDetail = true;
277
- self.$emit("showTaskNode", item);
278
- break;
279
- }
280
- }
281
- });
282
-
283
- for (var k in doneLightSet) {
284
- if (doneLightSet[k]) {
285
- canvas.addMarker(doneLightSet[k], "done-line");
286
- }
287
- }
288
- for (var k in donePointSet) {
289
- if (donePointSet[k]) {
290
- canvas.addMarker(donePointSet[k], "done-point");
291
- }
292
- }
293
- for (var k in doneTaskSet) {
294
- if (doneTaskSet[k]) {
295
- canvas.addMarker(doneTaskSet[k], "done-task");
296
- }
297
- }
298
- for (var k in todoPointSet) {
299
- if (todoPointSet[k]) {
300
- canvas.addMarker(todoPointSet[k], "todo-point");
301
- }
302
- }
303
-
304
- },
305
- // 校验必填
306
- isNotBlank(val) {
307
- return (val != null || val === 0) && (val.toString().trim() != '' || val === 0)
308
- }
309
- },
310
- mounted() {
311
- if (this.isNotBlank(this.applyId)) {
312
- this.workflowInit();
313
- }
314
- },
315
- watch: {
316
- applyId(val) {
317
- if (this.isNotBlank(val)) {
318
- this.workflowInit();
319
- }
320
- },
321
- instanceId(val) {
322
- if (this.isNotBlank(this.applyId) && this.isNotBlank(val)) {
323
- this.workflowInit();
324
- }
325
- }
326
- }
327
- }
328
- </script>
329
-
330
- <style lang="less">
331
- @import "./styles/css/index.less";
332
- </style>
1
+ <template>
2
+ <div ref="workflowDiagram">
3
+
4
+ <Collapse v-show="showChart" v-model="showDefaultExpand">
5
+ <Panel name="0">
6
+ 流程跟踪图
7
+ <div slot="content">
8
+ <div style="display: flex; justify-content: flex-start; align-items: center; margin-bottom: 8px;">
9
+ <span class="operating-instructions">提示:Ctrl+鼠标滚轮可缩放;按住鼠标左键拖动可平移</span>
10
+ </div>
11
+ <div class="notes-div">
12
+ <span class="notes-done-task">已办节点</span>
13
+ <span class="notes-todo-task">在办节点</span>
14
+ <span class="notes-none-task">未办节点</span>
15
+ </div>
16
+ <div class="containers" :style="'height: '+canvasHeight+ 'px'">
17
+ <div class="canvas" ref="canvas"></div>
18
+ </div>
19
+ <div v-if="predictButtonEnabled" style="display: flex; justify-content: center; margin-top: 12px;">
20
+ <Button
21
+ type="primary"
22
+ size="small"
23
+ @click="toggleNodeDisplay"
24
+ :loading="loadingFutureNodes">
25
+ {{ showAllNodes ? '显示预测路线' : '显示设计路线' }}
26
+ </Button>
27
+ </div>
28
+ </div>
29
+ </Panel>
30
+ </Collapse>
31
+
32
+ <Collapse v-show="showTable" v-model="showDefaultExpand">
33
+ <Panel name="1">
34
+ {{ tableName }}
35
+ <div slot="content">
36
+ <EditTable :columns="tableColumns" v-model="hisAuditTableData" :show-table-option="false"
37
+ row-key="taskNode">
38
+ </EditTable>
39
+ </div>
40
+ </Panel>
41
+ </Collapse>
42
+
43
+
44
+ <Modal
45
+ v-model="showDetail"
46
+ :width="futureDetail ? 360 : modelWidth"
47
+ :footer-hide='true'
48
+ title="办理详情"
49
+ :class="futureDetail ? 'future-detail-modal' : ''">
50
+ <div v-if="futureDetail" class="future-detail-card">
51
+ <div class="future-detail-title">{{ futureDetail.taskName }}</div>
52
+ <div class="future-detail-body">
53
+ <span class="future-detail-status">待分配</span>
54
+ <span class="future-detail-users" :title="futureDetail.assigneeDisplay">
55
+ {{ futureDetail.assigneeDisplay }}
56
+ </span>
57
+ </div>
58
+ </div>
59
+ <template v-else>
60
+ <Card v-for="(item,index) in detailDones" :key="index" style="margin-bottom: 10px">
61
+ <Form :model="item" :label-width="120" label-colon>
62
+ <Row>
63
+ <i-col span="12" offset="0">
64
+ <FormItem label="节点">
65
+ {{ item.taskName }}
66
+ </FormItem>
67
+ </i-col>
68
+ <i-col span="12" offset="0">
69
+ <FormItem label="办理人">
70
+ <Tooltip placement="bottom-end">
71
+ <span class="tooltip-text">{{ item.auditName }}</span>
72
+ <div slot="content" style="white-space: normal;">
73
+ {{ item.auditName }}
74
+ </div>
75
+ </Tooltip>
76
+
77
+ </FormItem>
78
+ </i-col>
79
+
80
+ </Row>
81
+ <Row>
82
+ <i-col span="12" offset="0">
83
+ <FormItem label="结果">
84
+ <Tag color="green" v-if="item.auditResult == '00'">流程发起</Tag>
85
+ <Tag color="green" v-else-if="item.auditResult == '10'">首节点自动跳过</Tag>
86
+ <Tag color="green" v-else-if="item.auditResult == '11'">与上一环节办理人相同自动跳过</Tag>
87
+ <Tag color="green" v-else-if="item.auditResult == '12'">与发起人相同自动跳过</Tag>
88
+ <Tag color="green" v-else-if="item.auditResult == '13'">办理人为空自动跳过</Tag>
89
+ <Tag color="green" v-else-if="item.auditResult == '14'">符合流程变量条件自动跳过</Tag>
90
+ <Tag color="green" v-else-if="item.auditResult == '30'">{{item.handleName && item.handleName !== '审批' ? item.handleName : '通过'}}</Tag>
91
+ <Tag color="green" v-else-if="item.auditResult == '31'">{{`${item.handleName && item.handleName !== '审批' ? item.handleName : '通过'}`}}</Tag>
92
+ <Tag color="volcano" v-else-if="item.auditResult == '32'">{{`不${item.handleName && item.handleName !== '审批' ? item.handleName : '通过'}`}}</Tag>
93
+ <Tag color="volcano" v-else-if="item.auditResult == '40'">{{item.rejectName ? item.rejectName : '驳回'}}上一节点</Tag>
94
+ <Tag color="red" v-else-if="item.auditResult=='50'">{{item.rejectName ? item.rejectName : '驳回'}}到原点</Tag>
95
+ <Tag color="purple" v-else-if="item.auditResult=='51'">流程终止</Tag>
96
+ <Tag color="blue" v-else-if="item.auditResult == '60'">撤回</Tag>
97
+ <Tag color="green" v-else-if="item.auditResult == '61'">交回委派任务</Tag>
98
+ <Tag color="blue" v-else-if="item.auditResult == '62'">委派任务被撤回</Tag>
99
+ <Tag color="orange" v-else-if="item.auditResult == '63'">委派任务</Tag>
100
+ <Tag color="cyan" v-else-if="item.auditResult == '80'">跳转指定节点</Tag>
101
+ <Tag color="cyan" v-else-if="item.auditResult == '82'">指定他人处理</Tag>
102
+ <Tag color="blue" v-else-if="item.auditResult == '83'">会签减签</Tag>
103
+ <Tag color="magenta" v-else-if="item.auditResult == '90'">{{item.rejectName ? item.rejectName : '驳回'}}指定节点</Tag>
104
+ <Tag color="orange" v-else>待办理</Tag>
105
+ </FormItem>
106
+ </i-col>
107
+ <i-col span="12" offset="0">
108
+ <FormItem v-if="item.auditDate" label="时间">
109
+ {{ item.auditDate }}
110
+ </FormItem>
111
+ <FormItem v-else :label="item.auditResult == '00' ? '发起时间' : '接收时间'">
112
+ {{ item.createTime }}
113
+ </FormItem>
114
+ </i-col>
115
+ </Row>
116
+ <Row>
117
+ <i-col span="24" offset="0">
118
+ <FormItem v-if="item.auditComment" label="意见">
119
+ {{ item.auditComment }}
120
+ </FormItem>
121
+ </i-col>
122
+ </Row>
123
+ </Form>
124
+ </Card>
125
+ </template>
126
+ </Modal>
127
+
128
+
129
+ </div>
130
+ </template>
131
+
132
+ <script>
133
+ import ajax from "@lambo-design/shared/utils/ajax";
134
+ import zoomScroll from "@lambo-design/shared/utils/zoomScroll";
135
+ import BpmnViewer from "bpmn-js/lib/Viewer";
136
+ import ModelingModule from 'bpmn-js/lib/features/modeling';
137
+ import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas';
138
+ import EditTable from "@lambo-design/edit-table";
139
+ import customRenderer from './components/customRenderer'
140
+
141
+ export default {
142
+ components: {
143
+ EditTable
144
+ },
145
+ props: {
146
+ canvasHeight: {
147
+ type: Number,
148
+ default: 350
149
+ },
150
+ modelWidth: {
151
+ type: Number,
152
+ default: 693
153
+ },
154
+ modelTitle: {
155
+ type: String,
156
+ default: "审批详情"
157
+ },
158
+ showChart: {
159
+ type: Boolean,
160
+ default: true
161
+ },
162
+ showTable: {
163
+ type: Boolean,
164
+ default: true
165
+ },
166
+ tableName: {
167
+ type: String,
168
+ default: "流程跟踪表"
169
+ },
170
+ handleName: {
171
+ type: String,
172
+ default: "审批"
173
+ },
174
+ approveDetailShowWay: {
175
+ type: String,
176
+ required: false,
177
+ default: "click"
178
+ },
179
+ predictButtonEnabled: {
180
+ type: Boolean,
181
+ default: false
182
+ },
183
+ scrollElement: {
184
+ required: false
185
+ },
186
+ tableColumns: {
187
+ type: Array,
188
+ default: () => {
189
+ return [
190
+ // {
191
+ // title: '序号',
192
+ // minWidth: 60,
193
+ // render: (h, {row, index}) => {
194
+ // // 子节点不显示序号
195
+ // if (row._isChildNode) {
196
+ // return h('span', '');
197
+ // }
198
+ // return h('span', index + 1);
199
+ // }
200
+ // },
201
+ // {title: '节点', key: 'taskName', minWidth: 130, tree: true},
202
+ {title: '序号', type: 'index', minWidth: 80},
203
+ {title: '节点', key: 'taskName', minWidth: 130},
204
+ {title: '办理人', key: 'auditName', minWidth: 150, tooltip: true},
205
+ {
206
+ title: '结果', key: 'auditResult', minWidth: 150,
207
+ render: (h, {row, column, index}) => {
208
+ let label = "";
209
+ let tagColor = "";
210
+ if (row.auditResult == '00'){
211
+ label = '流程发起';
212
+ tagColor = 'green'; // 绿色
213
+ }
214
+ else if (row.auditResult == '10'){
215
+ label = '首节点自动跳过';
216
+ tagColor = 'green'; // 绿色
217
+ }
218
+ else if (row.auditResult == '11') {
219
+ label = '与上一环节办理人相同自动跳过'
220
+ tagColor = 'green' // 绿色
221
+ }
222
+ else if (row.auditResult == '12') {
223
+ label = '与发起人相同自动跳过'
224
+ tagColor = 'green' // 绿色
225
+ }
226
+ else if (row.auditResult == '13') {
227
+ label = '办理人为空自动跳过'
228
+ tagColor = 'green' // 绿色
229
+ }
230
+ else if (row.auditResult == '14') {
231
+ label = '符合流程变量条件自动跳过'
232
+ tagColor = 'green' // 绿色
233
+ }
234
+ else if (row.auditResult == '30'){
235
+ label = '通过';
236
+ tagColor = 'green'; // 绿色
237
+ }
238
+ else if (row.auditResult == '31'){
239
+ label = `${row.passName ? row.passName : '通过'}`;
240
+ tagColor = 'green'; // 绿色
241
+ }
242
+ else if (row.auditResult == '32'){
243
+ label = `不${row.passName ? row.passName : '通过'}`;
244
+ tagColor = 'volcano'; // 红色
245
+ }
246
+ else if (row.auditResult == '40'){
247
+ label = `${row.rejectName ? row.rejectName : '驳回'}到上一级`;
248
+ tagColor = 'volcano'; // 火红色
249
+ }
250
+ else if (row.auditResult == '50'){
251
+ label = `${row.rejectName ? row.rejectName : '驳回'}到原点`;
252
+ tagColor = 'red'; // 红色
253
+ }
254
+ else if (row.auditResult == '51'){
255
+ label = '流程终止';
256
+ tagColor = 'purple'; // 紫色
257
+ }
258
+ else if (row.auditResult == '60'){
259
+ label = '撤回';
260
+ tagColor = 'blue'; // 蓝色
261
+ }
262
+ else if (row.auditResult == '61'){
263
+ label = '交回委派任务';
264
+ tagColor = 'green'; // 绿色
265
+ }
266
+ else if (row.auditResult == '62'){
267
+ label = '委派任务被撤回';
268
+ tagColor = 'blue'; // 蓝色
269
+ }
270
+ else if (row.auditResult == '63'){
271
+ label = '委派任务';
272
+ tagColor = 'orange'; // 橙色
273
+ }
274
+ else if (row.auditResult == '80'){
275
+ label = '跳转到指定节点';
276
+ tagColor = 'cyan'; // 青色
277
+ }
278
+ else if (row.auditResult == '82'){
279
+ label = '指定他人处理';
280
+ tagColor = 'cyan'; // 青色
281
+ }
282
+ else if (row.auditResult == '83'){
283
+ label = '会签减签';
284
+ tagColor = 'blue'; // 蓝色
285
+ }
286
+ else if (row.auditResult == '90'){
287
+ label = `${row.rejectName ? row.rejectName : '驳回'}到指定节点`;
288
+ tagColor = 'magenta'; // 品红色
289
+ }
290
+ else if (row.auditResult == '待分配'){
291
+ label = '待分配';
292
+ tagColor = 'orange'; // 橙色
293
+ }
294
+ else{
295
+ label = '待审核';
296
+ tagColor = 'orange'; // 默认橙色
297
+ }
298
+ return h('Tag', {
299
+ props: {
300
+ color: tagColor,
301
+ value: row.auditResult
302
+ }
303
+ }, label);
304
+ }
305
+ },
306
+ {
307
+ title: '时间', key: 'auditTime', minWidth: 150,
308
+ render: (h, {row}) => {
309
+ const displayDate = row.auditDate;
310
+ return h('span', displayDate);
311
+ }
312
+ },
313
+ {
314
+ title: '意见', key: 'auditComment', minWidth: 180, tooltip: true,
315
+ render: (h, {row, column, index}) => {
316
+ let label = "";
317
+ if (row.auditComment == '' || row.auditComment == null) label = '无';
318
+ else label = row.auditComment;
319
+ return h('Label', {
320
+ props: {
321
+ value: row.auditComment
322
+ }
323
+ }, label)
324
+ }
325
+ }
326
+ ];
327
+ }
328
+ },
329
+ hisAudit: {
330
+ type: Array,
331
+ default: () => {
332
+ return []
333
+ }
334
+ },
335
+ tableData: {
336
+ type: Array,
337
+ default: () => {
338
+ return []
339
+ }
340
+ },
341
+ applyId: {
342
+ type: String,
343
+ default: ""
344
+ },
345
+ instanceId: {
346
+ type: String,
347
+ default: ""
348
+ },
349
+ procId: {
350
+ type: String,
351
+ default: ""
352
+ },
353
+ smartFlowServerContext: {
354
+ type: String,
355
+ default: '/api/smart-flow-server',
356
+ },
357
+ currentTaskId: {
358
+ type: String,
359
+ default: ""
360
+ },
361
+ },
362
+ data() {
363
+ return {
364
+ detailDones: [],
365
+ futureDetail: null,
366
+ showDetail: false,
367
+ showDefaultExpand: this.getShowDefaultExpand(),
368
+ showAllNodes: true, // 是否显示全部节点
369
+ loadingFutureNodes: false, // 加载未来节点状态
370
+ futureNodeData: null, // 未来节点数据
371
+ doneLightSet: new Set(), // 已办连线集合
372
+ doneTaskSet: new Set(), // 已办任务集合
373
+ donePointIdSet: new Set(), // 已办节点(网关等)集合
374
+ doneNodeIdSet: new Set(), // 已办节点总集合
375
+ todoPointIdSet: new Set(), // 待办节点集合(设计模式下用于校验父子流程状态)
376
+ diagramReady: false, // 流程图是否加载完成
377
+ hisAuditTableData: [], // table数据
378
+ }
379
+ },
380
+ computed: {},
381
+ methods: {
382
+ getShowDefaultExpand() {
383
+ let expand = [];
384
+ if (this.showChart) {
385
+ expand.push('0');
386
+ }
387
+ if (this.showTable) {
388
+ expand.push('1');
389
+ }
390
+ return expand;
391
+ },
392
+
393
+ //画流程图
394
+ workflowInit() {
395
+ const canvas = this.$refs.canvas;
396
+ // this.bpmnViewer.destroy();
397
+ // if (this.bpmnViewer) return
398
+ if (this.bpmnViewer) {
399
+ this.bpmnViewer.destroy()
400
+ }
401
+ this.diagramReady = false;
402
+ this.bpmnViewer = new BpmnViewer({
403
+ container: canvas,
404
+ additionalModules: [
405
+ // MoveModule, // 可以调整元素
406
+ ModelingModule, // 基础工具 MoveModule、SetColor 等依赖于此
407
+ MoveCanvasModule, // 移动整个画布
408
+ zoomScroll, // 放大缩小
409
+ //渲染器
410
+ customRenderer
411
+ ]
412
+ });
413
+
414
+ //修改zoomScroll的scroll方法,滚动目标element
415
+ zoomScroll.zoomScroll[1].prototype.scroll = (delta)=>{
416
+ let scrollElement = this.scrollElement ? this.scrollElement : this.$refs.workflowDiagram.parentElement.parentElement
417
+ scrollElement.scrollTop -= delta.dy;
418
+ }
419
+
420
+ ajax.get(this.smartFlowServerContext + "/manage/processTodo/printData", {
421
+ params: {
422
+ applyId: this.applyId,
423
+ instanceId: this.instanceId,
424
+ procId: this.procId
425
+ }
426
+ }).then(resp => {
427
+ if (resp.data.code == '200') {
428
+ const bpmnXmlStr = resp.data.data.processXml;
429
+ this.bpmnViewer.importXML(bpmnXmlStr, (err) => {
430
+ if (err) {
431
+ this.$Message.error("加载流程xml失败," + err.message);
432
+ this.diagramReady = false;
433
+ return;
434
+ }
435
+ setTimeout(() => {
436
+ const printData = resp.data.data.historicData;
437
+ this.doPrint(printData);
438
+ this.diagramReady = true;
439
+ }, 700);
440
+ });
441
+ } else {
442
+ this.$Message.error("加载流程xml失败," + resp.data.data);
443
+ this.diagramReady = false;
444
+ }
445
+ }).catch(() => {
446
+ this.diagramReady = false;
447
+ });
448
+ },
449
+ doPrint(printData) {
450
+ var self = this;
451
+ const {doneLightSet, donePointSet, doneTaskSet, todoPointSet} =
452
+ printData;
453
+
454
+ // 保存已办连线和节点集合,供未来节点显示时使用
455
+ self.doneLightSet = new Set(doneLightSet || []);
456
+ self.doneTaskSet = new Set(doneTaskSet || []);
457
+ self.donePointIdSet = new Set(donePointSet || []);
458
+ self.doneNodeIdSet = new Set([
459
+ ...self.doneTaskSet,
460
+ ...self.donePointIdSet
461
+ ]);
462
+ self.todoPointIdSet = new Set(todoPointSet || []);
463
+
464
+ const canvas = this.bpmnViewer.get("canvas");
465
+ const elementRegistry = this.bpmnViewer.get('elementRegistry');
466
+ const userTaskList = elementRegistry.filter(
467
+ (item) => item.type === 'bpmn:UserTask'
468
+ );
469
+ //循环节点查找multiple
470
+ self.notMultiple = true;
471
+ canvas.zoom("fit-viewport", "auto");
472
+ var eventBus = this.bpmnViewer.get("eventBus");
473
+ const getDetailData = () => {
474
+ // if (Array.isArray(self.hisAudit) && self.hisAudit.length > 0) {
475
+ // return self.hisAudit;
476
+ // }
477
+ return Array.isArray(self.tableData) ? self.tableData : [];
478
+ };
479
+ // eventBus.on('element.click', function (evt) {
480
+ // eventBus.on('element.hover', function (evt) {
481
+ eventBus.on(`element.${this.approveDetailShowWay}`, function (evt) {
482
+ const detailSource = getDetailData();
483
+ if (self.showAllNodes) {
484
+ self.futureDetail = null;
485
+ self.detailDones = [];
486
+ const currentItems = detailSource.filter(item => item.taskNode === evt.element.id && !item.isFuture);
487
+ if (currentItems.length > 0) {
488
+ self.detailDones = currentItems;
489
+ self.showDetail = true;
490
+ } else {
491
+ self.hideFutureDetail();
492
+ }
493
+ return;
494
+ }
495
+
496
+ const matched = detailSource.filter(item => item.taskNode === evt.element.id);
497
+ const futureItem = matched.find(item => item.isFuture || item.auditResult === '待分配');
498
+ if (futureItem) {
499
+ self.futureDetail = self.buildFutureDetail(futureItem);
500
+ self.detailDones = [];
501
+ self.showDetail = true;
502
+ return;
503
+ }
504
+
505
+ self.futureDetail = null;
506
+ self.detailDones = matched.filter(item => !item.isFuture);
507
+ self.showDetail = self.detailDones.length > 0;
508
+ if (!self.showDetail) {
509
+ self.hideFutureDetail();
510
+ }
511
+ });
512
+
513
+ const isInsideExecutedSubProcess = (elementId) => {
514
+ if (!elementId) return false;
515
+ const element = elementRegistry.get(elementId);
516
+ if (!element) return true;
517
+ let parent = element.parent;
518
+ while (parent) {
519
+ if (parent.type === 'bpmn:SubProcess') {
520
+ const parentId = parent.id;
521
+ if (
522
+ !self.doneNodeIdSet.has(parentId) &&
523
+ !(self.todoPointIdSet && self.todoPointIdSet.has(parentId))
524
+ ) {
525
+ return false;
526
+ }
527
+ }
528
+ parent = parent.parent;
529
+ }
530
+ return true;
531
+ };
532
+
533
+ for (const k in doneLightSet) {
534
+ const lineId = doneLightSet[k];
535
+ if (lineId && isInsideExecutedSubProcess(lineId)) {
536
+ canvas.addMarker(lineId, "done-line");
537
+ }
538
+ }
539
+ for (const k in donePointSet) {
540
+ const pointId = donePointSet[k];
541
+ if (pointId && isInsideExecutedSubProcess(pointId)) {
542
+ canvas.addMarker(pointId, "done-point");
543
+ }
544
+ }
545
+ for (const k in doneTaskSet) {
546
+ const taskId = doneTaskSet[k];
547
+ if (taskId && isInsideExecutedSubProcess(taskId)) {
548
+ canvas.addMarker(taskId, "done-task");
549
+ }
550
+ }
551
+ for (const k in todoPointSet) {
552
+ const todoId = todoPointSet[k];
553
+ if (todoId && isInsideExecutedSubProcess(todoId)) {
554
+ canvas.addMarker(todoId, "todo-point");
555
+ }
556
+ }
557
+
558
+ },
559
+
560
+ buildFutureDetail(item) {
561
+ const names = (item.auditName || '')
562
+ .split(',')
563
+ .map(name => name && name.trim())
564
+ .filter(Boolean);
565
+ return {
566
+ taskName: item.taskName || '',
567
+ assigneeDisplay: names.length > 0 ? names.join(',') : '暂无人员'
568
+ };
569
+ },
570
+
571
+ hideFutureDetail() {
572
+ this.futureDetail = null;
573
+ this.detailDones = [];
574
+ this.showDetail = false;
575
+ },
576
+
577
+ // 切换节点显示模式
578
+ async toggleNodeDisplay() {
579
+ if (this.showAllNodes) {
580
+ // 当前显示全部,切换为只显示未来节点
581
+ const success = await this.loadFutureNodes();
582
+ if (!success) return;
583
+ this.showAllNodes = false;
584
+ // 发射事件通知父组件显示预测路线
585
+ this.$emit('toggle-predictive', {showPredictive: true, futureNodeData: this.futureNodeData});
586
+ } else {
587
+ // 当前显示未来节点,切换回显示全部
588
+ this.showAllNodesInDiagram();
589
+ this.hideFutureDetail();
590
+ this.showAllNodes = true;
591
+ // 发射事件通知父组件隐藏预测路线
592
+ this.$emit('toggle-predictive', {showPredictive: false, futureNodeData: null});
593
+ }
594
+ },
595
+
596
+ // 加载未来节点数据
597
+ async loadFutureNodes() {
598
+ if (!this.instanceId || !this.currentTaskId) {
599
+ this.$Message.warning('缺少必要参数:流程实例ID或当前任务ID');
600
+ return false;
601
+ }
602
+ if (!this.diagramReady) {
603
+ this.$Message.warning('流程图尚未加载完成,请稍后重试');
604
+ return false;
605
+ }
606
+
607
+ this.loadingFutureNodes = true;
608
+ try {
609
+ const resp = await ajax.get(this.smartFlowServerContext + "/manage/processTodo/renderFutureNode", {
610
+ params: {
611
+ processInstanceId: this.instanceId,
612
+ currentTaskId: this.currentTaskId
613
+ }
614
+ });
615
+
616
+ if (resp.data.code == '200' && resp.data.data) {
617
+ const futureData = resp.data.data;
618
+ const currentNodeId = futureData.currentNodeId;
619
+ const elementRegistry = this.bpmnViewer && this.bpmnViewer.get('elementRegistry');
620
+ if (currentNodeId && elementRegistry) {
621
+ const currentElement = elementRegistry.get(currentNodeId);
622
+ if (currentElement && this.isElementInSubProcess(currentElement)) {
623
+ this.$Message.warning('预测路线暂不支持子流程节点,请查看设计路线');
624
+ return false;
625
+ }
626
+ }
627
+ this.futureNodeData = futureData;
628
+ this.showFutureNodesOnly();
629
+ return true;
630
+ } else {
631
+ this.$Message.error("加载未来节点失败:" + resp.data.message);
632
+ return false;
633
+ }
634
+ } catch (error) {
635
+ this.$Message.error("加载未来节点失败:" + error.message);
636
+ return false;
637
+ } finally {
638
+ this.loadingFutureNodes = false;
639
+ }
640
+ },
641
+
642
+ // 只显示未来节点
643
+ showFutureNodesOnly() {
644
+ if (!this.futureNodeData || !this.bpmnViewer) return;
645
+
646
+ const elementRegistry = this.bpmnViewer.get('elementRegistry');
647
+ const modeling = this.bpmnViewer.get('modeling');
648
+
649
+ // 获取所有元素
650
+ const allElements = elementRegistry.getAll();
651
+
652
+ // 从后端数据中提取节点和边信息
653
+ const {nodes = [], edges = [], futureNodeIds = [], currentNodeId = ''} = this.futureNodeData;
654
+
655
+ // 构建节点ID到节点数据的映射
656
+ const nodeMap = new Map();
657
+ nodes.forEach(node => {
658
+ nodeMap.set(node.id, node);
659
+ });
660
+
661
+ // 构建边ID到边数据的映射
662
+ const edgeMap = new Map();
663
+ edges.forEach(edge => {
664
+ edgeMap.set(edge.id, edge);
665
+ });
666
+
667
+ // 已办连线和节点集合(使用 Set 便于判断)
668
+ const doneLightSet = this.doneLightSet instanceof Set ? this.doneLightSet : new Set(this.doneLightSet || []);
669
+ const doneNodeSet = this.doneNodeIdSet instanceof Set ? this.doneNodeIdSet : new Set(this.doneNodeIdSet || []);
670
+ const hiddenNodeSet = new Set(this.futureNodeData.hiddenNodeIds || []);
671
+ const hiddenEdgeSet = new Set(this.futureNodeData.hiddenEdgeIds || []);
672
+
673
+ const futureNodeIdSet = new Set(futureNodeIds || []);
674
+
675
+ const visibleSubProcessIds = new Set(this.futureNodeData.visibleSubProcessIds || []);
676
+
677
+ // 遍历所有元素
678
+ allElements.forEach(element => {
679
+ const gfx = elementRegistry.getGraphics(element);
680
+ if (!gfx) return;
681
+
682
+ // 开始事件和结束事件
683
+ if (element.type === 'bpmn:StartEvent' || element.type === 'bpmn:EndEvent') {
684
+ const nodeData = nodeMap.get(element.id) || {};
685
+ const nodeFutureVisible = nodeData.hasOwnProperty('futureVisible')
686
+ ? nodeData.futureVisible
687
+ : !hiddenNodeSet.has(element.id);
688
+ const parentSubProcess = element.parent && element.parent.type === 'bpmn:SubProcess' ? element.parent : null;
689
+ const parentVisible = parentSubProcess ? visibleSubProcessIds.has(parentSubProcess.id) : true;
690
+ const parentDone = parentSubProcess && doneNodeSet.has(parentSubProcess.id);
691
+ if (parentSubProcess && !parentVisible && !parentDone) {
692
+ gfx.style.display = 'none';
693
+ return;
694
+ }
695
+ const shouldDisplay = nodeFutureVisible || doneNodeSet.has(element.id);
696
+ if (!shouldDisplay) {
697
+ gfx.style.display = 'none';
698
+ return;
699
+ }
700
+ gfx.style.display = '';
701
+ gfx.style.opacity = '1';
702
+ return;
703
+ }
704
+
705
+ // 处理任务节点
706
+ if (element.type === 'bpmn:UserTask' ||
707
+ element.type === 'bpmn:ServiceTask' ||
708
+ element.type === 'bpmn:Task' ||
709
+ element.type === 'bpmn:CallActivity') {
710
+
711
+ const nodeData = nodeMap.get(element.id) || {};
712
+ const nodeFutureVisible = nodeData.hasOwnProperty('futureVisible')
713
+ ? nodeData.futureVisible
714
+ : !hiddenNodeSet.has(element.id);
715
+ const parentId = nodeData.parentSubProcessId;
716
+ const parentIsDone = parentId && doneNodeSet.has(parentId);
717
+ const isDoneTask = doneNodeSet.has(element.id) || parentIsDone;
718
+ const shouldDisplay = nodeFutureVisible || isDoneTask;
719
+
720
+ if (!shouldDisplay) {
721
+ gfx.style.display = 'none';
722
+ return;
723
+ }
724
+
725
+ gfx.style.display = '';
726
+ gfx.style.opacity = '1';
727
+
728
+ if (nodeData.futureHighlight) {
729
+ const visualElement = gfx.querySelector('.djs-visual > :nth-child(1)');
730
+ if (visualElement) {
731
+ visualElement.style.stroke = '#FFD700';
732
+ visualElement.style.strokeWidth = '2px';
733
+ }
734
+ }
735
+ }
736
+
737
+ // 处理连线
738
+ if (element.type === 'bpmn:SequenceFlow') {
739
+ const edgeData = edgeMap.get(element.id) || {};
740
+ const target = element.target;
741
+ const source = element.source;
742
+ const targetParent = target && target.parent && target.parent.type === 'bpmn:SubProcess' ? target.parent : null;
743
+ const sourceParent = source && source.parent && source.parent.type === 'bpmn:SubProcess' ? source.parent : null;
744
+ const targetParentDone = targetParent && doneNodeSet.has(targetParent.id);
745
+ const sourceParentDone = sourceParent && doneNodeSet.has(sourceParent.id);
746
+ const targetIsDoneTask = target && doneNodeSet.has(target.id) || targetParentDone;
747
+ const sourceIsDoneTask = source && doneNodeSet.has(source.id) || sourceParentDone;
748
+ // 修改:严格判断连线是否已办,只有连线本身在doneLightSet中才视为已办
749
+ const isDoneLine = doneLightSet.has(element.id);
750
+
751
+ const edgeFutureVisible = edgeData.hasOwnProperty('futureVisible')
752
+ ? edgeData.futureVisible
753
+ : !hiddenEdgeSet.has(element.id);
754
+
755
+ if (!(edgeFutureVisible || isDoneLine)) {
756
+ gfx.style.display = 'none';
757
+ return;
758
+ }
759
+
760
+ gfx.style.display = '';
761
+ gfx.style.opacity = '1';
762
+
763
+ const shouldHighlight = edgeData.futureHighlight === true;
764
+ if (shouldHighlight) {
765
+ const canvas = this.bpmnViewer.get('canvas');
766
+ if (canvas && canvas.hasMarker(element.id, 'done-line')) {
767
+ canvas.removeMarker(element.id, 'done-line');
768
+ }
769
+ const visualElement = gfx.querySelector('.djs-visual > :nth-child(1)');
770
+ if (visualElement) {
771
+ visualElement.style.stroke = '#FFD700';
772
+ visualElement.style.strokeWidth = '2px';
773
+ }
774
+ }
775
+ }
776
+
777
+ // 处理网关节点
778
+ if (element.type.includes('Gateway')) {
779
+ const nodeData = nodeMap.get(element.id) || {};
780
+ const nodeFutureVisible = nodeData.hasOwnProperty('futureVisible')
781
+ ? nodeData.futureVisible
782
+ : !hiddenNodeSet.has(element.id);
783
+
784
+ // 检查是否有已办连线连接
785
+ const hasConnectedDone = element.incoming?.some(conn => {
786
+ const source = conn.source;
787
+ //连线本身已办或者连线的来源节点
788
+ return doneLightSet.has(conn.id) || (source && doneNodeSet.has(source.id));
789
+ }) || element.outgoing?.some(conn => {
790
+ const target = conn.target;
791
+ //连线本身已办或者连线的目标节点已办
792
+ return doneLightSet.has(conn.id) || (target && doneNodeSet.has(target.id));
793
+ });
794
+
795
+ // 检查是否同时连接到已办节点和预测路径上的节点
796
+ // 确保网关既连接了已办部分又连接了预测路径部分
797
+ // 只有同时满足这两个条件的网关才应该显示(除非它本身在预测路径中或已办)
798
+ const hasIncomingDone = element.incoming?.some(conn => {
799
+ const source = conn.source;
800
+ return doneLightSet.has(conn.id) || (source && doneNodeSet.has(source.id));
801
+ });
802
+
803
+ const hasOutgoingFuture = element.outgoing?.some(conn => {
804
+ const target = conn.target;
805
+ const connData = edgeMap.get(conn.id) || {};
806
+ return connData.futureVisible || (target && nodeMap.get(target.id)?.futureVisible);
807
+ });
808
+
809
+ // 严格网关显示条件,只有以下情况才显示网关:
810
+ // 1. 网关在预测路径中
811
+ // 2. 网关本身已办
812
+ // 3. 网关同时连接了已办节点和预测路径节点(双向连接,确保不是孤立网关)
813
+ const shouldDisplay = nodeFutureVisible || doneNodeSet.has(element.id) || (hasIncomingDone && hasOutgoingFuture);
814
+ if (!shouldDisplay) {
815
+ gfx.style.display = 'none';
816
+ return;
817
+ }
818
+ gfx.style.display = '';
819
+ gfx.style.opacity = '1';
820
+ const visualElement = gfx.querySelector('.djs-visual > :nth-child(1)');
821
+ if (visualElement) {
822
+ if (nodeData.futureHighlight) {
823
+ visualElement.style.stroke = '#FFD700';
824
+ visualElement.style.strokeWidth = '2px';
825
+ } else {
826
+ visualElement.style.stroke = '#000000';
827
+ visualElement.style.strokeWidth = '2px';
828
+ }
829
+ }
830
+ }
831
+
832
+ // 处理子流程节点(SubProcess)
833
+ if (element.type === 'bpmn:SubProcess') {
834
+ const nodeData = nodeMap.get(element.id) || {};
835
+ const nodeFutureVisible = nodeData.hasOwnProperty('futureVisible')
836
+ ? nodeData.futureVisible
837
+ : visibleSubProcessIds.has(element.id);
838
+ const shouldDisplay = nodeFutureVisible || doneNodeSet.has(element.id);
839
+ if (!shouldDisplay) {
840
+ gfx.style.display = 'none';
841
+ return;
842
+ }
843
+ gfx.style.display = '';
844
+ gfx.style.opacity = '1';
845
+ const visualElement = gfx.querySelector('.djs-visual > :nth-child(1)');
846
+ if (visualElement) {
847
+ if (nodeData.futureHighlight) {
848
+ visualElement.style.stroke = '#FFD700';
849
+ visualElement.style.strokeWidth = '2px';
850
+ } else {
851
+ visualElement.style.stroke = '#000000';
852
+ visualElement.style.strokeWidth = '2px';
853
+ }
854
+ }
855
+ }
856
+
857
+ // 处理 label(如连线名称)
858
+ if (element.type === 'label' && element.labelTarget) {
859
+ const target = element.labelTarget;
860
+ const targetEdgeData = edgeMap.get(target.id) || {};
861
+ const futureLabelVisible = targetEdgeData.hasOwnProperty('futureLabelVisible')
862
+ ? targetEdgeData.futureLabelVisible
863
+ : false;
864
+ const targetIsDoneLine = doneLightSet.has(target.id);
865
+ gfx.style.display = (futureLabelVisible || targetIsDoneLine) ? '' : 'none';
866
+ }
867
+ });
868
+
869
+ },
870
+
871
+ // 显示全部节点
872
+ showAllNodesInDiagram() {
873
+ if (!this.bpmnViewer) return;
874
+ this.hideFutureDetail();
875
+
876
+ // 重新加载流程图是最可靠的恢复方式
877
+ // 这样可以避免样式清除不彻底导致的黑框问题
878
+ this.workflowInit();
879
+ },
880
+
881
+ isElementInSubProcess(element) {
882
+ let parent = element && element.parent;
883
+ while (parent) {
884
+ if (parent.type === 'bpmn:SubProcess') {
885
+ return true;
886
+ }
887
+ parent = parent.parent;
888
+ }
889
+ return false;
890
+ },
891
+
892
+ // 校验必填
893
+ isNotBlank(val) {
894
+ return (val != null || val === 0) && (val.toString().trim() != '' || val === 0)
895
+ }
896
+ },
897
+ mounted() {
898
+ if (this.isNotBlank(this.applyId)) {
899
+ this.$nextTick(() => {
900
+ this.workflowInit();
901
+ });
902
+ }
903
+ },
904
+ watch: {
905
+ applyId(val) {
906
+ if (this.isNotBlank(val)) {
907
+ this.workflowInit();
908
+ }
909
+ },
910
+ instanceId(val) {
911
+ if (this.isNotBlank(this.applyId) && this.isNotBlank(val)) {
912
+ this.workflowInit();
913
+ }
914
+ },
915
+ tableData: {
916
+ handler(val) {
917
+ this.hisAuditTableData = val.filter(
918
+ item => !item.status || item.status !== 'future'
919
+ );
920
+ },
921
+ immediate: true
922
+ }
923
+ }
924
+ }
925
+ </script>
926
+
927
+ <style lang="less">
928
+ @import "./styles/css/index.less";
929
+
930
+ .future-detail-modal .ivu-modal-header {
931
+ border-bottom: none;
932
+ padding-bottom: 0;
933
+ }
934
+
935
+ .future-detail-modal .ivu-modal-body {
936
+ padding: 20px 24px;
937
+ }
938
+
939
+ .future-detail-card {
940
+ background: #f7f8fa;
941
+ border-radius: 8px;
942
+ padding: 16px;
943
+ }
944
+
945
+ .future-detail-title {
946
+ font-size: 16px;
947
+ font-weight: 600;
948
+ color: #1f2329;
949
+ margin-bottom: 12px;
950
+ }
951
+
952
+ .future-detail-body {
953
+ display: flex;
954
+ align-items: center;
955
+ gap: 12px;
956
+ font-size: 14px;
957
+ color: #4e5969;
958
+ }
959
+
960
+ .future-detail-status {
961
+ background: #f7dba7;
962
+ color: #8f5d00;
963
+ border-radius: 999px;
964
+ padding: 4px 12px;
965
+ font-size: 12px;
966
+ line-height: 1;
967
+ }
968
+
969
+ .future-detail-users {
970
+ flex: 1;
971
+ white-space: nowrap;
972
+ overflow: hidden;
973
+ text-overflow: ellipsis;
974
+ }
975
+ </style>
976
+
977
+
978
+
979
+
980
+