n20-common-lib 2.22.72 → 2.22.73
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.
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<el-checkbox-group v-model="checkArrayCalc" :max="maxLength" class="popover-check-box" @change="checkChange">
|
|
2
|
+
<el-checkbox-group v-model="checkArrayCalc" :disabled="disabled" :max="maxLength" class="popover-check-box" @change="checkChange">
|
|
3
3
|
<vue-draggable
|
|
4
4
|
:list="filterList"
|
|
5
5
|
:animation="200"
|
|
6
|
+
:disabled="disabled"
|
|
6
7
|
group="dragBox"
|
|
7
8
|
:handle="'.n20-icon-tuodong'"
|
|
8
9
|
@change="dragChange"
|
|
@@ -24,6 +25,11 @@ export default {
|
|
|
24
25
|
vueDraggable: () => importGlobal('vuedraggable', () => import(/*webpackChunkName: "vuedraggable"*/ 'vuedraggable'))
|
|
25
26
|
},
|
|
26
27
|
props: {
|
|
28
|
+
// 忙碌时禁用勾选和拖拽,保留列表实例及其当前草稿。
|
|
29
|
+
disabled: {
|
|
30
|
+
type: Boolean,
|
|
31
|
+
default: false
|
|
32
|
+
},
|
|
27
33
|
filterList: {
|
|
28
34
|
type: Array,
|
|
29
35
|
default: () => []
|
|
@@ -26,29 +26,30 @@
|
|
|
26
26
|
v-if="!item.isNotClose"
|
|
27
27
|
:key="getOnlyKey(item)"
|
|
28
28
|
:class="['filter_remove', 'n20-icon-yichu']"
|
|
29
|
-
@click="handleClose(item)"
|
|
29
|
+
@click="!filterBusy && handleClose(item)"
|
|
30
30
|
></i>
|
|
31
31
|
</el-form-item>
|
|
32
32
|
<slot name="suffix"></slot>
|
|
33
33
|
<el-form-item :class="prefixCls + '-add'">
|
|
34
|
-
<el-button v-popover:advanced_popover onlyicon plain icon="el-icon-plus" />
|
|
35
|
-
<el-popover ref="advanced_popover" placement="bottom-start" trigger="click">
|
|
36
|
-
<el-input v-model="keyword" clearable class="input-w m-b-s" :placeholder="$lc('搜索筛选条件')" />
|
|
34
|
+
<el-button v-popover:advanced_popover :loading="filterBusy" :disabled="filterBusy" onlyicon plain icon="el-icon-plus" />
|
|
35
|
+
<el-popover ref="advanced_popover" :disabled="filterBusy" placement="bottom-start" trigger="click">
|
|
36
|
+
<el-input v-model="keyword" :disabled="filterBusy" clearable class="input-w m-b-s" :placeholder="$lc('搜索筛选条件')" />
|
|
37
37
|
<div class="flex-box flex-lr">
|
|
38
|
-
<el-button type="text" @click="allCheck">{{
|
|
38
|
+
<el-button type="text" :disabled="filterBusy" @click="!filterBusy && allCheck()">{{
|
|
39
39
|
checkList.length === filterListS.length ? $lc('取消全选') : $lc('全选')
|
|
40
40
|
}}</el-button>
|
|
41
|
-
<el-button type="text" @click="defaultCheck">{{ $lc('恢复默认') }}</el-button>
|
|
41
|
+
<el-button type="text" :disabled="filterBusy" @click="!filterBusy && defaultCheck()">{{ $lc('恢复默认') }}</el-button>
|
|
42
42
|
</div>
|
|
43
43
|
<filterItem
|
|
44
|
+
:disabled="filterBusy"
|
|
44
45
|
:filter-list="filterListS"
|
|
45
46
|
:max-length="maxLength"
|
|
46
47
|
:check-array.sync="checkList"
|
|
47
48
|
@checked="check"
|
|
48
49
|
/>
|
|
49
50
|
<div class="flex-box flex-c m-t-m">
|
|
50
|
-
<el-button size="mini" type="primary" @click="savaCheck">{{ $lc('确认') }}</el-button>
|
|
51
|
-
<el-button plain type="primary" @click="cancel">{{ $lc('取消') }}</el-button>
|
|
51
|
+
<el-button size="mini" type="primary" :disabled="filterBusy" @click="!filterBusy && savaCheck()">{{ $lc('确认') }}</el-button>
|
|
52
|
+
<el-button plain type="primary" :disabled="filterBusy" @click="!filterBusy && cancel()">{{ $lc('取消') }}</el-button>
|
|
52
53
|
</div>
|
|
53
54
|
</el-popover>
|
|
54
55
|
<el-link class="m-l-s color-primary" :underline="false" icon="n20-icon-query" @click="$emit('search')">
|
|
@@ -123,6 +124,7 @@ export default {
|
|
|
123
124
|
},
|
|
124
125
|
data() {
|
|
125
126
|
return {
|
|
127
|
+
pendingFilterOperations: 0,
|
|
126
128
|
keyword: '',
|
|
127
129
|
prefixCls,
|
|
128
130
|
remoteList: [],
|
|
@@ -133,7 +135,14 @@ export default {
|
|
|
133
135
|
userNo: sessionStorage.getItem('userNo')
|
|
134
136
|
}
|
|
135
137
|
},
|
|
138
|
+
beforeCreate() {
|
|
139
|
+
// Promise 队列和请求令牌无需响应式,避免 Vue 观察异步控制对象。
|
|
140
|
+
this.filterRuntime = { tail: Promise.resolve(), readId: 0, revision: 0, contextId: 0, destroyed: false, selection: null }
|
|
141
|
+
},
|
|
136
142
|
computed: {
|
|
143
|
+
filterBusy() {
|
|
144
|
+
return this.pendingFilterOperations > 0
|
|
145
|
+
},
|
|
137
146
|
filterListS: {
|
|
138
147
|
get() {
|
|
139
148
|
if (!this.keyword) {
|
|
@@ -149,16 +158,22 @@ export default {
|
|
|
149
158
|
}
|
|
150
159
|
},
|
|
151
160
|
watch: {
|
|
161
|
+
filterId: 'invalidateFilterContext',
|
|
162
|
+
userNo: 'invalidateFilterContext',
|
|
152
163
|
visible: {
|
|
153
164
|
handler(val) {
|
|
154
|
-
val && this.
|
|
165
|
+
val && this.loadFilterList()
|
|
155
166
|
}
|
|
156
167
|
}
|
|
157
168
|
},
|
|
158
169
|
created() {
|
|
159
|
-
this.
|
|
170
|
+
this.loadFilterList()
|
|
160
171
|
this.getDefaultCheck()
|
|
161
172
|
},
|
|
173
|
+
beforeDestroy() {
|
|
174
|
+
this.filterRuntime.destroyed = true
|
|
175
|
+
this.invalidateFilterContext()
|
|
176
|
+
},
|
|
162
177
|
methods: {
|
|
163
178
|
getDefaultCheck() {
|
|
164
179
|
let _checkList = this.filterList
|
|
@@ -261,15 +276,15 @@ export default {
|
|
|
261
276
|
break
|
|
262
277
|
}
|
|
263
278
|
}
|
|
264
|
-
|
|
279
|
+
// 连续实例调用从上一笔提交快照删除,避免后一笔把已删除条件带回来。
|
|
280
|
+
const selection = this.filterRuntime.selection
|
|
281
|
+
const source = selection && this.isFilterContextCurrent(selection.context) ? selection.data : this.GroupData
|
|
282
|
+
let data = source.filter((s) => {
|
|
265
283
|
return s.id !== item.id
|
|
266
284
|
})
|
|
267
|
-
this.
|
|
268
|
-
if (code === 200) {
|
|
269
|
-
this.getFilterList()
|
|
270
|
-
}
|
|
271
|
-
})
|
|
285
|
+
const operation = this.queueFilterSave(data)
|
|
272
286
|
this.$emit('clean', this.model)
|
|
287
|
+
return operation
|
|
273
288
|
},
|
|
274
289
|
cleared() {
|
|
275
290
|
this.$emit('clear')
|
|
@@ -330,15 +345,10 @@ export default {
|
|
|
330
345
|
})
|
|
331
346
|
}
|
|
332
347
|
this.setModelData(saveCheckArr)
|
|
333
|
-
this.
|
|
334
|
-
// 如果保存成功
|
|
335
|
-
if (code === 200) {
|
|
336
|
-
// 重新获取过滤条件列表
|
|
337
|
-
this.getFilterList()
|
|
338
|
-
}
|
|
339
|
-
})
|
|
348
|
+
const operation = this.queueFilterSave(saveCheckArr)
|
|
340
349
|
this.$refs.advanced_popover.showPopper = false
|
|
341
350
|
this.$emit('check', saveCheckArr)
|
|
351
|
+
return operation
|
|
342
352
|
},
|
|
343
353
|
check(keys) {
|
|
344
354
|
this.checkList = keys
|
|
@@ -347,83 +357,148 @@ export default {
|
|
|
347
357
|
* 保存筛选条件
|
|
348
358
|
* @param data
|
|
349
359
|
*/
|
|
350
|
-
saveFilter(data) {
|
|
351
|
-
return
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
360
|
+
saveFilter(data, context = this.getFilterContext()) {
|
|
361
|
+
return axios.post(
|
|
362
|
+
`/bems/prod_1.0/user/pageHabit?t=${Date.now()}`,
|
|
363
|
+
{ userNo: context.userNo, pageId: context.pageId, showStructure: JSON.stringify(data) },
|
|
364
|
+
{ loading: false, noMsg: true }
|
|
365
|
+
)
|
|
366
|
+
},
|
|
367
|
+
getFilterContext() {
|
|
368
|
+
return { userNo: this.userNo, pageId: this.filterId, contextId: this.filterRuntime.contextId }
|
|
369
|
+
},
|
|
370
|
+
isFilterContextCurrent(context) {
|
|
371
|
+
return !this.filterRuntime.destroyed && context.userNo === this.userNo &&
|
|
372
|
+
context.pageId === this.filterId && context.contextId === this.filterRuntime.contextId
|
|
373
|
+
},
|
|
374
|
+
invalidateFilterContext() {
|
|
375
|
+
this.filterRuntime.contextId++
|
|
376
|
+
this.filterRuntime.readId++
|
|
377
|
+
this.filterRuntime.selection = null
|
|
378
|
+
},
|
|
379
|
+
finishFilterOperation() {
|
|
380
|
+
if (!this.filterRuntime.destroyed) this.pendingFilterOperations--
|
|
381
|
+
},
|
|
382
|
+
reportFilterError(context, message) {
|
|
383
|
+
if (this.isFilterContextCurrent(context)) this.$message.error(this.$lc(message))
|
|
384
|
+
},
|
|
385
|
+
// 生命周期入口消费异常;公开读取方法仍返回可拒绝的 Promise,供实例调用者处理。
|
|
386
|
+
loadFilterList() {
|
|
387
|
+
const context = this.getFilterContext()
|
|
388
|
+
return this.getFilterList().catch(() => {
|
|
389
|
+
this.reportFilterError(context, '筛选条件加载失败,请重试')
|
|
365
390
|
})
|
|
366
391
|
},
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
392
|
+
queueFilterSave(data) {
|
|
393
|
+
const runtime = this.filterRuntime
|
|
394
|
+
if (runtime.destroyed) return Promise.resolve()
|
|
395
|
+
const context = this.getFilterContext()
|
|
396
|
+
const snapshot = XEUtils.clone(data, true)
|
|
397
|
+
const revision = ++runtime.revision
|
|
398
|
+
runtime.readId++
|
|
399
|
+
runtime.selection = { context, data: snapshot }
|
|
400
|
+
this.pendingFilterOperations++
|
|
401
|
+
// 一笔操作覆盖保存、回读及配置回调;失败不能阻断后续提交。
|
|
402
|
+
const operation = runtime.tail.then(async () => {
|
|
403
|
+
if (runtime.destroyed) return
|
|
404
|
+
let saved = false
|
|
405
|
+
try {
|
|
406
|
+
const result = await this.saveFilter(snapshot, context)
|
|
407
|
+
if (result.code !== 200) throw new Error('Filter save failed')
|
|
408
|
+
saved = true
|
|
409
|
+
await this.readFilterList(context, revision)
|
|
410
|
+
} catch (error) {
|
|
411
|
+
this.reportFilterError(context, saved
|
|
412
|
+
? '筛选条件已保存,但加载失败,请重试'
|
|
413
|
+
: '筛选条件保存失败,请重试')
|
|
414
|
+
}
|
|
415
|
+
}).finally(() => {
|
|
416
|
+
if (runtime.revision === revision) runtime.selection = null
|
|
417
|
+
this.finishFilterOperation()
|
|
418
|
+
})
|
|
419
|
+
runtime.tail = operation.catch(() => {})
|
|
420
|
+
return operation
|
|
421
|
+
},
|
|
422
|
+
/** 获取筛选条件,外部读取等待已排队的提交,避免读到保存前的配置。 */
|
|
370
423
|
async getFilterList() {
|
|
424
|
+
if (this.filterRuntime.destroyed) return
|
|
425
|
+
const context = this.getFilterContext()
|
|
426
|
+
const revision = this.filterRuntime.revision
|
|
427
|
+
this.pendingFilterOperations++
|
|
428
|
+
try {
|
|
429
|
+
await this.filterRuntime.tail
|
|
430
|
+
await this.readFilterList(context, revision)
|
|
431
|
+
} finally {
|
|
432
|
+
this.finishFilterOperation()
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
async readFilterList(context, revision) {
|
|
436
|
+
const runtime = this.filterRuntime
|
|
437
|
+
if (!this.isFilterContextCurrent(context)) return
|
|
438
|
+
const readId = ++runtime.readId
|
|
439
|
+
const isCurrent = () => this.isFilterContextCurrent(context) &&
|
|
440
|
+
readId === runtime.readId && revision === runtime.revision
|
|
371
441
|
try {
|
|
372
442
|
const { data } = await axios.post(
|
|
373
443
|
`/bems/prod_1.0/user/pageHabit/list?t=${Date.now()}`,
|
|
374
|
-
{
|
|
375
|
-
userNo: this.userNo,
|
|
376
|
-
pageId: this.filterId
|
|
377
|
-
},
|
|
444
|
+
{ userNo: context.userNo, pageId: context.pageId },
|
|
378
445
|
{ loading: false, noMsg: true }
|
|
379
446
|
)
|
|
380
|
-
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
if (
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
447
|
+
if (!isCurrent()) return
|
|
448
|
+
const list = JSON.parse(data)
|
|
449
|
+
// null 是历史接口的未配置状态;其他非数组内容不能覆盖当前显示。
|
|
450
|
+
if (list !== null && !Array.isArray(list)) throw new Error('Invalid filter configuration')
|
|
451
|
+
await Promise.resolve(this.beforeFilterList(list))
|
|
452
|
+
if (!isCurrent()) return
|
|
453
|
+
this.applyFilterList(list)
|
|
454
|
+
this.applyFilterOptions()
|
|
455
|
+
} catch (error) {
|
|
456
|
+
// 旧请求失败不干扰当前页面,也不产生过期错误提示。
|
|
457
|
+
if (isCurrent()) throw error
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
applyFilterList(_data) {
|
|
461
|
+
if (_data && _data.length > 0) {
|
|
462
|
+
this.GroupData = _data.map((item) => {
|
|
463
|
+
for (let i = 0; i < this.filterList.length; i++) {
|
|
464
|
+
const originItem = this.filterList[i]
|
|
465
|
+
if (originItem[this.onlyKey] === item[this.onlyKey]) {
|
|
466
|
+
item.label = originItem.label
|
|
467
|
+
if (item.options && originItem.options) {
|
|
468
|
+
item.options = originItem.options
|
|
394
469
|
}
|
|
395
470
|
}
|
|
396
|
-
|
|
471
|
+
}
|
|
472
|
+
return item
|
|
473
|
+
})
|
|
474
|
+
} else {
|
|
475
|
+
if (this.removeLoad) {
|
|
476
|
+
this.GroupData = this.filterList.filter((item) => {
|
|
477
|
+
if (item.isDefault) {
|
|
478
|
+
return item
|
|
479
|
+
}
|
|
397
480
|
})
|
|
398
481
|
} else {
|
|
399
|
-
|
|
400
|
-
this.GroupData = this.filterList.filter((item) => {
|
|
401
|
-
if (item.isDefault) {
|
|
402
|
-
return item
|
|
403
|
-
}
|
|
404
|
-
})
|
|
405
|
-
} else {
|
|
406
|
-
this.GroupData = []
|
|
407
|
-
}
|
|
482
|
+
this.GroupData = []
|
|
408
483
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
484
|
+
}
|
|
485
|
+
this.checkList = this.GroupData.map((res) => res.id)
|
|
486
|
+
this.cancelCheck = XEUtils.clone(this.checkList, true)
|
|
487
|
+
// 查找排序,在this.filterList中查找this.checkList中的每一项,并将其排在前面
|
|
488
|
+
this.filterList.sort((a, b) => {
|
|
489
|
+
if (this.checkList.includes(a[this.onlyKey]) && !this.checkList.includes(b[this.onlyKey])) {
|
|
490
|
+
return -1
|
|
491
|
+
}
|
|
492
|
+
if (!this.checkList.includes(a[this.onlyKey]) && this.checkList.includes(b[this.onlyKey])) {
|
|
493
|
+
return 1
|
|
494
|
+
}
|
|
495
|
+
return 0
|
|
496
|
+
})
|
|
497
|
+
},
|
|
498
|
+
applyFilterOptions() {
|
|
499
|
+
for (const key in getOptionsMap()) {
|
|
500
|
+
if (Object.hasOwnProperty.call(getOptionsMap(), key)) {
|
|
501
|
+
this.setChoices(key, getOptionsMap()[key])
|
|
427
502
|
}
|
|
428
503
|
}
|
|
429
504
|
}
|