n20-common-lib 2.22.71 → 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,6 +1,6 @@
1
1
  {
2
2
  "name": "n20-common-lib",
3
- "version": "2.22.71",
3
+ "version": "2.22.73",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -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.getFilterList()
165
+ val && this.loadFilterList()
155
166
  }
156
167
  }
157
168
  },
158
169
  created() {
159
- this.getFilterList()
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
- let data = this.GroupData.filter((s) => {
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.saveFilter(data).then(({ code }) => {
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.saveFilter(saveCheckArr).then(({ code }) => {
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 new Promise((resolve) => {
352
- axios
353
- .post(
354
- `/bems/prod_1.0/user/pageHabit?t=${Date.now()}`,
355
- {
356
- userNo: this.userNo,
357
- pageId: this.filterId,
358
- showStructure: JSON.stringify(data)
359
- },
360
- { loading: false, noMsg: true }
361
- )
362
- .then((res) => {
363
- resolve(res)
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 _data = JSON.parse(data)
382
-
383
- await Promise.resolve(this.beforeFilterList(_data))
384
-
385
- if (_data && _data.length > 0) {
386
- this.GroupData = _data.map((item) => {
387
- for (let i = 0; i < this.filterList.length; i++) {
388
- const originItem = this.filterList[i]
389
- if (originItem[this.onlyKey] === item[this.onlyKey]) {
390
- item.label = originItem.label
391
- if (item.options && originItem.options) {
392
- item.options = originItem.options
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
- return item
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
- if (this.removeLoad) {
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
- this.checkList = this.GroupData.map((res) => res.id)
410
- this.cancelCheck = XEUtils.clone(this.checkList, true)
411
- // 查找排序,在this.filterList中查找this.checkList中的每一项,并将其排在前面
412
- this.filterList = this.filterList.sort((a, b) => {
413
- if (this.checkList.includes(a[this.onlyKey]) && !this.checkList.includes(b[this.onlyKey])) {
414
- return -1
415
- }
416
- if (!this.checkList.includes(a[this.onlyKey]) && this.checkList.includes(b[this.onlyKey])) {
417
- return 1
418
- }
419
- return 0
420
- })
421
- } finally {
422
- for (const key in getOptionsMap()) {
423
- if (Object.hasOwnProperty.call(getOptionsMap(), key)) {
424
- const data = getOptionsMap()[key]
425
- this.setChoices(key, data)
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
  }
package/src/i18n.json CHANGED
@@ -1086,7 +1086,7 @@
1086
1086
  },
1087
1087
  "业务流水号": {
1088
1088
  "en": "businessId",
1089
- "es": "businessId",
1089
+ "es": "Número de secuencia de negocio",
1090
1090
  "th": "เลขที่ธุรการ",
1091
1091
  "vi": "số hoạch đồng",
1092
1092
  "ja": "ビジネス取引記録"
@@ -7764,7 +7764,7 @@
7764
7764
  },
7765
7765
  "飞天 ePass 3000 GM": {
7766
7766
  "en": "Flying ePass 3000 GM",
7767
- "es": "Flying ePass 3000 GM",
7767
+ "es": "Feitian ePass 3000 GM",
7768
7768
  "th": "ePass 3000 GMบิน",
7769
7769
  "vi": "Bay ePass 3000 GM",
7770
7770
  "ja": "フライングePass 3000 GM"
package/src/utils/auth.js CHANGED
@@ -52,7 +52,7 @@ const auth = {
52
52
  }
53
53
  },
54
54
  setReqLang() {
55
- let langMap = { 'zh-cn': 'zh_CN', en: 'en_US', 'zh-hk': 'zh_TW', th: 'th_TH', vi: 'vi_VI' }
55
+ let langMap = { 'zh-cn': 'zh_CN', en: 'en_US', 'zh-hk': 'zh_TW', th: 'th_TH', vi: 'vi_VN', es: 'es_ES' }
56
56
  let langKey = window.localStorage.getItem('pageLang') || 'zh-cn'
57
57
  reqLang = langMap[langKey]
58
58
  },