@lambo-design-mobile/workflow-approve 1.0.0-beta.2 → 1.0.0-beta.21

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.
@@ -58,10 +58,10 @@
58
58
  icon-size="18px">全选
59
59
  </van-checkbox>
60
60
  </div>
61
- <div class="bar-item" @click="batchApproval(1)">批量驳回</div>
62
- <div class="bar-item approve" @click="batchApproval(0)">批量通过</div>
61
+ <div v-if="showBatchReject" class="bar-item" @click="batchApproval(1)">批量驳回</div>
62
+ <div class="bar-item approve" :style="approveButtonStyle" @click="batchApproval(0)">批量通过</div>
63
63
  </div>
64
- <van-dialog v-model="batchApprovalDialog" :before-close="handleBeforeClose" @confirm="batchApprovalSubmit"
64
+ <van-dialog v-model="batchApprovalDialog" :before-close="handleBeforeClose"
65
65
  :title="this.batchApprovalForm.approvalStatus===0 ?'批量通过':'批量驳回'"
66
66
  show-cancel-button>
67
67
  <van-field ref="auditOpinionField" v-model="batchApprovalForm.auditOpinion"
@@ -70,8 +70,9 @@
70
70
  label="审批意见"
71
71
  type="textarea"
72
72
  maxlength="50"
73
- placeholder="请输入审批意见"
73
+ :placeholder="this.batchApprovalForm.approvalStatus===0 ? '请输入审批意见(选填)' : '请输入审批意见(必填)'"
74
74
  show-word-limit
75
+ :class="{'required-field': this.batchApprovalForm.approvalStatus===1}"
75
76
  />
76
77
  </van-dialog>
77
78
  <van-popup v-model="searchFilterShow" ref="searchPopup" position="top"
@@ -82,9 +83,9 @@
82
83
  <van-field
83
84
  readonly
84
85
  name="calendar"
85
- :value="formatCalendarDate(searchForm.startDate)"
86
+ :value="formatDateRange()"
86
87
  label="发起时间"
87
- placeholder="点击选择发起时间"
88
+ placeholder="点击选择发起时间范围"
88
89
  @click="showCalendar = true"
89
90
  />
90
91
  <van-field
@@ -106,6 +107,7 @@
106
107
  <van-calendar style="height:60%"
107
108
  :min-date="new Date(1900, 0, 1)"
108
109
  :max-date=" new Date()"
110
+ type="range"
109
111
  v-model="showCalendar" @confirm="onDateConfirm" color="#1989fa"/>
110
112
  <van-popup v-model="showPicker" position="bottom">
111
113
  <van-picker
@@ -145,6 +147,11 @@ export default {
145
147
  type: String,
146
148
  default: ''
147
149
  },
150
+ // 控制批量驳回按钮是否显示
151
+ showBatchReject: {
152
+ type: Boolean,
153
+ default: false
154
+ },
148
155
  // 新增 4 个路由 name 参数
149
156
  businessDetailsRouterName: {
150
157
  type: String,
@@ -174,7 +181,8 @@ export default {
174
181
  detailName: '',
175
182
  applyId: '',
176
183
  procName: '',
177
- startDate: '',
184
+ startDateBegin: '',
185
+ startDateEnd: '',
178
186
  procType: '',
179
187
  procTypeName: '',
180
188
  taskName: '',
@@ -202,6 +210,16 @@ export default {
202
210
  return {
203
211
  backgroundImage: this.headerBackground ? `url(${this.headerBackground})` : `url(${this.todoBackImage})`
204
212
  }
213
+ },
214
+ // 根据是否显示批量驳回按钮动态调整批量通过按钮样式
215
+ approveButtonStyle() {
216
+ if (!this.showBatchReject) {
217
+ return {
218
+ 'flex': '2',
219
+ 'margin': '0 20px 0 60px'
220
+ }
221
+ }
222
+ return {}
205
223
  }
206
224
  },
207
225
  methods: {
@@ -262,8 +280,31 @@ export default {
262
280
  const day = String(date.getDate()).padStart(2, '0');
263
281
  return `${year}-${month}-${day}`;
264
282
  },
265
- onDateConfirm(date) {
266
- this.searchForm.startDate = date.toISOString();
283
+ formatDateRange() {
284
+ const { startDateBegin, startDateEnd } = this.searchForm;
285
+ if (!startDateBegin && !startDateEnd) return '';
286
+
287
+ // 将ISO格式转换为显示格式
288
+ const formatDisplayDate = (isoString) => {
289
+ if (!isoString) return '';
290
+ const date = new Date(isoString);
291
+ return this.formatCalendarDate(date);
292
+ };
293
+
294
+ if (startDateBegin && startDateEnd) {
295
+ return `${formatDisplayDate(startDateBegin)} ~ ${formatDisplayDate(startDateEnd)}`;
296
+ }
297
+ return formatDisplayDate(startDateBegin) || formatDisplayDate(startDateEnd);
298
+ },
299
+ onDateConfirm(dates) {
300
+ if (Array.isArray(dates) && dates.length === 2) {
301
+ // 将日期转换为ISO格式以匹配后端期望
302
+ this.searchForm.startDateBegin = dates[0].toISOString();
303
+ this.searchForm.startDateEnd = dates[1].toISOString();
304
+ } else {
305
+ this.searchForm.startDateBegin = '';
306
+ this.searchForm.startDateEnd = '';
307
+ }
267
308
  this.showCalendar = false;
268
309
  },
269
310
  onPickerConfirm(value) {
@@ -282,6 +323,11 @@ export default {
282
323
  Toast.fail(msg);
283
324
  },
284
325
  batchApproval(status) {
326
+ // 如果是驳回操作,但驳回按钮被禁用,则不执行操作
327
+ if (status === 1 && !this.showBatchReject) {
328
+ return;
329
+ }
330
+
285
331
  if (this.checkResult.length > 10) {
286
332
  Toast.fail("最多同时审批十条记录");
287
333
  return;
@@ -300,6 +346,22 @@ export default {
300
346
  this.batchApprovalForm.approvalStatus = status;
301
347
  this.batchApprovalDialog = true;
302
348
  },
349
+ handleBeforeClose(action, done) {
350
+ if (action === 'confirm') {
351
+ // 驳回(status=1)时审批意见必填,通过(status=0)时审批意见可选
352
+ if (this.batchApprovalForm.approvalStatus === 1 && !this.batchApprovalForm.auditOpinion) {
353
+ Toast("驳回操作必须填写审批意见");
354
+ return done(false);
355
+ }
356
+ // 校验通过,关闭对话框,然后提交审批
357
+ done();
358
+ this.batchApprovalSubmit();
359
+ } else {
360
+ // 取消操作,直接关闭对话框
361
+ done();
362
+ this.batchApprovalForm.auditOpinion = '';
363
+ }
364
+ },
303
365
  async batchApprovalSubmit() {
304
366
  const flowIds = this.checkResult.join(",");
305
367
  const status = this.batchApprovalForm.approvalStatus;
@@ -311,23 +373,16 @@ export default {
311
373
  this.checkResult = [];
312
374
  this.onSearch();
313
375
  Toast.success("审批成功");
376
+ // 成功后清空审批意见
377
+ this.batchApprovalForm.auditOpinion = '';
378
+ } else {
379
+ Toast.fail(res.data.message || "审批失败");
314
380
  }
315
381
  } catch (error) {
316
382
  console.error('Error submitting batch approval:', error);
317
383
  Toast.fail("审批失败");
318
384
  }
319
385
  },
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
386
  batchSelect(checked) {
332
387
  if (checked) {
333
388
  this.$refs.todoListCard.checkAll();
@@ -518,4 +573,17 @@ export default {
518
573
  background: linear-gradient(90deg, rgba(0, 150, 255, 0.8), rgba(22, 119, 255, 0.79));
519
574
  }
520
575
 
521
- </style>
576
+ .required-field {
577
+ border-color: red;
578
+ }
579
+
580
+ .required-field ::v-deep .van-field__label {
581
+ color: #f44;
582
+ }
583
+
584
+ .required-field ::v-deep .van-field__label::before {
585
+ content: '* ';
586
+ color: #f44;
587
+ }
588
+
589
+ </style>
@@ -0,0 +1,116 @@
1
+ <template>
2
+ <van-cell center>
3
+ <template v-slot:icon>
4
+ <div
5
+ v-if="['通过','流程发起','跳转指定节点'].includes(getAuditStatus(item.auditResult).text)"
6
+ class="completed-icon">
7
+ <van-icon class="iconfont" class-prefix='icon' :name=getAuditStatus(item.auditResult).icon
8
+ size="28px" color="#fff"/>
9
+ </div>
10
+ <div v-else class="pending-icon">
11
+ <van-icon class="iconfont" class-prefix='icon' :name=getAuditStatus(item.auditResult).icon
12
+ size="28px" color="#fff"/>
13
+ </div>
14
+ </template>
15
+ <template v-slot:title>
16
+ <div :style="{ color: getAuditStatus(item.auditResult).color }"
17
+ style="font-size: 16px;font-weight: bold;padding:5px 0;">
18
+ {{ Array.isArray(item.auditName) ? item.auditName[0] : item.auditName }}
19
+ </div>
20
+ <div>{{ item.taskName }}</div>
21
+ <div>
22
+ <div v-if="foldingApprovalComments">
23
+ <span v-if="item.auditComment" @click="showAuditDetail(item.auditComment)">
24
+ 审批意见 <van-icon name="comment-circle-o"/>
25
+ </span>
26
+ </div>
27
+ <div v-else >
28
+ <div v-if="item.auditComment">
29
+ 意见: {{ item.auditComment }}
30
+ </div>
31
+ </div>
32
+ </div>
33
+ </template>
34
+ <template v-slot:label>
35
+ <div>{{ item.auditDate || item.createTime }}</div>
36
+ </template>
37
+ <template v-slot:default>
38
+ <span :style="{ color: getAuditStatus(item.auditResult).color }">{{
39
+ getAuditStatus(item.auditResult).text
40
+ }}</span>
41
+ </template>
42
+ </van-cell>
43
+ </template>
44
+
45
+ <script>
46
+ import {Dialog} from "vant";
47
+ import {getAuditStatus} from "./js/global";
48
+
49
+ export default {
50
+ name: "FlowNodeCell",
51
+ props: {
52
+ nodeDetail: {
53
+ type: Object,
54
+ required: true
55
+ },
56
+ foldingApprovalComments: {
57
+ type: Boolean,
58
+ default: false
59
+ }
60
+ },
61
+ data() {
62
+ return {
63
+ item: this.nodeDetail
64
+ }
65
+ },
66
+ methods: {
67
+ getAuditStatus,
68
+ showAuditDetail(auditComment) {
69
+ Dialog.alert({
70
+ title: '审批意见',
71
+ message: auditComment,
72
+ }).then(() => {
73
+ // on close
74
+ });
75
+ }
76
+ }
77
+ }
78
+ </script>
79
+
80
+ <style scoped>
81
+ .completed-icon {
82
+ width: 50px;
83
+ height: 50px;
84
+ background-clip: padding-box; /* 使背景不填充边框 */
85
+
86
+ border-radius: 50%;
87
+ margin-right: 1rem;
88
+
89
+ display: flex;
90
+ align-items: center;
91
+ justify-content: center;
92
+
93
+ background: linear-gradient(90deg, #0096FF, #1677FF);
94
+ border: 0.3rem solid rgb(229, 244, 255); /* 半透明边框,颜色和背景色相同 */
95
+ }
96
+
97
+ .pending-icon {
98
+ width: 50px;
99
+ height: 50px;
100
+ background-clip: padding-box; /* 使背景不填充边框 */
101
+
102
+ border-radius: 50%;
103
+ margin-right: 1rem;
104
+
105
+ display: flex;
106
+ align-items: center;
107
+ justify-content: center;
108
+
109
+ background: linear-gradient(90deg, #FF7E00, #FFA200);
110
+ border: 0.4rem solid rgb(255, 245, 229); /* 半透明边框,颜色和背景色相同 */
111
+ }
112
+
113
+ ::v-deep .van-cell__value {
114
+ flex: 0 auto;
115
+ }
116
+ </style>
@@ -2,7 +2,7 @@
2
2
  <div>
3
3
  <div id="headTitle" class="headTitle">
4
4
  <div class="van-nav-bar__content">
5
- <div class="van-nav-bar__title">指定办理人</div>
5
+ <div class="van-nav-bar__title">{{title}}</div>
6
6
  </div>
7
7
  <van-search
8
8
  show-action
@@ -28,8 +28,8 @@
28
28
  :overlay-style="{ position:'absolute',marginTop: '104px'}" transition="none">
29
29
  <div style="padding: 12px">
30
30
  <van-form>
31
- <van-field clearable v-model="searchForm.organId" label="组织ID" readonly />
32
- <van-field clearable v-model="searchForm.organTitle" label="组织名称" readonly />
31
+ <van-field clearable @click="searchOrganShow = true" v-model="searchForm.organId" label="组织ID" readonly />
32
+ <van-field clearable @click="searchOrganShow = true" v-model="searchForm.organTitle" label="组织名称" readonly />
33
33
  <van-field clearable v-model="searchForm.userId" label="用户ID" placeholder="请输入用户ID"/>
34
34
  <van-field name="radio" label="是否管理员">
35
35
  <template #input>
@@ -47,12 +47,12 @@
47
47
  </van-form>
48
48
  </div>
49
49
  </van-popup>
50
- <div id="listContent">
50
+ <div id="listContent" style="background: #f7f8fa">
51
51
  <van-list v-model="loading" style="padding-top: 15px"
52
52
  :finished="finished"
53
53
  finished-text="没有更多了"
54
54
  @load="handleLoad()">
55
- <select-handle-card ref="selectHandleCard" :person-list="personList.rows"
55
+ <select-handle-card ref="selectHandleCard" :multi-select="multiSelect" :person-list="personList.rows"
56
56
  :result.sync="checkResult">
57
57
  </select-handle-card>
58
58
  </van-list>
@@ -61,23 +61,46 @@
61
61
  <div class="bar-item" @click="handleSelect('cancel')">取消</div>
62
62
  <div class="bar-item approve" @click="handleSelect('select')">选择</div>
63
63
  </div>
64
+
65
+ <van-popup v-model="searchOrganShow" closeable round position="bottom" :style="{ height: '80%' }">
66
+ <select-organize :all-organize="true" :show-check-box="false" @handleSelect="onSelect" ></select-organize>
67
+ </van-popup>
64
68
  </div>
65
69
  </template>
66
70
 
67
71
  <script>
68
72
  import SelectHandleCard from "./SelectHandleCard.vue";
69
- import {getOrgRootTree, getUserList} from "../api";
73
+ import {getOrgRootTree, getProcessType, getUserList} from "../api";
74
+ import Tree from "./tree/Tree.vue";
75
+ import SelectOrganize from "./SelectOrganize.vue";
70
76
 
71
77
  export default {
72
78
  name: "SelectHandle",
73
- components: {SelectHandleCard},
79
+ components: {SelectOrganize, Tree, SelectHandleCard},
80
+ props: {
81
+ title: {
82
+ type: String,
83
+ default: ''
84
+ },
85
+ procType: {
86
+ type: String,
87
+ required: true
88
+ },
89
+ multiSelect: { // 新增一个 prop 来控制是否开启多选模式
90
+ type: Boolean,
91
+ default: false
92
+ }
93
+ },
74
94
  data() {
75
95
  return {
96
+ searchOrganShow: false,
76
97
  searchFilterShow: false,
77
98
  searchFilterBadge: false,
78
99
  searchForm: {
79
100
  userId: '',
101
+ orgTreeType: '',
80
102
  userName: '',
103
+ permScopeList: '',
81
104
  organId: '',
82
105
  organTitle: '',
83
106
  directChild: '',
@@ -91,43 +114,65 @@ export default {
91
114
  rows: []
92
115
  },
93
116
  checkResult: [], // 需要传递到子组件的 checkResult
94
- }
117
+ };
118
+ },
119
+ mounted() {
120
+ this.initSearch();
95
121
  },
96
122
  methods: {
97
- handleLoad() {
98
- console.log("触发加载")
123
+ initSearch() {
99
124
  getOrgRootTree().then(res => {
100
125
  const result = res.data;
101
126
  if (result.code === 1) {
102
127
  this.searchForm.organId = result.data[0].organId;
103
128
  this.searchForm.organTitle = result.data[0].organName;
129
+ this.searchForm.userId = '';
130
+ }
131
+ this.handleLoad()
132
+ })
133
+ },
134
+ handleLoad() {
135
+ console.log("触发加载")
136
+ const offset = this.personList.rows.length;
137
+ const limit = 10;
104
138
 
105
- const offset = this.personList.rows.length;
106
- const limit = 10;
107
-
108
- getUserList(offset, limit, this.searchForm).then(res => {
109
-
110
- const result = res.data;
111
- if (result.code === "1") {
139
+ getProcessType(this.procType).then(res => {
112
140
 
113
- //返回的数据添加到 personList
114
- this.personList.rows = this.personList.rows.concat(result.data.rows);
115
- this.personList.total = result.data.total;
141
+ this.searchForm.orgTreeType = res.data.data.rows[0].organTreeType;
142
+ const userArray = this.extractUsers(res.data.data.rows[0].permScope);
143
+ if (userArray.length > 0) {
144
+ this.searchForm.permScopeList = userArray.join(',')
145
+ }
146
+ getUserList(offset, limit, this.searchForm).then(res => {
116
147
 
117
- } else {
118
- console.error('Failed to load data');
119
- }
148
+ const result = res.data;
149
+ if (result.code === "1") {
150
+ //返回的数据添加到 personList 中
151
+ this.personList.rows = this.personList.rows.concat(result.data.rows);
152
+ this.personList.total = result.data.total;
153
+ }
120
154
 
121
- this.loading = false;
122
- this.finished = this.personList.rows.length >= this.personList.total;
155
+ this.loading = false;
156
+ this.finished = this.personList.rows.length >= this.personList.total;
123
157
 
124
- }).catch(error => {
125
- console.error('Error fetching data:', error);
126
- this.loading = false;
127
- });
128
- }
158
+ }).catch(error => {
159
+ console.error('Error fetching data:', error);
160
+ this.loading = false;
161
+ });
162
+ }).catch(error => {
163
+ console.error(error)
129
164
  })
130
165
  },
166
+
167
+ extractUsers(permScope) {
168
+ const match = permScope.match(/U:([^,]+)/);
169
+ if (match && match[1]) {
170
+ return match[1].split(';');
171
+ } else {
172
+ return [];
173
+ }
174
+ },
175
+
131
176
  onSearch() {
132
177
  // 如果搜索弹框是打开的
133
178
  if (this.searchFilterShow) {
@@ -148,15 +193,7 @@ export default {
148
193
  }
149
194
  },
150
195
  resetSearch() {
151
- // 遍历 searchForm 对象的每个属性,将其值置为空
152
- for (let key in this.searchForm) {
153
- // eslint-disable-next-line no-prototype-builtins
154
- if (this.searchForm.hasOwnProperty(key)) {
155
- if (key !== "organId" && key !== "organTitle") {
156
- this.searchForm[key] = '';
157
- }
158
- }
159
- }
196
+ this.initSearch()
160
197
  },
161
198
  resetAndLoadPersonList() {
162
199
  this.personList.total = 0;
@@ -174,6 +211,12 @@ export default {
174
211
  checkSearchForm() {
175
212
  this.searchFilterBadge = Object.values(this.searchForm).some(value => value !== '');
176
213
  },
214
+
215
+ onSelect(v) {
216
+ this.searchForm.organId = v.id;
217
+ this.searchForm.organTitle = v.title;
218
+ this.searchOrganShow = false;
219
+ },
177
220
  },
178
221
  watch: {
179
222
  // 监听 searchForm 的每一个字段的变化
@@ -265,6 +308,7 @@ export default {
265
308
  color: #fff;
266
309
  background: linear-gradient(90deg, #0096FF, #1677FF);
267
310
  border-radius: 9px;
311
+ margin: 0 20px;
268
312
  }
269
313
 
270
314
  .approve:active {
@@ -1,12 +1,12 @@
1
1
  <template>
2
2
  <div>
3
- <van-checkbox-group v-model="localResult" :max="1" ref="checkboxGroup">
3
+ <van-checkbox-group v-model="localResult" :max="multiSelect ? 0 : 1" ref="checkboxGroup">
4
4
  <van-cell-group v-for="(item,index) in personList" :key="index"
5
5
  style="margin-bottom: 10px;" inset>
6
6
  <van-cell value-class="value-status" center size="large"
7
7
  :value="item.userName">
8
8
  <template #icon>
9
- <van-checkbox @click="clearLocalResult(item)" style="padding: 0 5px" :name="item"
9
+ <van-checkbox @click="handleCheckboxClick(item)" style="padding: 0 5px" :name="item"
10
10
  icon-size="18px"></van-checkbox>
11
11
  </template>
12
12
  </van-cell>
@@ -29,8 +29,8 @@
29
29
  </van-checkbox-group>
30
30
  </div>
31
31
  </template>
32
- <script>
33
32
 
33
+ <script>
34
34
  export default {
35
35
  name: 'SelectHandleCard',
36
36
  props: {
@@ -42,6 +42,10 @@ export default {
42
42
  result: {
43
43
  type: Array,
44
44
  default: () => []
45
+ },
46
+ multiSelect: { // 新增一个 prop 来控制是否开启多选模式
47
+ type: Boolean,
48
+ default: false
45
49
  }
46
50
  },
47
51
  data() {
@@ -71,9 +75,19 @@ export default {
71
75
  toggleAll() {
72
76
  this.$refs.checkboxGroup.toggleAll(false);
73
77
  },
78
+ handleCheckboxClick(item) {
79
+ if (this.multiSelect) {
80
+ this.addLocalResult(item); // 如果是多选模式,调用 addLocalResult
81
+ } else {
82
+ this.clearLocalResult(item); // 否则调用 clearLocalResult,保持单选
83
+ }
84
+ },
74
85
  clearLocalResult(item) {
75
86
  this.localResult = [];
76
87
  this.localResult.push(item)
88
+ },
89
+ addLocalResult(item) {
90
+
77
91
  },
78
92
  getStatusText(status) {
79
93
  switch (status) {
@@ -119,8 +133,8 @@ export default {
119
133
  }
120
134
  }
121
135
  </script>
122
- <style scoped>
123
136
 
137
+ <style scoped>
124
138
  /* 自定义 .van-cell 样式 */
125
139
  .custom-cell {
126
140
  padding: 5px 16px;
@@ -131,5 +145,4 @@ export default {
131
145
  font-weight: bold; /* 设置文字加粗 */
132
146
  text-align: right;
133
147
  }
134
-
135
- </style>
148
+ </style>