@lambo-design-mobile/workflow-approve 1.0.0-beta.3 → 1.0.0-beta.30

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.
@@ -45,23 +45,24 @@
45
45
  :finished="finished"
46
46
  finished-text="没有更多了"
47
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>
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
+ @refresh="resetAndLoadList"/>
54
+ </van-list>
55
+ </div>
55
56
  <div v-if="checkResult.length > 0" class="custom-bottom-bar">
56
57
  <div class="bar-item">
57
58
  <van-checkbox v-model="batchSelectChecked" @change="batchSelect" style="justify-content: center;"
58
59
  icon-size="18px">全选
59
60
  </van-checkbox>
60
61
  </div>
61
- <div class="bar-item" @click="batchApproval(1)">批量驳回</div>
62
- <div class="bar-item approve" @click="batchApproval(0)">批量通过</div>
62
+ <div v-if="showBatchReject" class="bar-item" @click="batchApproval(1)">批量驳回</div>
63
+ <div class="bar-item approve" :style="approveButtonStyle" @click="batchApproval(0)">批量通过</div>
63
64
  </div>
64
- <van-dialog v-model="batchApprovalDialog" :before-close="handleBeforeClose" @confirm="batchApprovalSubmit"
65
+ <van-dialog v-model="batchApprovalDialog" :before-close="handleBeforeClose"
65
66
  :title="this.batchApprovalForm.approvalStatus===0 ?'批量通过':'批量驳回'"
66
67
  show-cancel-button>
67
68
  <van-field ref="auditOpinionField" v-model="batchApprovalForm.auditOpinion"
@@ -70,8 +71,9 @@
70
71
  label="审批意见"
71
72
  type="textarea"
72
73
  maxlength="50"
73
- placeholder="请输入审批意见"
74
+ :placeholder="this.batchApprovalForm.approvalStatus===0 ? '请输入审批意见(选填)' : '请输入审批意见(必填)'"
74
75
  show-word-limit
76
+ :class="{'required-field': this.batchApprovalForm.approvalStatus===1}"
75
77
  />
76
78
  </van-dialog>
77
79
  <van-popup v-model="searchFilterShow" ref="searchPopup" position="top"
@@ -82,9 +84,9 @@
82
84
  <van-field
83
85
  readonly
84
86
  name="calendar"
85
- :value="formatCalendarDate(searchForm.startDate)"
87
+ :value="formatDateRange()"
86
88
  label="发起时间"
87
- placeholder="点击选择发起时间"
89
+ placeholder="点击选择发起时间范围"
88
90
  @click="showCalendar = true"
89
91
  />
90
92
  <van-field
@@ -106,6 +108,7 @@
106
108
  <van-calendar style="height:60%"
107
109
  :min-date="new Date(1900, 0, 1)"
108
110
  :max-date=" new Date()"
111
+ type="range"
109
112
  v-model="showCalendar" @confirm="onDateConfirm" color="#1989fa"/>
110
113
  <van-popup v-model="showPicker" position="bottom">
111
114
  <van-picker
@@ -145,6 +148,11 @@ export default {
145
148
  type: String,
146
149
  default: ''
147
150
  },
151
+ // 控制批量驳回按钮是否显示
152
+ showBatchReject: {
153
+ type: Boolean,
154
+ default: false
155
+ },
148
156
  // 新增 4 个路由 name 参数
149
157
  businessDetailsRouterName: {
150
158
  type: String,
@@ -174,7 +182,8 @@ export default {
174
182
  detailName: '',
175
183
  applyId: '',
176
184
  procName: '',
177
- startDate: '',
185
+ startDateBegin: '',
186
+ startDateEnd: '',
178
187
  procType: '',
179
188
  procTypeName: '',
180
189
  taskName: '',
@@ -202,6 +211,16 @@ export default {
202
211
  return {
203
212
  backgroundImage: this.headerBackground ? `url(${this.headerBackground})` : `url(${this.todoBackImage})`
204
213
  }
214
+ },
215
+ // 根据是否显示批量驳回按钮动态调整批量通过按钮样式
216
+ approveButtonStyle() {
217
+ if (!this.showBatchReject) {
218
+ return {
219
+ 'flex': '2',
220
+ 'margin': '0 20px 0 60px'
221
+ }
222
+ }
223
+ return {}
205
224
  }
206
225
  },
207
226
  methods: {
@@ -262,8 +281,31 @@ export default {
262
281
  const day = String(date.getDate()).padStart(2, '0');
263
282
  return `${year}-${month}-${day}`;
264
283
  },
265
- onDateConfirm(date) {
266
- this.searchForm.startDate = date.toISOString();
284
+ formatDateRange() {
285
+ const { startDateBegin, startDateEnd } = this.searchForm;
286
+ if (!startDateBegin && !startDateEnd) return '';
287
+
288
+ // 将ISO格式转换为显示格式
289
+ const formatDisplayDate = (isoString) => {
290
+ if (!isoString) return '';
291
+ const date = new Date(isoString);
292
+ return this.formatCalendarDate(date);
293
+ };
294
+
295
+ if (startDateBegin && startDateEnd) {
296
+ return `${formatDisplayDate(startDateBegin)} ~ ${formatDisplayDate(startDateEnd)}`;
297
+ }
298
+ return formatDisplayDate(startDateBegin) || formatDisplayDate(startDateEnd);
299
+ },
300
+ onDateConfirm(dates) {
301
+ if (Array.isArray(dates) && dates.length === 2) {
302
+ // 将日期转换为ISO格式以匹配后端期望
303
+ this.searchForm.startDateBegin = dates[0].toISOString();
304
+ this.searchForm.startDateEnd = dates[1].toISOString();
305
+ } else {
306
+ this.searchForm.startDateBegin = '';
307
+ this.searchForm.startDateEnd = '';
308
+ }
267
309
  this.showCalendar = false;
268
310
  },
269
311
  onPickerConfirm(value) {
@@ -282,6 +324,11 @@ export default {
282
324
  Toast.fail(msg);
283
325
  },
284
326
  batchApproval(status) {
327
+ // 如果是驳回操作,但驳回按钮被禁用,则不执行操作
328
+ if (status === 1 && !this.showBatchReject) {
329
+ return;
330
+ }
331
+
285
332
  if (this.checkResult.length > 10) {
286
333
  Toast.fail("最多同时审批十条记录");
287
334
  return;
@@ -300,6 +347,22 @@ export default {
300
347
  this.batchApprovalForm.approvalStatus = status;
301
348
  this.batchApprovalDialog = true;
302
349
  },
350
+ handleBeforeClose(action, done) {
351
+ if (action === 'confirm') {
352
+ // 驳回(status=1)时审批意见必填,通过(status=0)时审批意见可选
353
+ if (this.batchApprovalForm.approvalStatus === 1 && !this.batchApprovalForm.auditOpinion) {
354
+ Toast("驳回操作必须填写审批意见");
355
+ return done(false);
356
+ }
357
+ // 校验通过,关闭对话框,然后提交审批
358
+ done();
359
+ this.batchApprovalSubmit();
360
+ } else {
361
+ // 取消操作,直接关闭对话框
362
+ done();
363
+ this.batchApprovalForm.auditOpinion = '';
364
+ }
365
+ },
303
366
  async batchApprovalSubmit() {
304
367
  const flowIds = this.checkResult.join(",");
305
368
  const status = this.batchApprovalForm.approvalStatus;
@@ -311,23 +374,16 @@ export default {
311
374
  this.checkResult = [];
312
375
  this.onSearch();
313
376
  Toast.success("审批成功");
377
+ // 成功后清空审批意见
378
+ this.batchApprovalForm.auditOpinion = '';
379
+ } else {
380
+ Toast.fail(res.data.message || "审批失败");
314
381
  }
315
382
  } catch (error) {
316
383
  console.error('Error submitting batch approval:', error);
317
384
  Toast.fail("审批失败");
318
385
  }
319
386
  },
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
387
  batchSelect(checked) {
332
388
  if (checked) {
333
389
  this.$refs.todoListCard.checkAll();
@@ -518,4 +574,17 @@ export default {
518
574
  background: linear-gradient(90deg, rgba(0, 150, 255, 0.8), rgba(22, 119, 255, 0.79));
519
575
  }
520
576
 
521
- </style>
577
+ .required-field {
578
+ border-color: red;
579
+ }
580
+
581
+ .required-field ::v-deep .van-field__label {
582
+ color: #f44;
583
+ }
584
+
585
+ .required-field ::v-deep .van-field__label::before {
586
+ content: '* ';
587
+ color: #f44;
588
+ }
589
+
590
+ </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, item.handleName, item.rejectName).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, item.handleName, item.rejectName).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>
@@ -16,7 +16,7 @@
16
16
  <template #right-icon>
17
17
  <div @click="onSearch" class="todo-search">搜索</div>
18
18
  </template>
19
- <template #action>
19
+ <template v-if="!useUserTransferRange" #action>
20
20
  <van-badge :dot="searchFilterBadge">
21
21
  <van-icon @click="searchFilterShow = !searchFilterShow;" name="filter-o" color="#666666" size="25px"/>
22
22
  </van-badge>
@@ -24,7 +24,7 @@
24
24
  </van-search>
25
25
  </div>
26
26
  <van-popup v-model="searchFilterShow" ref="searchPopup" position="top"
27
- get-container="#headTitle" style="position: absolute;margin-top: 104px"
27
+ style="position: absolute;margin-top: 104px"
28
28
  :overlay-style="{ position:'absolute',marginTop: '104px'}" transition="none">
29
29
  <div style="padding: 12px">
30
30
  <van-form>
@@ -51,9 +51,10 @@
51
51
  <van-list v-model="loading" style="padding-top: 15px"
52
52
  :finished="finished"
53
53
  finished-text="没有更多了"
54
+ :immediate-check="false"
54
55
  @load="handleLoad()">
55
56
  <select-handle-card ref="selectHandleCard" :multi-select="multiSelect" :person-list="personList.rows"
56
- :result.sync="checkResult">
57
+ :result.sync="checkResult" :show-status="!useUserTransferRange">
57
58
  </select-handle-card>
58
59
  </van-list>
59
60
  </div>
@@ -63,14 +64,14 @@
63
64
  </div>
64
65
 
65
66
  <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
+ <select-organize :all-organize="true" :treetype-id="orgTreeType" :org-root-with-permit="orgRootWithPermit" :organize-id-list="organizeIdList" :show-check-box="false" @handleSelect="onSelect" ></select-organize>
67
68
  </van-popup>
68
69
  </div>
69
70
  </template>
70
71
 
71
72
  <script>
72
73
  import SelectHandleCard from "./SelectHandleCard.vue";
73
- import {getOrgRootTree, getProcessType, getUserList} from "../api";
74
+ import {getOrgRootTree, getProcessType, getUserList, getTransferRange, getBizOrgRootTree} from "../api";
74
75
  import Tree from "./tree/Tree.vue";
75
76
  import SelectOrganize from "./SelectOrganize.vue";
76
77
 
@@ -86,9 +87,26 @@ export default {
86
87
  type: String,
87
88
  required: true
88
89
  },
90
+ // 根组织获取规则,为true表示用登录人权限范围
91
+ orgRootWithPermit: {
92
+ type: Boolean,
93
+ default: true
94
+ },
89
95
  multiSelect: { // 新增一个 prop 来控制是否开启多选模式
90
96
  type: Boolean,
91
97
  default: false
98
+ },
99
+ organTreeType: {
100
+ type: String,
101
+ required: false
102
+ },
103
+ userList: { // 用户选择范围
104
+ type: Array,
105
+ default: () => ([])
106
+ },
107
+ orgList: { // 组织选择范围
108
+ type: Array,
109
+ default: () => ([])
92
110
  }
93
111
  },
94
112
  data() {
@@ -96,8 +114,12 @@ export default {
96
114
  searchOrganShow: false,
97
115
  searchFilterShow: false,
98
116
  searchFilterBadge: false,
117
+ useUserTransferRange: false,
118
+ userRangeList: [],
119
+ orgTreeType: '',
99
120
  searchForm: {
100
121
  userId: '',
122
+ orgTreeType: '',
101
123
  userName: '',
102
124
  permScopeList: '',
103
125
  organId: '',
@@ -113,6 +135,7 @@ export default {
113
135
  rows: []
114
136
  },
115
137
  checkResult: [], // 需要传递到子组件的 checkResult
138
+ organizeIdList: [], // 需要传递到子组件的组织选择范围
116
139
  };
117
140
  },
118
141
  mounted() {
@@ -120,46 +143,108 @@ export default {
120
143
  },
121
144
  methods: {
122
145
  initSearch() {
123
- getOrgRootTree().then(res => {
124
- const result = res.data;
125
- if (result.code === 1) {
126
- this.searchForm.organId = result.data[0].organId;
127
- this.searchForm.organTitle = result.data[0].organName;
146
+ if (this.userList && this.userList.length > 0) {
147
+ this.useUserTransferRange = true
148
+ this.userRangeList = this.userList.map(item => { return{...item, organName: item.organName || item.deptName} })
149
+ this.personList.rows = this.userRangeList
150
+ this.personList.total = this.userRangeList.length
151
+ this.finished = true;
152
+ return
153
+ }
154
+ this.useUserTransferRange = false
155
+ this.personList.total = 0;
156
+ this.personList.rows = [];
157
+ this.finished = false;
158
+ this.loading = false;
159
+ if (this.orgList && this.orgList.length > 0){
160
+ this.organizeIdList = this.orgList.map(item => item.orgId)
161
+ this.orgTreeType = this.organTreeType;
162
+ this.searchForm = {
163
+ ...this.searchForm,
164
+ orgTreeType: this.organTreeType,
165
+ organId: this.orgList[0].orgId,
166
+ organTitle: this.orgList[0].orgName,
167
+ userId: ''
128
168
  }
129
169
  this.handleLoad()
130
- })
170
+ } else {
171
+ getProcessType(this.procType).then(res => {
172
+ this.orgTreeType = res.data.data.rows[0].organTreeType;
173
+ const permScope = res.data.data.rows[0].permScope || 'all'
174
+ if (this.orgRootWithPermit){
175
+ getOrgRootTree(this.orgTreeType).then(res => {
176
+ const orgResult = res.data;
177
+ if (orgResult.code === 200) {
178
+ this.searchForm = {
179
+ ...this.searchForm,
180
+ orgTreeType: this.orgTreeType,
181
+ permScopeList: permScope,
182
+ organId: orgResult.data[0].organId,
183
+ organTitle: orgResult.data[0].organName,
184
+ userId: ''
185
+ }
186
+ }
187
+ this.handleLoad()
188
+ })
189
+ } else {
190
+ getBizOrgRootTree(this.orgTreeType).then(res => {
191
+ const orgResult = res.data;
192
+ if (orgResult.code === 200) {
193
+ this.searchForm = {
194
+ ...this.searchForm,
195
+ orgTreeType: this.orgTreeType,
196
+ permScopeList: permScope,
197
+ organId: orgResult.data[0].bizOrganId,
198
+ organTitle: orgResult.data[0].bizOrganName,
199
+ userId: ''
200
+ }
201
+ }
202
+ this.handleLoad()
203
+ })
204
+ }
205
+
206
+ }).catch(error => {
207
+ console.error(error)
208
+ })
209
+ }
131
210
  },
132
211
  handleLoad() {
212
+ if (this.finished) {
213
+ return
214
+ }
133
215
  console.log("触发加载")
216
+ this.loading = true;
217
+ if (this.useUserTransferRange){
218
+ this.personList.rows = this.searchForm.userName ? this.userRangeList.filter(item => item.userName === this.searchForm.userName) : this.userRangeList
219
+ this.personList.total = this.personList.rows.length;
220
+ this.finished = true;
221
+ this.loading = false;
222
+ return
223
+ }
134
224
  const offset = this.personList.rows.length;
135
225
  const limit = 10;
136
226
 
137
- getProcessType(this.procType).then(res => {
138
-
139
- const userArray = this.extractUsers(res.data.data.rows[0].permScope);
140
- console.log(userArray)
141
- if (userArray.length > 0) {
142
- this.searchForm.permScopeList = userArray.join(',')
143
- }
144
- getUserList(offset, limit, this.searchForm).then(res => {
145
-
146
- const result = res.data;
147
- if (result.code === "1") {
148
- //返回的数据添加到 personList 中
149
- this.personList.rows = this.personList.rows.concat(result.data.rows);
150
- this.personList.total = result.data.total;
227
+ getUserList(offset, limit, this.searchForm).then(res => {
228
+ const result = res.data;
229
+ if (result.code === "1") {
230
+ //返回的数据添加到 personList 中
231
+ const newRows = Array.isArray(result.data.rows) ? result.data.rows : [];
232
+ this.personList.rows = this.personList.rows.concat(newRows);
233
+ const total = Number(result.data.total);
234
+ if (!Number.isNaN(total)) {
235
+ this.personList.total = total;
151
236
  }
237
+ const noMoreByPage = newRows.length < limit;
238
+ const noMoreByTotal = this.personList.total > 0 && this.personList.rows.length >= this.personList.total;
239
+ this.finished = noMoreByPage || noMoreByTotal;
240
+ }
152
241
 
153
- this.loading = false;
154
- this.finished = this.personList.rows.length >= this.personList.total;
242
+ this.loading = false;
155
243
 
156
- }).catch(error => {
157
- console.error('Error fetching data:', error);
158
- this.loading = false;
159
- });
160
244
  }).catch(error => {
161
- console.error(error)
162
- })
245
+ console.error('Error fetching data:', error);
246
+ this.loading = false;
247
+ });
163
248
  },
164
249
 
165
250
  extractUsers(permScope) {
@@ -196,6 +281,8 @@ export default {
196
281
  resetAndLoadPersonList() {
197
282
  this.personList.total = 0;
198
283
  this.personList.rows = [];
284
+ this.finished = false;
285
+ this.loading = false;
199
286
  this.handleLoad();
200
287
  },
201
288
  handleSelect(handle) {
@@ -218,10 +305,10 @@ export default {
218
305
  },
219
306
  watch: {
220
307
  // 监听 searchForm 的每一个字段的变化
221
- searchForm: {
222
- handler: 'checkSearchForm',
223
- deep: true // 深度监听
224
- }
308
+ // searchForm: {
309
+ // handler: 'checkSearchForm',
310
+ // deep: true // 深度监听
311
+ // }
225
312
  }
226
313
  }
227
314
  </script>
@@ -347,4 +434,4 @@ export default {
347
434
  .submitForm:active {
348
435
  background: linear-gradient(90deg, rgba(0, 150, 255, 0.8), rgba(22, 119, 255, 0.79));
349
436
  }
350
- </style>
437
+ </style>
@@ -17,7 +17,7 @@
17
17
  :border="false" title="部门名称"
18
18
  :value="item.organName"></van-cell>
19
19
  <van-cell title-class="list-title" value-class="list-value" class="custom-cell"
20
- :border="false" title="人员状态">
20
+ :border="false" title="人员状态" v-if="showStatus">
21
21
  <template v-slot:default>
22
22
  <span :style="getStatusStyle(item.status)"></span>
23
23
  <span :style="{ color: getStatusStyle(item.status)['background-color'] }">
@@ -46,6 +46,10 @@ export default {
46
46
  multiSelect: { // 新增一个 prop 来控制是否开启多选模式
47
47
  type: Boolean,
48
48
  default: false
49
+ },
50
+ showStatus: {
51
+ type: Boolean,
52
+ default: false
49
53
  }
50
54
  },
51
55
  data() {