n20-common-lib 3.1.6 → 3.1.8

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": "3.1.6",
3
+ "version": "3.1.8",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -50,7 +50,9 @@
50
50
  @import './page.scss';
51
51
  @import './view-toggle.scss';
52
52
  @import './pivot.scss';
53
+ @import './dynamic-table.scss';
53
54
  @import './v3/table.scss';
54
55
  @import './v3/secondary-tab.scss';
56
+
55
57
  //临时引入
56
58
  @import '../../components/ChildRange/style.scss';
@@ -0,0 +1,6 @@
1
+ .dynamic-table-pro {
2
+ .el-form-item,
3
+ .el-form-item--small.el-form-item {
4
+ margin-bottom: 0;
5
+ }
6
+ }
@@ -1,13 +1,8 @@
1
1
  .n20-page-header {
2
2
  display: flex;
3
3
  line-height: 20px;
4
- padding: 12px 16px;
5
-
4
+ padding-bottom: 2px;
6
5
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
7
- &__wappr {
8
- display: flex;
9
- flex-wrap: wrap;
10
- }
11
6
 
12
7
  .page-header__left {
13
8
  display: flex;
@@ -37,23 +32,14 @@
37
32
  }
38
33
 
39
34
  .page-header__title {
40
- color: var(--text-2, #4e5969);
41
- /* 14/CN-Regular */
42
- font-family: 'PingFang SC';
43
35
  font-size: 14px;
44
- font-style: normal;
45
- font-weight: 400;
46
- line-height: 22px; /* 157.143% */
36
+ line-height: 20px;
37
+ color: #3d4a57;
47
38
  }
48
39
 
49
40
  .page-header__content {
50
- color: var(--text-1, #1d2129);
51
-
52
- /* 16/CN-Medium */
53
- font-family: 'PingFang SC';
54
- font-size: 16px;
55
- font-style: normal;
56
- font-weight: 500;
57
- line-height: 24px; /* 150% */
41
+ font-size: 14px;
42
+ line-height: 20px;
43
+ color: #3d4a57;
58
44
  }
59
45
  }
@@ -8,7 +8,7 @@ import inputNumber from '../InputNumber/index.vue'
8
8
  import InputNumberRange from '../InputNumber/numberRange.vue'
9
9
  import selectTree from '../SelectTree/index.vue'
10
10
  import selectTreePro from '../SelectTree/pro.vue'
11
- import { setOptionsMap } from './utils.js'
11
+
12
12
  const canvas = document.createElement('canvas')
13
13
  const context = canvas.getContext('2d')
14
14
  context.font = '14px Ping Fang SC'
@@ -83,7 +83,6 @@ export default {
83
83
  resKeys.forEach((key) => {
84
84
  list = list[key]
85
85
  })
86
- setOptionsMap(this.item.id, list)
87
86
  this.$set(this.item, 'options', list)
88
87
  // 同步更新 filterList 中的 options,确保两者一致,
89
88
  // 防止 AdvancedFilter 的 filterList watcher 用旧数据覆盖 reqOptions 结果
@@ -5,7 +5,7 @@
5
5
  <el-form-item
6
6
  v-for="item in GroupData"
7
7
  :key="getOnlyKey(item)"
8
- :class="[prefixCls + '-item', activeClass(item)]"
8
+ :class="[prefixCls + '-item', activeClassMap[item.value || item[onlyKey]] || '']"
9
9
  :label="item.label"
10
10
  :required="!!item.required"
11
11
  :disabled="item.props && item.props.disabled"
@@ -107,17 +107,17 @@
107
107
  </template>
108
108
 
109
109
  <script>
110
- // import formItemInput from './form-item-input.vue'
111
110
  import XEUtils from 'xe-utils'
112
111
 
113
112
  import filterItem from './filterItem.vue'
114
113
  import formItemRender from './formItemRender.vue'
115
- import { getOnlyKey } from './utils'
116
114
 
117
115
  import axios from '../../utils/axios.js'
118
116
  import ClDialog from '../Dialog/index.vue'
119
117
  import InputSearch from '../InputSearch/index.vue'
120
118
 
119
+ const getOnlyKeys = (key, data) => (key ? data[key] : Date.now().toString(36) + Math.random().toString(36).slice(2, 7))
120
+
121
121
  const prefixCls = 'n20-advanced-filter'
122
122
  export default {
123
123
  name: 'AdvancedFilter',
@@ -136,14 +136,6 @@ export default {
136
136
  }
137
137
  },
138
138
  props: {
139
- hasSaveView: {
140
- type: Boolean,
141
- default: false
142
- },
143
- isNotClose: {
144
- type: Boolean,
145
- default: false
146
- },
147
139
  maxLength: {
148
140
  type: Number
149
141
  },
@@ -221,6 +213,48 @@ export default {
221
213
 
222
214
  const nonFixedItemIds = nonFixedItems.map((item) => item[this.onlyKey])
223
215
  return nonFixedItemIds.every((id) => this.checkList.includes(id))
216
+ },
217
+ // 通过深度遍历 model 所有属性触发 getter,建立完整响应式依赖
218
+ // 同时收集对象级 __ob__.dep,确保 $set 新增属性时也能触发重算
219
+ _modelValues() {
220
+ const model = this.model
221
+ // 遍历所有自有属性触发 getter(收集属性级 dep)
222
+ // Object.keys 在 Vue 2 内部会触发对象级 dep 收集
223
+ const keys = Object.keys(model)
224
+ const values = []
225
+ for (let i = 0; i < keys.length; i++) {
226
+ values.push(model[keys[i]])
227
+ }
228
+ return values
229
+ },
230
+ activeClassMap() {
231
+ // 访问 _modelValues 建立深度依赖,确保 model 任何值变化都能触发重算
232
+ void this._modelValues
233
+
234
+ const model = this.model
235
+ const hasValue = (val) => {
236
+ if (Array.isArray(val)) return val.length > 0
237
+ return val !== null && val !== undefined && val !== ''
238
+ }
239
+ const hasRange = (start, end) => hasValue(model[start]) && hasValue(model[end])
240
+ const rangeTypes = ['daterange', 'datetimerange', 'monthrange']
241
+
242
+ const map = {}
243
+ this.GroupData.forEach((item) => {
244
+ const key = item.value || item[this.onlyKey]
245
+ let isActive = false
246
+ if (item.type === 'numberrange') {
247
+ isActive = hasRange(item.startValue, item.endValue)
248
+ } else if (rangeTypes.includes(item.type)) {
249
+ isActive = hasRange(item.startDate, item.endDate)
250
+ } else if (item.slotFields && item.slotFields.length > 0) {
251
+ isActive = item.slotFields.some((field) => hasValue(model[field]))
252
+ } else {
253
+ isActive = hasValue(model[item.value])
254
+ }
255
+ map[key] = isActive ? this.prefixCls + '-active' : ''
256
+ })
257
+ return map
224
258
  }
225
259
  },
226
260
  watch: {
@@ -298,7 +332,7 @@ export default {
298
332
  this.defineCheckList = XEUtils.clone(_checkList, true)
299
333
  },
300
334
  getOnlyKey(data) {
301
- return getOnlyKey(this.onlyKey, data)
335
+ return getOnlyKeys(this.onlyKey, data)
302
336
  },
303
337
  setOptions(id, opts) {
304
338
  this.optionsMap[id] = opts
@@ -318,29 +352,7 @@ export default {
318
352
  filter() {
319
353
  return this.model
320
354
  },
321
- activeClass(item) {
322
- // 判断值是否有效(非空)
323
- const hasValue = (val) => {
324
- if (Array.isArray(val)) return val.length > 0
325
- return val !== null && val !== undefined && val !== ''
326
- }
327
355
 
328
- // 判断范围类型是否有效(起止值都存在)
329
- const hasRange = (start, end) => hasValue(this.model[start]) && hasValue(this.model[end])
330
-
331
- const rangeTypes = ['daterange', 'datetimerange', 'monthrange']
332
-
333
- if (item.type === 'numberrange') {
334
- return hasRange(item.startValue, item.endValue) ? this.prefixCls + '-active' : ''
335
- }
336
- if (rangeTypes.includes(item.type)) {
337
- return hasRange(item.startDate, item.endDate) ? this.prefixCls + '-active' : ''
338
- }
339
- if (item.slotFields && item.slotFields.length > 0) {
340
- return item.slotFields.some((field) => hasValue(this.model[field])) ? this.prefixCls + '-active' : ''
341
- }
342
- return hasValue(this.model[item.value]) ? this.prefixCls + '-active' : ''
343
- },
344
356
  handleClose(item) {
345
357
  const key = item[this.onlyKey] || item.value
346
358
  if (this.optionsMap[key]) {
@@ -437,7 +449,7 @@ export default {
437
449
  if (Object.hasOwnProperty.call(this.model, key)) {
438
450
  // 仅当该字段在 filterList 中定义且未被选中时才清除
439
451
  if (allDefinedFields.has(key) && !selectedFields.has(key)) {
440
- delete this.model[key]
452
+ this.$delete(this.model, key)
441
453
  }
442
454
  }
443
455
  }
@@ -1,24 +1,20 @@
1
1
  <template>
2
2
  <div class="n20-page-header">
3
- <div class="n20-page-header__wappr">
4
- <div v-if="!disable" class="page-header__left" @click="$emit('back')">
5
- <i :class="icon"></i>
6
- <div class="page-header__title">
7
- <slot name="title">{{ title }}</slot>
8
- </div>
9
- </div>
10
- <div class="page-header__content">
11
- <slot name="content">{{ content }}</slot>
3
+ <div v-if="!disable" class="page-header__left" @click="$emit('back')">
4
+ <i :class="icon"></i>
5
+ <div class="page-header__title">
6
+ <slot name="title">{{ title }}</slot>
12
7
  </div>
13
8
  </div>
14
- <div>
15
- <slot></slot>
9
+ <div class="page-header__content">
10
+ <slot name="content">{{ content }}</slot>
16
11
  </div>
17
12
  </div>
18
13
  </template>
19
14
 
20
15
  <script>
21
16
  import { $lc } from '../../utils/i18n/index'
17
+
22
18
  export default {
23
19
  name: 'PageHeader',
24
20
  props: {
@@ -418,48 +418,49 @@ export default {
418
418
  this.$emit('filter', this.filterObj, 'clean')
419
419
  },
420
420
  handleClear() {
421
- // 收集 required 项的当前值,清空时保留
421
+ // 收集不需要清空的字段 key(required / isNotClose / initialValue 中的额外 key)
422
+ const noClearKeys = new Set()
423
+ const allFilterKeys = new Set()
424
+
425
+ this.filterList.forEach((item) => {
426
+ const keys =
427
+ item.type === 'daterange'
428
+ ? [item.startDate, item.endDate]
429
+ : item.type === 'numberrange'
430
+ ? [item.startValue, item.endValue]
431
+ : item.slotFields?.length
432
+ ? item.slotFields
433
+ : [item.value]
434
+
435
+ keys.forEach((k) => allFilterKeys.add(k))
436
+ if (item.required) {
437
+ keys.forEach((k) => noClearKeys.add(k))
438
+ }
439
+ })
440
+
441
+ // initialValue 中不在 filterList 里的 key 也需要保留
442
+ Object.keys(this.initialValue).forEach((key) => {
443
+ if (!allFilterKeys.has(key)) {
444
+ noClearKeys.add(key)
445
+ }
446
+ })
447
+
448
+ // 仅保留 noClearKeys 中的值,其余清空
422
449
  const preserved = {}
423
- this.filterList
424
- .filter((item) => item.required)
425
- .forEach((item) => {
426
- // 日期范围类型
427
- if (item.startDate && this.searchValue[item.startDate] !== undefined) {
428
- preserved[item.startDate] = this.searchValue[item.startDate]
429
- }
430
- if (item.endDate && this.searchValue[item.endDate] !== undefined) {
431
- preserved[item.endDate] = this.searchValue[item.endDate]
432
- }
433
- // 数字范围类型
434
- if (item.startValue && this.searchValue[item.startValue] !== undefined) {
435
- preserved[item.startValue] = this.searchValue[item.startValue]
436
- }
437
- if (item.endValue && this.searchValue[item.endValue] !== undefined) {
438
- preserved[item.endValue] = this.searchValue[item.endValue]
439
- }
440
- // 普通单值
441
- if (item.value && this.searchValue[item.value] !== undefined) {
442
- preserved[item.value] = this.searchValue[item.value]
443
- }
444
- // slot 多值绑定
445
- if (item.slotFields?.length) {
446
- item.slotFields.forEach((field) => {
447
- if (this.searchValue[field] !== undefined) {
448
- preserved[field] = this.searchValue[field]
449
- }
450
- })
451
- }
452
- })
450
+ for (const key in this.searchValue) {
451
+ if (noClearKeys.has(key)) {
452
+ preserved[key] = this.searchValue[key]
453
+ }
454
+ }
453
455
 
454
- this.searchValue = { ...this.initialValue, ...this.getInitialSearchValue, ...preserved }
455
- // 直接构建 payload,绕过 filterObj 避免 initialValue 重新注入
456
+ this.searchValue = preserved
456
457
  this.$emit(
457
458
  'filter',
458
459
  {
459
460
  conditionGroups: this.conditionGroups,
460
461
  searchValue: { ...this.searchValue },
461
- viewId: this.selectItem ? this.selectItem.viewId : null,
462
- viewType: this.selectItem ? this.selectItem.viewType : null
462
+ viewId: this.selectItem?.viewId ?? null,
463
+ viewType: this.selectItem?.viewType ?? null
463
464
  },
464
465
  'clear'
465
466
  )