n20-common-lib 3.1.20 → 3.2.0

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.20",
3
+ "version": "3.2.0",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -171,6 +171,12 @@ export default {
171
171
  bussId: {
172
172
  type: String,
173
173
  default: ''
174
+ },
175
+ // 开启后,自动把 filterList 中有非空值但不在 GroupData 的项追加到可见列表
176
+ // 默认 false —— 完全向后兼容,仅在 ProFilterView 透传或业务方显式开启时生效
177
+ autoShowActiveFilters: {
178
+ type: Boolean,
179
+ default: false
174
180
  }
175
181
  },
176
182
  data() {
@@ -288,6 +294,65 @@ export default {
288
294
  this.optionsMap = {}
289
295
  },
290
296
  methods: {
297
+ /**
298
+ * 判断某个 filterList 项在当前 model 中是否有非空值
299
+ * 支持 daterange / datetimerange / monthrange / numberrange / slotFields / value 四种形态
300
+ * 非空定义与 activeClassMap 内 hasValue 一致:null/undefined/''/空数组 均为空
301
+ * @param {Object} item - filterList 中的某一项
302
+ * @returns {boolean}
303
+ */
304
+ isFilterActive(item) {
305
+ const hasValue = (val) => {
306
+ if (Array.isArray(val)) return val.length > 0
307
+ return val !== null && val !== undefined && val !== ''
308
+ }
309
+ // 区间型字段:起止任一非空即视为有值
310
+ const rangeTypes = ['daterange', 'datetimerange', 'monthrange']
311
+ if (rangeTypes.includes(item.type)) {
312
+ return hasValue(this.model[item.startDate]) || hasValue(this.model[item.endDate])
313
+ }
314
+ // 数值区间
315
+ if (item.type === 'numberrange') {
316
+ return hasValue(this.model[item.startValue]) || hasValue(this.model[item.endValue])
317
+ }
318
+ // slot 多值绑定:slotFields 中任一字段非空即视为有值
319
+ if (item.slotFields && item.slotFields.length) {
320
+ return item.slotFields.some((field) => hasValue(this.model[field]))
321
+ }
322
+ // 默认单值字段
323
+ if (item.value) {
324
+ return hasValue(this.model[item.value])
325
+ }
326
+ return false
327
+ },
328
+ /**
329
+ * 当 autoShowActiveFilters 开启时,自动把 filterList 中有非空值但不在 GroupData
330
+ * (用户隐藏的列表)的项追加到可见列表,并同步 checkList
331
+ * - 跳过 static 项(由 mackData 单独处理)
332
+ * - 跳过已存在于 GroupData 的项
333
+ * - 以 { ...item } 浅拷贝追加,避免外部 filterList 引用被改
334
+ */
335
+ ensureActiveFiltersVisible() {
336
+ if (!this.autoShowActiveFilters) return
337
+
338
+ // 当前 GroupData 中已有的 id 集合
339
+ const existingIds = new Set(this.GroupData.map((item) => item[this.onlyKey]))
340
+
341
+ // 筛选出:有值 & 不在 GroupData & 非 static 的项
342
+ const missingActiveItems = this.filterList.filter((item) => {
343
+ if (existingIds.has(item[this.onlyKey])) return false
344
+ if (item.static) return false
345
+ return this.isFilterActive(item)
346
+ })
347
+
348
+ if (missingActiveItems.length > 0) {
349
+ // 浅拷贝追加到 GroupData 末尾
350
+ const newItems = missingActiveItems.map((item) => ({ ...item }))
351
+ this.GroupData = [...this.GroupData, ...newItems]
352
+ // 同步 checkList(按 onlyKey 取值)
353
+ this.checkList = this.GroupData.map((res) => res[this.onlyKey])
354
+ }
355
+ },
291
356
  // 保存视图
292
357
  saveSt() {
293
358
  this.$refs.stform.validate(async (valid) => {
@@ -673,6 +738,10 @@ export default {
673
738
  })
674
739
 
675
740
  console.log(this.filterList)
741
+
742
+ // 双保险:在 mackData 末尾兜底执行一次可见性追加
743
+ // 覆盖"接口异步返回隐藏列表后再次渲染"的场景,与 ProFilterView.$nextTick 调用形成双钩子
744
+ this.ensureActiveFiltersVisible()
676
745
  }
677
746
  }
678
747
  }
@@ -0,0 +1,248 @@
1
+ <template>
2
+ <cl-page class="CustomFormView">
3
+ <slot name="top"></slot>
4
+ <cl-expandable-pane v-for="(item, index) in formOptions" :key="index" :title="item.tabName">
5
+ <template v-if="item.slotName">
6
+ <slot :name="item.slotName"></slot>
7
+ </template>
8
+ <cl-descriptions>
9
+ <template v-for="(formItem, key) in item.list">
10
+ <!-- 字段标识:数组模式用 formItem.key,对象模式用 v-for 的 key -->
11
+ <el-descriptions-item v-if="formItem.type" :key="formItem.key || key" :label="$l(formItem.label)">
12
+ <template slot="label">
13
+ {{ formItem.label }}
14
+ </template>
15
+ <!-- 插槽 -->
16
+ <template v-if="formItem.type === 'slot'">
17
+ <div>
18
+ <slot :name="formItem.key || key"></slot>
19
+ </div>
20
+ </template>
21
+ <!-- 期限 -->
22
+ <template v-else-if="(formItem.key || key) === 'contractDTO_loanTerm' && formItem.type === 'input'">
23
+ <cl-diff
24
+ v-if="diff"
25
+ :value="`${formValue['contractDTO_loanTerm']} ${
26
+ formValue['contractDTO_termUnit'] === 'DAY'
27
+ ? $l('天')
28
+ : formValue['contractDTO_termUnit'] === 'MONTH'
29
+ ? $l('月')
30
+ : formValue['contractDTO_termUnit'] === 'YEAR'
31
+ ? $l('年')
32
+ : ''
33
+ }`"
34
+ :old-value="`${oldFormValue['contractDTO_loanTerm']} ${
35
+ oldFormValue['contractDTO_termUnit'] === 'DAY'
36
+ ? $l('天')
37
+ : oldFormValue['contractDTO_termUnit'] === 'MONTH'
38
+ ? $l('月')
39
+ : oldFormValue['contractDTO_termUnit'] === 'YEAR'
40
+ ? $l('年')
41
+ : ''
42
+ }`"
43
+ :tooltip="true"
44
+ />
45
+ <div v-else>
46
+ {{ formValue['contractDTO_loanTerm'] }}
47
+ {{
48
+ formValue['contractDTO_termUnit'] === 'DAY'
49
+ ? $l('天')
50
+ : formValue['contractDTO_termUnit'] === 'MONTH'
51
+ ? $l('月')
52
+ : formValue['contractDTO_termUnit'] === 'YEAR'
53
+ ? $l('年')
54
+ : ''
55
+ }}
56
+ </div>
57
+ </template>
58
+ <!-- 结构化/费用编号 -->
59
+ <template
60
+ v-else-if="
61
+ $l(formItem.label) === $l('是否结构化') ||
62
+ ((formItem.key || key) === 'exInfoDTO_feeNo' && (formItem.key || key) !== 'mainFeeCurrencyCode')
63
+ "
64
+ >
65
+ <cl-diff
66
+ v-if="diff"
67
+ :value="(formValue[formItem.key || key] && formValue[formItem.key || key].name) || '--'"
68
+ :old-value="(oldFormValue[formItem.key || key] && oldFormValue[formItem.key || key].name) || '--'"
69
+ :tooltip="true"
70
+ />
71
+ <div v-else>{{ (formValue[formItem.key || key] && formValue[formItem.key || key].name) || '--' }}</div>
72
+ </template>
73
+ <!-- 地区信息 -->
74
+ <template v-else-if="(formItem.key || key) === 'recInfoDTO_areaInfo'">
75
+ <cl-diff
76
+ v-if="diff"
77
+ :value="formValue['recInfoDTO_areaInfoName'] || '--'"
78
+ :old-value="oldFormValue['recInfoDTO_areaInfoName'] || '--'"
79
+ :tooltip="true"
80
+ />
81
+ <div v-else>{{ formValue['recInfoDTO_areaInfoName'] || '--' }}</div>
82
+ </template>
83
+ <!-- 借/贷方 -->
84
+ <template v-else-if="(formItem.key || key) === 'contractDTO_debtorCode' || (formItem.key || key) === 'contractDTO_creditorCode'">
85
+ <cl-diff
86
+ v-if="diff"
87
+ :value="(formValue[formItem.key || key] && formValue[formItem.key || key].name) || '--'"
88
+ :old-value="(oldFormValue[formItem.key || key] && oldFormValue[formItem.key || key].name) || '--'"
89
+ :tooltip="true"
90
+ />
91
+ <div v-else>{{ (formValue[formItem.key || key] && formValue[formItem.key || key].name) || '--' }}</div>
92
+ </template>
93
+ <!-- 金额 -->
94
+ <template v-else-if="formItem.type === 'input-number'">
95
+ <div
96
+ v-if="
97
+ [$l('利率'), $l('合同利率'), $l('利率(%)'), $l('合同利率(%)'), $l('中间价')].includes(
98
+ $l(formItem.label)
99
+ )
100
+ "
101
+ >
102
+ <cl-diff
103
+ v-if="diff"
104
+ :value="(formValue[formItem.key || key] && numerify(formValue[formItem.key || key], '0.000000')) || '--'"
105
+ :old-value="(oldFormValue[formItem.key || key] && numerify(oldFormValue[formItem.key || key], '0.000000')) || '--'"
106
+ :tooltip="true"
107
+ />
108
+ <span v-else>{{ formValue[formItem.key || key] ? numerify(formValue[formItem.key || key], '0.000000') : '--' }}</span>
109
+ </div>
110
+ <div v-else>
111
+ <cl-diff
112
+ v-if="diff"
113
+ :value="(formValue[formItem.key || key] && numerify(formValue[formItem.key || key], '0,0.00')) || '--'"
114
+ :old-value="(oldFormValue[formItem.key || key] && numerify(oldFormValue[formItem.key || key], '0,0.00')) || '--'"
115
+ :tooltip="true"
116
+ />
117
+ <span v-else>{{ formValue[formItem.key || key] ? numerify(formValue[formItem.key || key], '0,0.00') : '--' }}</span>
118
+ </div>
119
+ </template>
120
+ <!-- 来款国家(地区) -->
121
+ <template v-else-if="$l(formItem.label) === $l('来款国家(地区)')">
122
+ <cl-diff
123
+ v-if="diff"
124
+ :value="formValue['applyInfoDTO_areaInfoName'] || '--'"
125
+ :old-value="oldFormValue['applyInfoDTO_areaInfoName'] || '--'"
126
+ :tooltip="true"
127
+ />
128
+ <div v-else>{{ formValue['applyInfoDTO_areaInfoName'] || '--' }}</div>
129
+ </template>
130
+ <!-- 下拉枚举 -->
131
+ <template v-else-if="formItem.type === 'select'">
132
+ <div class="flex-box flex-v">
133
+ <div v-if="formValue[formItem.key || key] && formValue[formItem.key || key].name">
134
+ <cl-diff
135
+ v-if="diff"
136
+ :value="(formValue[formItem.key || key] && formValue[formItem.key || key].name) || '--'"
137
+ :old-value="(oldFormValue[formItem.key || key] && oldFormValue[formItem.key || key].name) || '--'"
138
+ :tooltip="true"
139
+ />
140
+ <span v-else>{{ (formValue[formItem.key || key] && formValue[formItem.key || key].name) || '--' }}</span>
141
+ </div>
142
+ <div v-else-if="formItem.label === '结售汇性质'">
143
+ <cl-diff
144
+ v-if="diff"
145
+ :value="formValue[formItem.key || key] || '--'"
146
+ :old-value="oldFormValue[formItem.key || key] || '--'"
147
+ :tooltip="true"
148
+ />
149
+ <span v-else>{{ formValue[formItem.key || key] || '--' }}</span>
150
+ </div>
151
+ <div v-else>
152
+ <template v-if="diff">
153
+ <cl-diff
154
+ :value="getEnumName(formItem.enumList, formValue[formItem.key || key])"
155
+ :old-value="getEnumName(formItem.enumList, oldFormValue[formItem.key || key])"
156
+ :tooltip="true"
157
+ />
158
+ </template>
159
+ <span v-else>{{ getEnumName(formItem.enumList, formValue[formItem.key || key]) }}</span>
160
+ </div>
161
+ </div>
162
+ </template>
163
+ <!-- 其他类型(含 text 类型,自动支持超长文本 Tooltip) -->
164
+ <template v-else>
165
+ <div>
166
+ <cl-diff
167
+ v-if="diff"
168
+ :value="formValue[formItem.key || key] || '--'"
169
+ :old-value="oldFormValue[formItem.key || key] || '--'"
170
+ :tooltip="true"
171
+ />
172
+ <div v-else>
173
+ <el-tooltip
174
+ v-if="formValue[formItem.key || key] && formValue[formItem.key || key].length > 50"
175
+ class="item"
176
+ effect="dark"
177
+ :content="formValue[formItem.key || key]"
178
+ placement="top"
179
+ >
180
+ <span>{{ formValue[formItem.key || key].slice(0, 50) + '...' }}</span>
181
+ </el-tooltip>
182
+ <span v-else>{{ formValue[formItem.key || key] || '--' }}</span>
183
+ </div>
184
+ </div>
185
+ </template>
186
+ </el-descriptions-item>
187
+ </template>
188
+ </cl-descriptions>
189
+ </cl-expandable-pane>
190
+ <slot name="bottom"></slot>
191
+ </cl-page>
192
+ </template>
193
+
194
+ <script>
195
+ import numerify from 'numerify'
196
+
197
+ import ClDescriptions from '../Descriptions/index.vue'
198
+ import ClDiff from '../Diff/index.vue'
199
+ import ClExpandablePane from '../Expandable/index.vue'
200
+ import ClPage from '../PageLayout/page.vue'
201
+
202
+ export default {
203
+ name: 'DynamicFormView',
204
+ components: { ClPage, ClExpandablePane, ClDescriptions, ClDiff },
205
+ props: {
206
+ // 表单配置
207
+ formOptions: {
208
+ type: Array,
209
+ default: () => {
210
+ return []
211
+ }
212
+ },
213
+ formValue: {
214
+ type: Object,
215
+ default: () => {
216
+ return {}
217
+ }
218
+ },
219
+ diff: {
220
+ type: Boolean,
221
+ default: false
222
+ },
223
+ oldFormValue: {
224
+ type: Object,
225
+ default: () => {
226
+ return {}
227
+ }
228
+ }
229
+ },
230
+ data() {
231
+ return {
232
+ numerify
233
+ }
234
+ },
235
+ methods: {
236
+ getEnumName(enumList, code) {
237
+ if (!enumList?.length) return
238
+ const enumData = Array.isArray(enumList) ? enumList : JSON.parse(enumList)
239
+ return enumData?.find((item) => (item.code || item.value) === code)?.name || '--'
240
+ }
241
+ }
242
+ }
243
+ </script>
244
+ <style scoped>
245
+ .CustomFormView {
246
+ font-size: 14px;
247
+ }
248
+ </style>
@@ -82,6 +82,7 @@
82
82
  :filter-id="filterId"
83
83
  :buss-id="bussId"
84
84
  :model="searchValue"
85
+ :auto-show-active-filters="autoExpandWithValues"
85
86
  only-key="id"
86
87
  :filter-list="filterList"
87
88
  v-bind="$attrs"
@@ -249,10 +250,16 @@ export default {
249
250
  type: Object,
250
251
  default: () => ({})
251
252
  },
252
- // 筛选器默认展开/收起状态
253
+ // 筛选器默认展开/收起状态(与 data 中 filterExpanded 初始值对齐,无新功能)
253
254
  defaultExpanded: {
254
255
  type: Boolean,
255
256
  default: false
257
+ },
258
+ // 当筛选条件有非空值时,是否自动展开筛选区域
259
+ // 默认 false —— 完全向后兼容,业务方按页面粒度显式开启
260
+ autoExpandWithValues: {
261
+ type: Boolean,
262
+ default: false
256
263
  }
257
264
  },
258
265
  data() {
@@ -346,6 +353,8 @@ export default {
346
353
 
347
354
  // 智能合并:保留用户已手动修改的值
348
355
  this.searchValue = newVal
356
+ // 父组件更新 initialValue 后,重新判定是否需要自动展开
357
+ this.checkAndAutoExpand()
349
358
  },
350
359
  deep: true,
351
360
  immediate: false
@@ -368,8 +377,57 @@ export default {
368
377
  }
369
378
  // 合并初始筛选值:slot 字段使用默认值,其他字段使用 initialValue 传入的值
370
379
  this.searchValue = { ...this.getInitialSearchValue, ...this.initialValue }
380
+ // 初始挂载后根据筛选值情况决定是否自动展开
381
+ this.checkAndAutoExpand()
371
382
  },
372
383
  methods: {
384
+ /**
385
+ * 判断 searchValue 中是否有非空值
386
+ * 仅扫描 filterList 中声明的字段,忽略 searchValue 中其它无关 key
387
+ * 支持 daterange / numberrange / slotFields / value 四种 field 形态
388
+ * @returns {boolean}
389
+ */
390
+ hasActiveFilters() {
391
+ // 非空判定:null/undefined/''/空数组 均为空
392
+ const hasValue = (val) => {
393
+ if (Array.isArray(val)) return val.length > 0
394
+ return val !== null && val !== undefined && val !== ''
395
+ }
396
+
397
+ return this.filterList.some((item) => {
398
+ // 区间型字段(日期/日期时间/月份):起止任一非空即视为有值
399
+ if (item.type === 'daterange' || item.type === 'datetimerange' || item.type === 'monthrange') {
400
+ return hasValue(this.searchValue[item.startDate]) || hasValue(this.searchValue[item.endDate])
401
+ }
402
+ // 数值区间
403
+ if (item.type === 'numberrange') {
404
+ return hasValue(this.searchValue[item.startValue]) || hasValue(this.searchValue[item.endValue])
405
+ }
406
+ // slot 多值绑定
407
+ if (item.slotFields && item.slotFields.length) {
408
+ return item.slotFields.some((field) => hasValue(this.searchValue[field]))
409
+ }
410
+ // 默认单值字段
411
+ if (item.value) {
412
+ return hasValue(this.searchValue[item.value])
413
+ }
414
+ return false
415
+ })
416
+ },
417
+ /**
418
+ * 当 autoExpandWithValues 开启且当前存在有效筛选值时,自动展开筛选区域
419
+ * 并在下一帧通知 AdvancedFilter 把"有值但被用户隐藏"的筛选项重新拉回可见列表
420
+ */
421
+ checkAndAutoExpand() {
422
+ if (!this.autoExpandWithValues) return
423
+ if (this.hasActiveFilters()) {
424
+ this.filterExpanded = true
425
+ // $nextTick 等 AdvancedFilter 的 mackData 把 GroupData 填充好后再追加
426
+ this.$nextTick(() => {
427
+ this.$refs.filter?.ensureActiveFiltersVisible()
428
+ })
429
+ }
430
+ },
373
431
  // 处理 slot 类型字段的输入事件,同时更新 searchValue 和 initialValue
374
432
  handleSlotInput(fieldName, val) {
375
433
  // 防御性检查:如果 fieldName 无效,不执行任何操作
@@ -413,6 +413,15 @@ export default {
413
413
  }
414
414
  }
415
415
  },
416
+ // ai校验取消
417
+ aiCheckCancel(beid) {
418
+ this.tableData.forEach((item) => {
419
+ if (item.beid === beid) {
420
+ item.aiCheckStatus = ''
421
+ }
422
+ this.tableKey++
423
+ })
424
+ },
416
425
  async getFileTypes() {
417
426
  if (!this.dataProp?.bussValues?.length) return
418
427
  try {
package/src/index.js CHANGED
@@ -66,6 +66,7 @@ import DragList from './components/DragList/index.vue'
66
66
  // 动态表单
67
67
  import DynamicField from './components/DynamicField/DynamicField.vue'
68
68
  import DynamicFieldOptions from './components/DynamicField/DynamicFieldOptions.vue'
69
+ import DynamicFormView from './components/DynamicField/DynamicFormView.vue'
69
70
  import DynamicFieldTable from './components/DynamicField/DynamicTable.vue'
70
71
  import ElectronicArchive from './components/ElectronicArchive/index.vue'
71
72
  import Empty from './components/Empty/index.vue'
@@ -192,6 +193,7 @@ const components = [
192
193
  DynamicFieldTable,
193
194
  DynamicField,
194
195
  DynamicFieldOptions,
196
+ DynamicFormView,
195
197
  ContentLoading,
196
198
  ContentNull,
197
199
  NavMenu,