@lambo-design-mobile/workflow-approve 1.0.0-beta.1

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.
@@ -0,0 +1,521 @@
1
+ <template>
2
+ <div style="background: #f7f8fa">
3
+ <div id="headTitle" class="headTitle" :style="headerStyle">
4
+ <div class="van-nav-bar__content">
5
+ <div class="van-nav-bar__left">
6
+ <slot name="nav-bar-left"/>
7
+ </div>
8
+ <div class="van-nav-bar__right">
9
+ <slot name="nav-bar-right"/>
10
+ </div>
11
+ <div class="van-nav-bar__title">
12
+ <slot name="nav-bar-title"/>
13
+ </div>
14
+ </div>
15
+ <div class="task-status">
16
+ <van-search
17
+ show-action
18
+ placeholder="请输入查询条件"
19
+ v-model="searchForm.detailName"
20
+ @search="onSearch"
21
+ >
22
+ <template #left-icon>
23
+ <lambo-scan-code v-if="scanSearch"
24
+ style="color: #0068FF"
25
+ iconName="scan"
26
+ iconSize="24px"
27
+ :onScanSuccess="handleScanSuccess"
28
+ :onScanFail="handleScanFail"
29
+ />
30
+ <van-icon v-else class="iconfont" class-prefix='icon' name="tqm-search" color="#0068FF" size="20px"></van-icon>
31
+ </template>
32
+ <template #right-icon>
33
+ <div @click="onSearch" class="todo-search">搜索</div>
34
+ </template>
35
+ <template #action>
36
+ <van-badge :dot="searchFilterBadge">
37
+ <van-icon @click="searchFilterShow = !searchFilterShow;" name="filter-o" color="#666666" size="25px"/>
38
+ </van-badge>
39
+ </template>
40
+ </van-search>
41
+ </div>
42
+ </div>
43
+ <div id="listContent" style="padding-top: 32vw">
44
+ <van-list v-model="loading" style="padding-top: 15px"
45
+ :finished="finished"
46
+ finished-text="没有更多了"
47
+ @load="handleLoad">
48
+ <TodoListCard ref="todoListCard" :selected-task="selectedTask" :todo-list="listData.rows" :result.sync="checkResult"
49
+ :business-approval-router-name="businessApprovalRouterName"
50
+ :business-details-router-name="businessDetailsRouterName"
51
+ :process-approval-router-name="processApprovalRouterName"
52
+ :process-details-router-name="processDetailsRouterName"/>
53
+ </van-list>
54
+ </div>
55
+ <div v-if="checkResult.length > 0" class="custom-bottom-bar">
56
+ <div class="bar-item">
57
+ <van-checkbox v-model="batchSelectChecked" @change="batchSelect" style="justify-content: center;"
58
+ icon-size="18px">全选
59
+ </van-checkbox>
60
+ </div>
61
+ <div class="bar-item" @click="batchApproval(1)">批量驳回</div>
62
+ <div class="bar-item approve" @click="batchApproval(0)">批量通过</div>
63
+ </div>
64
+ <van-dialog v-model="batchApprovalDialog" :before-close="handleBeforeClose" @confirm="batchApprovalSubmit"
65
+ :title="this.batchApprovalForm.approvalStatus===0 ?'批量通过':'批量驳回'"
66
+ show-cancel-button>
67
+ <van-field ref="auditOpinionField" v-model="batchApprovalForm.auditOpinion"
68
+ rows="3"
69
+ autosize
70
+ label="审批意见"
71
+ type="textarea"
72
+ maxlength="50"
73
+ placeholder="请输入审批意见"
74
+ show-word-limit
75
+ />
76
+ </van-dialog>
77
+ <van-popup v-model="searchFilterShow" ref="searchPopup" position="top"
78
+ get-container="#headTitle" style="margin-top: 32vw">
79
+ <div style="padding: 12px">
80
+ <van-form>
81
+ <van-field clearable v-model="searchForm.businessTopic" label="业务主题" placeholder="请输入业务主题"/>
82
+ <van-field
83
+ readonly
84
+ name="calendar"
85
+ :value="formatCalendarDate(searchForm.startDate)"
86
+ label="发起时间"
87
+ placeholder="点击选择发起时间"
88
+ @click="showCalendar = true"
89
+ />
90
+ <van-field
91
+ readonly
92
+ name="picker"
93
+ :value="searchForm.procTypeName"
94
+ label="流程类型"
95
+ placeholder="点击选择流程类型"
96
+ @click="showPicker = true"
97
+ />
98
+ <van-field clearable v-model="searchForm.taskName" label="环节名称" placeholder="请输入环节名称"/>
99
+ <div style="display: flex;justify-content: space-around;padding-top:10px">
100
+ <span @click="resetSearch" class="resetSearch">重置</span>
101
+ <span @click="onSearch" class="submitForm">查询</span>
102
+ </div>
103
+ </van-form>
104
+ </div>
105
+ </van-popup>
106
+ <van-calendar style="height:60%"
107
+ :min-date="new Date(1900, 0, 1)"
108
+ :max-date=" new Date()"
109
+ v-model="showCalendar" @confirm="onDateConfirm" color="#1989fa"/>
110
+ <van-popup v-model="showPicker" position="bottom">
111
+ <van-picker
112
+ show-toolbar
113
+ :columns="columns"
114
+ @confirm="onPickerConfirm"
115
+ @cancel="showPicker = false"
116
+ />
117
+ </van-popup>
118
+ </div>
119
+ </template>
120
+
121
+ <script>
122
+ import todoBackImage from './assets/todoBackImage.png';
123
+ import {batchApproval, getApprovalType} from "../api";
124
+ import {Toast} from "vant";
125
+ import LamboScanCode from "@lambo-design-mobile/lambo-scan-code";
126
+ import TodoListCard from "./TodoListCard.vue";
127
+
128
+ export default {
129
+ name: 'FlowBaseList',
130
+ components: {TodoListCard, LamboScanCode},
131
+ props: {
132
+ scanSearch: {
133
+ type: Boolean,
134
+ default: false
135
+ },
136
+ selectedTask: {
137
+ type: String,
138
+ required: true
139
+ },
140
+ fetchListData: {
141
+ type: Function,
142
+ required: true
143
+ },
144
+ headerBackground: {
145
+ type: String,
146
+ default: ''
147
+ },
148
+ // 新增 4 个路由 name 参数
149
+ businessDetailsRouterName: {
150
+ type: String,
151
+ required: true
152
+ },
153
+ processDetailsRouterName: {
154
+ type: String,
155
+ required: true
156
+ },
157
+ businessApprovalRouterName: {
158
+ type: String,
159
+ required: true
160
+ },
161
+ processApprovalRouterName: {
162
+ type: String,
163
+ required: true
164
+ }
165
+ },
166
+ data() {
167
+ return {
168
+ todoBackImage,
169
+ searchFilterShow: false,
170
+ searchFilterBadge: false,
171
+ loading: false,
172
+ finished: false,
173
+ searchForm: {
174
+ detailName: '',
175
+ applyId: '',
176
+ procName: '',
177
+ startDate: '',
178
+ procType: '',
179
+ procTypeName: '',
180
+ taskName: '',
181
+ businessTopic: '',
182
+ },
183
+ showCalendar: false,
184
+ showPicker: false,
185
+ columns: [],
186
+ listData: {
187
+ total: 0,
188
+ rows: []
189
+ },
190
+ checkResult: [],
191
+ batchSelectChecked: false,
192
+ batchApprovalDialog: false,
193
+ batchApprovalForm: {
194
+ approvalStatus: "",
195
+ auditOpinion: "",
196
+ },
197
+ }
198
+ },
199
+ computed: {
200
+ // 添加计算属性来处理头部样式
201
+ headerStyle() {
202
+ return {
203
+ backgroundImage: this.headerBackground ? `url(${this.headerBackground})` : `url(${this.todoBackImage})`
204
+ }
205
+ }
206
+ },
207
+ methods: {
208
+ async handleLoad() {
209
+ this.loading = true;
210
+ const offset = this.listData.rows.length;
211
+ const limit = 10;
212
+
213
+ try {
214
+ const res = await this.fetchListData(offset, limit, this.searchForm);
215
+ const result = res.data;
216
+ if (result.code === '200') {
217
+ this.listData.rows = this.listData.rows.concat(result.data.rows);
218
+ this.listData.total = result.data.total;
219
+ } else {
220
+ console.error('Failed to load data');
221
+ }
222
+ } catch (error) {
223
+ console.error('Error fetching data:', error);
224
+ }
225
+
226
+ this.loading = false;
227
+ this.finished = this.listData.rows.length >= this.listData.total;
228
+ },
229
+ resetSearch() {
230
+ for (let key in this.searchForm) {
231
+ if (this.searchForm.hasOwnProperty(key)) {
232
+ this.searchForm[key] = '';
233
+ }
234
+ }
235
+ },
236
+ onSearch() {
237
+ if (this.searchFilterShow) {
238
+ this.searchFilterShow = false;
239
+ this.$nextTick(() => {
240
+ if (this.$refs.searchPopup) {
241
+ this.$refs.searchPopup.$once('closed', () => {
242
+ this.resetAndLoadList();
243
+ });
244
+ }
245
+ });
246
+ } else {
247
+ this.resetAndLoadList();
248
+ }
249
+ },
250
+ resetAndLoadList() {
251
+ this.listData.total = 0;
252
+ this.listData.rows = [];
253
+ this.handleLoad();
254
+ },
255
+ formatCalendarDate(date) {
256
+ if (date === '') return '';
257
+ if (!(date instanceof Date)) {
258
+ date = new Date(date);
259
+ }
260
+ const year = date.getFullYear();
261
+ const month = String(date.getMonth() + 1).padStart(2, '0');
262
+ const day = String(date.getDate()).padStart(2, '0');
263
+ return `${year}-${month}-${day}`;
264
+ },
265
+ onDateConfirm(date) {
266
+ this.searchForm.startDate = date.toISOString();
267
+ this.showCalendar = false;
268
+ },
269
+ onPickerConfirm(value) {
270
+ this.searchForm.procTypeName = value.text;
271
+ this.searchForm.procType = value.proType;
272
+ this.showPicker = false;
273
+ },
274
+ checkSearchForm() {
275
+ this.searchFilterBadge = Object.values(this.searchForm).some(value => value !== '');
276
+ },
277
+ handleScanSuccess(msg) {
278
+ this.searchForm.detailName = msg;
279
+ this.onSearch();
280
+ },
281
+ handleScanFail(msg) {
282
+ Toast.fail(msg);
283
+ },
284
+ batchApproval(status) {
285
+ if (this.checkResult.length > 10) {
286
+ Toast.fail("最多同时审批十条记录");
287
+ return;
288
+ }
289
+
290
+ const procTypeSet = new Set(this.checkResult.map(flowId => {
291
+ const item = this.listData.rows.find(item => item.flowId === flowId);
292
+ return item.procType;
293
+ }));
294
+
295
+ if (procTypeSet.size > 1) {
296
+ Toast.fail("只能批量审批同类型流程");
297
+ return;
298
+ }
299
+
300
+ this.batchApprovalForm.approvalStatus = status;
301
+ this.batchApprovalDialog = true;
302
+ },
303
+ async batchApprovalSubmit() {
304
+ const flowIds = this.checkResult.join(",");
305
+ const status = this.batchApprovalForm.approvalStatus;
306
+ const auditOpinion = this.batchApprovalForm.auditOpinion;
307
+
308
+ try {
309
+ const res = await batchApproval(status, flowIds, auditOpinion);
310
+ if (res.data.code === '200') {
311
+ this.checkResult = [];
312
+ this.onSearch();
313
+ Toast.success("审批成功");
314
+ }
315
+ } catch (error) {
316
+ console.error('Error submitting batch approval:', error);
317
+ Toast.fail("审批失败");
318
+ }
319
+ },
320
+ handleBeforeClose(action, done) {
321
+ if (action === 'confirm' && !this.batchApprovalForm.auditOpinion) {
322
+ Toast("审批信息不能为空");
323
+ return done(false);
324
+ }
325
+ if (action === 'confirm') {
326
+ this.batchApprovalSubmit();
327
+ }
328
+ done();
329
+ this.batchApprovalForm.auditOpinion = '';
330
+ },
331
+ batchSelect(checked) {
332
+ if (checked) {
333
+ this.$refs.todoListCard.checkAll();
334
+ } else {
335
+ this.$refs.todoListCard.toggleAll();
336
+ }
337
+ },
338
+ },
339
+ async mounted() {
340
+ try {
341
+ const res = await getApprovalType();
342
+ if (res.data.code === '200') {
343
+ this.columns = res.data.data.map(item => ({
344
+ text: item.procTypeName,
345
+ proType: item.proType
346
+ }));
347
+ }
348
+ } catch (error) {
349
+ console.error('Error fetching approval types:', error);
350
+ }
351
+ },
352
+ watch: {
353
+ searchForm: {
354
+ handler: 'checkSearchForm',
355
+ deep: true
356
+ }
357
+ }
358
+ }
359
+ </script>
360
+
361
+ <style scoped>
362
+ @import 'styles/global.css';
363
+
364
+ ::v-deep .van-dialog__header{
365
+ padding-top: 10px;
366
+ }
367
+
368
+ .custom-bottom-bar {
369
+ position: fixed;
370
+ bottom: 0;
371
+ width: 100%;
372
+ display: flex;
373
+ justify-content: space-around;
374
+ background-color: #fff;
375
+ padding: 10px 0;
376
+ border-top: 1px solid #eaeaea;
377
+ box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.05);
378
+ z-index: 1000;
379
+ }
380
+
381
+ .bar-item {
382
+ flex: 1;
383
+ text-align: center;
384
+ padding: 10px 0;
385
+ font-size: 16px;
386
+ border-radius: 4px;
387
+ }
388
+
389
+ .bar-item:active {
390
+ background: #e6e6e6; /* 点击时的背景颜色 */
391
+ }
392
+
393
+ .approve {
394
+ color: #fff;
395
+ background: linear-gradient(90deg, #0096FF, #1677FF);
396
+ border-radius: 9px;
397
+ margin: 0 20px;
398
+ }
399
+
400
+ .approve:active {
401
+ background: linear-gradient(90deg, rgba(0, 150, 255, 0.8), rgba(22, 119, 255, 0.8));
402
+ }
403
+
404
+ .headTitle {
405
+ position: fixed; /* 固定在页面的顶部 */
406
+ top: 0; /* 设置距离顶部为 0 */
407
+ left: 0; /* 设置距离左边为 0 */
408
+ width: 100vw; /* 占满页面的宽度 */
409
+ z-index: 1000; /* 确保头部在其他元素之上 */
410
+
411
+ background-size: cover; /* 背景图片覆盖整个容器 */
412
+ background-repeat: no-repeat; /* 防止背景图片重复 */
413
+ background-position: center; /* 背景图片居中显示 */
414
+ }
415
+
416
+ .task-status {
417
+ padding: 10px 0;
418
+ }
419
+
420
+ .van-search {
421
+ background-color: transparent;
422
+ border-radius: 6px;
423
+ }
424
+
425
+ .van-search__content {
426
+ border-radius: 6px;
427
+ }
428
+
429
+ .van-badge__wrapper {
430
+ display: flex;
431
+ }
432
+
433
+ .van-search__action {
434
+ display: flex;
435
+ align-items: center;
436
+ padding: 5px 5px;
437
+ transition: background-color 0.3s ease; /* 添加过渡效果 */
438
+ }
439
+
440
+ .van-search__action:active {
441
+ border-radius: 6px;
442
+ background-color: rgba(0, 0, 0, 0.1); /* 点击时显示半透明背景 */
443
+ }
444
+
445
+ ::v-deep .van-field__left-icon {
446
+ display: flex;
447
+ align-items: center;
448
+ }
449
+
450
+ .todo-search {
451
+ display: flex;
452
+ justify-content: center;
453
+ align-items: center;
454
+
455
+ color: white;
456
+ width: 58px;
457
+ height: 30px;
458
+ background: linear-gradient(90deg, #0096FF, #1677FF);
459
+ border-radius: 6px;
460
+ }
461
+
462
+ .todo-search:active {
463
+ background: linear-gradient(90deg, rgba(0, 150, 255, 0.8), rgba(22, 119, 255, 0.79));
464
+ }
465
+
466
+ .van-nav-bar__title {
467
+ flex-grow: 1; /* 让标题占据剩余空间,从而使其居中 */
468
+ text-align: center; /* 确保文本在剩余空间中居中显示 */
469
+ }
470
+
471
+ .icon-flex {
472
+ position: absolute;
473
+ left: 10px; /* 或根据需要调整位置 */
474
+ top: 60%;
475
+ transform: translateY(-50%); /* 垂直居中 */
476
+ }
477
+
478
+ ::v-deep .van-overlay {
479
+ margin-top: 32vw;
480
+ }
481
+
482
+ ::v-deep .van-popup {
483
+ transform: translateY(0); /* 防止动画从顶部开始 */
484
+ }
485
+
486
+ .resetSearch {
487
+ color: black;
488
+ width: 160px;
489
+ height: 35px;
490
+ background: #F2F2F2;
491
+ border-radius: 9px;
492
+ font-size: 14px;
493
+
494
+ display: flex;
495
+ justify-content: center;
496
+ align-items: center;
497
+ }
498
+
499
+ .resetSearch:active {
500
+ background: rgba(242, 242, 242, 0.8);
501
+ }
502
+
503
+ .submitForm {
504
+ color: white;
505
+ width: 160px;
506
+ height: 35px;
507
+ background: linear-gradient(90deg, #0096FF, #1677FF);
508
+ border-radius: 9px;
509
+ font-size: 14px;
510
+
511
+
512
+ display: flex;
513
+ justify-content: center;
514
+ align-items: center;
515
+ }
516
+
517
+ .submitForm:active {
518
+ background: linear-gradient(90deg, rgba(0, 150, 255, 0.8), rgba(22, 119, 255, 0.79));
519
+ }
520
+
521
+ </style>
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <FlowBaseList
3
+ selected-task="complete"
4
+ :header-background=headerBackground
5
+ :fetch-list-data="fetchListData"
6
+ :scan-search=scanSearch
7
+ :business-approval-router-name="businessApprovalRouterName"
8
+ :business-details-router-name="businessDetailsRouterName"
9
+ :process-approval-router-name="processApprovalRouterName"
10
+ :process-details-router-name="processDetailsRouterName">
11
+ <template #nav-bar-left>
12
+ <slot name="nav-bar-left"/>
13
+ </template>
14
+ <template #nav-bar-title>
15
+ {{ title }}
16
+ </template>
17
+ <template #nav-bar-right>
18
+ <slot name="nav-bar-right"/>
19
+ </template>
20
+ </FlowBaseList>
21
+ </template>
22
+
23
+ <script>
24
+ import FlowBaseList from './FlowBaseList.vue';
25
+ import { getProcessDoneList } from "../api";
26
+
27
+ export default {
28
+ name: 'FlowTodoList',
29
+ components: {
30
+ FlowBaseList
31
+ },
32
+ props: {
33
+ title: {
34
+ type: String,
35
+ required: true
36
+ },
37
+ scanSearch: {
38
+ type: Boolean,
39
+ default: false
40
+ },
41
+ // 新增的背景图属性
42
+ headerBackground: {
43
+ type: String,
44
+ default: ''
45
+ },
46
+ // 新增 4 个路由 name 参数
47
+ businessDetailsRouterName: {
48
+ type: String,
49
+ required: true
50
+ },
51
+ processDetailsRouterName: {
52
+ type: String,
53
+ required: true
54
+ },
55
+ businessApprovalRouterName: {
56
+ type: String,
57
+ required: false,
58
+ default: '',
59
+ },
60
+ processApprovalRouterName: {
61
+ type: String,
62
+ required: false,
63
+ default: '',
64
+ }
65
+ },
66
+ data() {
67
+ return {
68
+ fetchListData: getProcessDoneList
69
+ }
70
+ }
71
+ }
72
+ </script>
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <FlowBaseList
3
+ selected-task="pending"
4
+ :header-background=headerBackground
5
+ :fetch-list-data="fetchListData"
6
+ :scan-search=scanSearch
7
+ :business-approval-router-name="businessApprovalRouterName"
8
+ :business-details-router-name="businessDetailsRouterName"
9
+ :process-approval-router-name="processApprovalRouterName"
10
+ :process-details-router-name="processDetailsRouterName">
11
+ <template #nav-bar-left>
12
+ <slot name="nav-bar-left"/>
13
+ </template>
14
+ <template #nav-bar-title>
15
+ {{ title }}
16
+ </template>
17
+ <template #nav-bar-right>
18
+ <slot name="nav-bar-right"/>
19
+ </template>
20
+ </FlowBaseList>
21
+ </template>
22
+
23
+ <script>
24
+ import FlowBaseList from './FlowBaseList.vue';
25
+ import {getProcessTodoList} from "../api";
26
+ import TodoListCard from "./TodoListCard.vue";
27
+
28
+ export default {
29
+ name: 'FlowTodoList',
30
+ components: {
31
+ TodoListCard,
32
+ FlowBaseList
33
+ },
34
+ props: {
35
+ title: {
36
+ type: String,
37
+ required: true
38
+ },
39
+ scanSearch: {
40
+ type: Boolean,
41
+ default: false
42
+ },
43
+ // 新增的背景图属性
44
+ headerBackground: {
45
+ type: String,
46
+ default: ''
47
+ },
48
+ // 新增 4 个路由 name 参数
49
+ businessDetailsRouterName: {
50
+ type: String,
51
+ required: true
52
+ },
53
+ processDetailsRouterName: {
54
+ type: String,
55
+ required: true
56
+ },
57
+ businessApprovalRouterName: {
58
+ type: String,
59
+ required: true
60
+ },
61
+ processApprovalRouterName: {
62
+ type: String,
63
+ required: true
64
+ }
65
+ },
66
+ data() {
67
+ return {
68
+ fetchListData: getProcessTodoList
69
+ }
70
+ }
71
+ }
72
+ </script>