n20-common-lib 3.3.5 → 3.3.7

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.3.5",
3
+ "version": "3.3.7",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -808,7 +808,7 @@ export default {
808
808
  },
809
809
  labelPosition: {
810
810
  type: String,
811
- default: 'top'
811
+ default: 'right'
812
812
  },
813
813
  disabled: {
814
814
  type: Boolean,
@@ -1,150 +1,150 @@
1
- import { $lc } from '../../utils/i18n/index.js'
2
- const obj = {
3
- mounted() {
4
- this.setRules()
5
- },
6
- data() {
7
- return {
8
- // batch 批处理内部标志:withBatch 包裹期间 _batchDepth>0,
9
- // 期间 setType 的 requiredFlag 变更只标记 dirty,不立即 setRules,由批次末尾合并触发一次
10
- _batchDepth: 0,
11
- _batchRulesDirty: false
12
- }
13
- },
14
- methods: {
15
- // 批处理包裹器:可重入。一次用户动作(如 valueChange/setView)内连续多次 setType,
16
- // 合并成末尾一次 setRules,避免 O(全表) rules 重建风暴。批次外(含异步逸出的 setType)保持原即时语义。
17
- withBatch(fn) {
18
- this._batchDepth++
19
- try {
20
- return fn()
21
- } finally {
22
- this._batchDepth--
23
- if (this._batchDepth === 0 && this._batchRulesDirty) {
24
- this._batchRulesDirty = false
25
- this.setRules()
26
- }
27
- }
28
- },
29
- // 对相关方法进行设置
30
- setType(name, value) {
31
- this.form.forEach((item) => {
32
- for (const key in item.list) {
33
- if (item.list && item.list[key].type === 'group-container') {
34
- const index = item.list[key].fields.findIndex((id) => {
35
- return id.fieldCode === name
36
- })
37
- if (index !== -1) {
38
- item.list[key].fields[index] = { ...item.list[key].fields[index], ...value }
39
- return false
40
- }
41
- }
42
- }
43
- if (item.list && item.list[name]) {
44
- item.list[name] = { ...item.list[name], ...value }
45
- }
46
- })
47
- // 更新rules:批次内只标记 dirty,批次外立即触发
48
- if (value.hasOwnProperty('requiredFlag')) {
49
- if (this._batchDepth > 0) {
50
- this._batchRulesDirty = true
51
- } else {
52
- this.setRules()
53
- }
54
- }
55
- },
56
- // 生成校验rules
57
- setRules() {
58
- const rules = {}
59
- this.form.forEach((item) => {
60
- let msg = ''
61
- for (const key in item.list) {
62
- if (item.list && item.list[key].type === 'group-container') {
63
- item.list[key].fields.forEach((item2) => {
64
- if (item2.requiredMessage) {
65
- msg = item2.requiredMessage
66
- } else if (['select', 'date-picker', 'searchSelect', 'daterange'].includes(item2.type)) {
67
- msg = $lc('请选择') + item2.label
68
- } else {
69
- msg = $lc('请输入') + item2.label
70
- }
71
- rules[item2.fieldCode] = [
72
- {
73
- required: item2.requiredFlag && item2.type,
74
- message: msg,
75
- trigger: ['submit', 'change']
76
- }
77
- ]
78
- })
79
- } else {
80
- if (item.list[key].requiredMessage) {
81
- msg = item.list[key].requiredMessage
82
- } else if (['select', 'date-picker', 'searchSelect', 'daterange'].includes(item.list[key].type)) {
83
- msg = $lc('请选择') + item.list[key].label
84
- } else {
85
- msg = $lc('请输入') + item.list[key].label
86
- }
87
- rules[key] = [
88
- {
89
- required: item.list[key].requiredFlag && item.list[key].type,
90
- message: msg,
91
- trigger: ['submit', 'change']
92
- }
93
- ]
94
- }
95
- }
96
- })
97
- this.rules = { ...rules, ...this.staticRules }
98
- this.$nextTick(() => {
99
- this.$refs.form && this.$refs.form.clearValidate()
100
- })
101
- },
102
- // 转换方法
103
- transform(value) {
104
- // 过滤掉空数据
105
- const valueData = value.filter((item) => {
106
- return item.fieldGroup && item.fieldType && item.fieldName && item.fieldCode && item.isEnabled
107
- })
108
- console.log(valueData)
109
- let groups = []
110
- let form = []
111
- let formValue = {}
112
- valueData.forEach((item) => {
113
- if (!groups.includes(item.fieldGroup)) {
114
- groups.push(item.fieldGroup)
115
- }
116
- // 有默认值的话
117
- if (item.defaultValue) {
118
- formValue[item.fieldCode] = item.defaultValue
119
- // 区间
120
- } else if (['input-number-range', 'input-rate-range', 'daterangee'].includes(item.fieldType)) {
121
- formValue[item.fieldCode] = {
122
- start: '',
123
- end: ''
124
- }
125
- // 字符串
126
- } else {
127
- formValue[item.fieldCode] = ''
128
- }
129
- })
130
- console.log(groups)
131
- groups.forEach((item) => {
132
- let list = {}
133
- valueData.forEach((item2) => {
134
- if (item2.fieldGroup === item) {
135
- list[item2.fieldCode] = {
136
- type: item2.fieldType,
137
- label: item2.fieldName,
138
- requiredFlag: item2.isMust,
139
- remark: item2.remark,
140
- options: item2.fieldValue
141
- }
142
- }
143
- })
144
- form.push({ tabName: item, list: list })
145
- })
146
- return { form: form, formValue: formValue }
147
- }
148
- }
149
- }
150
- export default obj
1
+ import { $lc } from '../../utils/i18n/index.js'
2
+ const obj = {
3
+ mounted() {
4
+ this.setRules()
5
+ },
6
+ data() {
7
+ return {
8
+ // batch 批处理内部标志:withBatch 包裹期间 _batchDepth>0,
9
+ // 期间 setType 的 requiredFlag 变更只标记 dirty,不立即 setRules,由批次末尾合并触发一次
10
+ _batchDepth: 0,
11
+ _batchRulesDirty: false
12
+ }
13
+ },
14
+ methods: {
15
+ // 批处理包裹器:可重入。一次用户动作(如 valueChange/setView)内连续多次 setType,
16
+ // 合并成末尾一次 setRules,避免 O(全表) rules 重建风暴。批次外(含异步逸出的 setType)保持原即时语义。
17
+ withBatch(fn) {
18
+ this._batchDepth++
19
+ try {
20
+ return fn()
21
+ } finally {
22
+ this._batchDepth--
23
+ if (this._batchDepth === 0 && this._batchRulesDirty) {
24
+ this._batchRulesDirty = false
25
+ this.setRules()
26
+ }
27
+ }
28
+ },
29
+ // 对相关方法进行设置
30
+ setType(name, value) {
31
+ this.form.forEach((item) => {
32
+ for (const key in item.list) {
33
+ if (item.list && item.list[key].type === 'group-container') {
34
+ const index = item.list[key].fields.findIndex((id) => {
35
+ return id.fieldCode === name
36
+ })
37
+ if (index !== -1) {
38
+ item.list[key].fields[index] = { ...item.list[key].fields[index], ...value }
39
+ return false
40
+ }
41
+ }
42
+ }
43
+ if (item.list && item.list[name]) {
44
+ item.list[name] = { ...item.list[name], ...value }
45
+ }
46
+ })
47
+ // 更新rules:批次内只标记 dirty,批次外立即触发
48
+ if (value.hasOwnProperty('requiredFlag')) {
49
+ if (this._batchDepth > 0) {
50
+ this._batchRulesDirty = true
51
+ } else {
52
+ this.setRules()
53
+ }
54
+ }
55
+ },
56
+ // 生成校验rules
57
+ setRules() {
58
+ const rules = {}
59
+ this.form.forEach((item) => {
60
+ let msg = ''
61
+ for (const key in item.list) {
62
+ if (item.list && item.list[key].type === 'group-container') {
63
+ item.list[key].fields.forEach((item2) => {
64
+ if (item2.requiredMessage) {
65
+ msg = item2.requiredMessage
66
+ } else if (['select', 'date-picker', 'searchSelect', 'daterange'].includes(item2.type)) {
67
+ msg = $lc('请选择') + item2.label
68
+ } else {
69
+ msg = $lc('请输入') + item2.label
70
+ }
71
+ rules[item2.fieldCode] = [
72
+ {
73
+ required: item2.requiredFlag && item2.type,
74
+ message: msg,
75
+ trigger: ['submit', 'change']
76
+ }
77
+ ]
78
+ })
79
+ } else {
80
+ if (item.list[key].requiredMessage) {
81
+ msg = item.list[key].requiredMessage
82
+ } else if (['select', 'date-picker', 'searchSelect', 'daterange'].includes(item.list[key].type)) {
83
+ msg = $lc('请选择') + item.list[key].label
84
+ } else {
85
+ msg = $lc('请输入') + item.list[key].label
86
+ }
87
+ rules[key] = [
88
+ {
89
+ required: item.list[key].requiredFlag && item.list[key].type,
90
+ message: msg,
91
+ trigger: ['submit', 'change']
92
+ }
93
+ ]
94
+ }
95
+ }
96
+ })
97
+ this.rules = { ...rules, ...this.staticRules }
98
+ this.$nextTick(() => {
99
+ this.$refs.form && this.$refs.form.clearValidate()
100
+ })
101
+ },
102
+ // 转换方法
103
+ transform(value) {
104
+ // 过滤掉空数据
105
+ const valueData = value.filter((item) => {
106
+ return item.fieldGroup && item.fieldType && item.fieldName && item.fieldCode && item.isEnabled
107
+ })
108
+ console.log(valueData)
109
+ let groups = []
110
+ let form = []
111
+ let formValue = {}
112
+ valueData.forEach((item) => {
113
+ if (!groups.includes(item.fieldGroup)) {
114
+ groups.push(item.fieldGroup)
115
+ }
116
+ // 有默认值的话
117
+ if (item.defaultValue) {
118
+ formValue[item.fieldCode] = item.defaultValue
119
+ // 区间
120
+ } else if (['input-number-range', 'input-rate-range', 'daterangee'].includes(item.fieldType)) {
121
+ formValue[item.fieldCode] = {
122
+ start: '',
123
+ end: ''
124
+ }
125
+ // 字符串
126
+ } else {
127
+ formValue[item.fieldCode] = ''
128
+ }
129
+ })
130
+ console.log(groups)
131
+ groups.forEach((item) => {
132
+ let list = {}
133
+ valueData.forEach((item2) => {
134
+ if (item2.fieldGroup === item) {
135
+ list[item2.fieldCode] = {
136
+ type: item2.fieldType,
137
+ label: item2.fieldName,
138
+ requiredFlag: item2.isMust,
139
+ remark: item2.remark,
140
+ options: item2.fieldValue
141
+ }
142
+ }
143
+ })
144
+ form.push({ tabName: item, list: list })
145
+ })
146
+ return { form: form, formValue: formValue }
147
+ }
148
+ }
149
+ }
150
+ export default obj
@@ -1,36 +1,36 @@
1
- /**
2
- * 深度克隆
3
- * @param target
4
- * @return {}
5
- */
6
- export const deepClone = (target, map = new Map()) => {
7
- // 检测数据的类型
8
- if (typeof target === 'object' && target !== null) {
9
- // 克隆数据之前, 进行判断, 数据之前是否克隆过
10
- const cache = map.get(target)
11
- if (cache) {
12
- return cache
13
- }
14
- // 判断目标数据的类型
15
- const isArray = Array.isArray(target)
16
- // 创建一个容器
17
- const result = isArray ? [] : {}
18
- // 将新的结果存入到容器中
19
- map.set(target, result)
20
- // 如果目标数据为数组
21
- if (isArray) {
22
- // forEach 遍历
23
- target.forEach((item, index) => {
24
- result[index] = deepClone(item, map)
25
- })
26
- } else {
27
- // 如果是对象, 获取所有的键名, 然后 forEach 遍历
28
- Object.keys(target).forEach((key) => {
29
- result[key] = deepClone(target[key], map)
30
- })
31
- }
32
- return result
33
- } else {
34
- return target
35
- }
36
- }
1
+ /**
2
+ * 深度克隆
3
+ * @param target
4
+ * @return {}
5
+ */
6
+ export const deepClone = (target, map = new Map()) => {
7
+ // 检测数据的类型
8
+ if (typeof target === 'object' && target !== null) {
9
+ // 克隆数据之前, 进行判断, 数据之前是否克隆过
10
+ const cache = map.get(target)
11
+ if (cache) {
12
+ return cache
13
+ }
14
+ // 判断目标数据的类型
15
+ const isArray = Array.isArray(target)
16
+ // 创建一个容器
17
+ const result = isArray ? [] : {}
18
+ // 将新的结果存入到容器中
19
+ map.set(target, result)
20
+ // 如果目标数据为数组
21
+ if (isArray) {
22
+ // forEach 遍历
23
+ target.forEach((item, index) => {
24
+ result[index] = deepClone(item, map)
25
+ })
26
+ } else {
27
+ // 如果是对象, 获取所有的键名, 然后 forEach 遍历
28
+ Object.keys(target).forEach((key) => {
29
+ result[key] = deepClone(target[key], map)
30
+ })
31
+ }
32
+ return result
33
+ } else {
34
+ return target
35
+ }
36
+ }
@@ -310,9 +310,13 @@ export default {
310
310
  viewId: undefined,
311
311
  isRefresh: false,
312
312
  // 视图类型切换时缓存对方数据
313
- savedViewConfigs: {},
314
- // 选中第一个视图前的筛选区状态快照,切回"无视图"时用它恢复,避免上一视图条件残留
315
- initialDisplaySnapshot: null
313
+ savedViewConfigs: {},
314
+ // 选中第一个视图前的筛选区状态快照,切回"无视图"时用它恢复,避免上一视图条件残留
315
+ initialDisplaySnapshot: null,
316
+ // 选中第一个视图前的筛选项布局快照,切回"无视图"时恢复
317
+ initialFilterKeySnapshot: null,
318
+ // 防止快速切换视图时旧的异步布局任务覆盖新视图
319
+ filterLayoutSyncKey: 0
316
320
  }
317
321
  },
318
322
  computed: {
@@ -756,11 +760,12 @@ export default {
756
760
  this.selectItem = ''
757
761
  // 切回"无视图"恢复选中第一个视图前的筛选区状态;
758
762
  // 不能用 initialValue 恢复——它是页面实时查询表单,此时已被上一视图的值污染
759
- this.searchValue = {
760
- ...this.getInitialSearchValue,
761
- ...(this.initialDisplaySnapshot || this.initialValue)
762
- }
763
- this.conditionGroups = []
763
+ this.searchValue = {
764
+ ...this.getInitialSearchValue,
765
+ ...(this.initialDisplaySnapshot || this.initialValue)
766
+ }
767
+ this.conditionGroups = []
768
+ this.syncFilterLayout(this.initialFilterKeySnapshot, true)
764
769
  // filterObj 的 extraInitialValues 取自实时 initialValue(已被上一视图污染),
765
770
  // 切"无视图"时改用恢复后的 searchValue 整体作为载荷,避免上一视图字段从事件漏出
766
771
  this.$emit(
@@ -776,23 +781,44 @@ export default {
776
781
  )
777
782
  return
778
783
  }
779
- if (!prevSelectItem?.viewId) {
780
- // 从"无视图/初始状态"首次切到具体视图前,快照当前筛选区状态
781
- this.initialDisplaySnapshot = { ...this.searchValue }
782
- }
784
+ if (!prevSelectItem?.viewId) {
785
+ // 从"无视图/初始状态"首次切到具体视图前,快照当前筛选区状态
786
+ this.initialDisplaySnapshot = { ...this.searchValue }
787
+ this.initialFilterKeySnapshot = (this.$refs.filter?.GroupData || []).map((filter) => filter.id)
788
+ }
783
789
  if (item.viewType === VIEW_TYPE.BASIC) {
784
790
  const config = this.safeParse(item.viewConfig)
785
791
  this.searchForm = config
786
792
  this.searchValue = { ...this.getInitialSearchValue, ...config }
787
- } else if (item.viewType === VIEW_TYPE.ADVANCED) {
788
- this.conditionGroups = this.safeParse(item.viewConfig).conditionGroups || []
789
- this.searchValue = { ...this.getInitialSearchValue }
790
- }
793
+ } else if (item.viewType === VIEW_TYPE.ADVANCED) {
794
+ this.conditionGroups = this.safeParse(item.viewConfig).conditionGroups || []
795
+ this.searchValue = { ...this.getInitialSearchValue }
796
+ }
797
+ // 基础视图只展示保存时勾选的字段;高级视图 keyIds 为空,仅保留 static 固定字段
798
+ this.syncFilterLayout(item.keyIds || [])
791
799
  // isViewSwitch 标记本次 filter 事件由视图切换触发。
792
800
  // 此前切到"无视图"与"无视图下手动查询"的事件参数完全一致(viewId 均为空),
793
801
  // 业务页面无法区分,只能靠额外记录状态判断
794
- this.$emit('filter', { ...this.filterObj, isViewSwitch: true }, 'filter')
795
- },
802
+ this.$emit('filter', { ...this.filterObj, isViewSwitch: true }, 'filter')
803
+ },
804
+ /**
805
+ * 按视图保存的 keyIds 同步筛选项布局。
806
+ * 等 searchValue/initialValue 的监听与自动追加逻辑执行完后再覆盖,避免旧布局残留。
807
+ */
808
+ syncFilterLayout(keyIds = [], resetSnapshot = false) {
809
+ const syncKey = ++this.filterLayoutSyncKey
810
+ this.$nextTick(() => {
811
+ this.$nextTick(() => {
812
+ if (syncKey !== this.filterLayoutSyncKey || !this.$refs.filter) return
813
+ const filterMap = new Map(this.filterList.map((filter) => [String(filter.id), filter]))
814
+ const filters = keyIds.map((id) => filterMap.get(String(id))).filter(Boolean)
815
+ this.$refs.filter.mackData(filters)
816
+ if (resetSnapshot) {
817
+ this.initialFilterKeySnapshot = null
818
+ }
819
+ })
820
+ })
821
+ },
796
822
  // 拖动保存视图列表
797
823
  handleDragSort(list) {
798
824
  console.log($lc('当前list:'), list)
@@ -840,8 +866,10 @@ export default {
840
866
  this.conditionGroups = []
841
867
  this.filterExpanded = this.defaultExpanded
842
868
  this.viewId = undefined
843
- this.savedViewConfigs = {}
844
- this.initialDisplaySnapshot = null
869
+ this.savedViewConfigs = {}
870
+ this.initialDisplaySnapshot = null
871
+ this.initialFilterKeySnapshot = null
872
+ this.filterLayoutSyncKey++
845
873
  }
846
874
  }
847
875
  }