n20-common-lib 3.2.4 → 3.2.5

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.
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <el-form
3
+ class="vertical-form cl-dynamic-field"
3
4
  ref="form"
4
5
  :inline="inline"
5
6
  :model="value"
@@ -11,8 +12,16 @@
11
12
  <slot name="top"></slot>
12
13
  <cl-anchor-item v-for="(item, index) in model" :id="item.tabName" :key="index" :title="item.tabName">
13
14
  <template slot="title">
14
- <span v-if="item.tabNamehtml" v-html="item.tabNamehtml"></span>
15
- <span v-else>{{ item.tabName }}</span>
15
+ <div class="anchor-title-wrapper">
16
+ <div class="anchor-title-left">
17
+ <span v-if="item.tabNamehtml" v-html="item.tabNamehtml"></span>
18
+ <span v-else>{{ item.tabName }}</span>
19
+ </div>
20
+ <!-- 右侧自定义内容插槽 -->
21
+ <div v-if="$slots[`title-right-${index}`]" class="anchor-title-right" @click.stop>
22
+ <slot :name="`title-right-${index}`"></slot>
23
+ </div>
24
+ </div>
16
25
  </template>
17
26
  <template slot="tips">
18
27
  <slot :name="`tips_${index}`"></slot>
@@ -21,313 +30,693 @@
21
30
  <slot :name="item.slotName" :list="item.list"></slot>
22
31
  </template>
23
32
  <div v-else>
33
+ <!-- 当前 tab 顶部块级插槽 -->
34
+ <div v-if="$slots[`tab-top-${index}`]" class="tab-block-slot tab-top-slot">
35
+ <slot :name="`tab-top-${index}`"></slot>
36
+ </div>
37
+
24
38
  <template v-for="(formItem, key) in item.list">
25
- <!--只渲染明确输入类型的字段-->
26
- <el-form-item
27
- v-show="formItem.type"
39
+ <!-- InfoCard 分组容器 - 只有当 fields 有有效内容时才显示 -->
40
+ <InfoCard
41
+ v-show="hasValidGroupFields(formItem.fields)"
42
+ v-if="formItem.isGroupContainer && formItem.type === 'group-container'"
28
43
  :key="key"
29
- :prop="key"
30
- v-bind="formItem"
31
- :class="{ 'col-span-24': formItem.type === 'textarea' || formItem.alone }"
44
+ :title="formItem.groupName"
45
+ :icon="formItem.groupInfo?.icon || ''"
46
+ :corner-logo="formItem.groupInfo?.cornerLogo || ''"
47
+ :background-color="formItem.groupInfo?.color || '#e8f7ff'"
48
+ :collapsible="formItem.groupInfo?.showCollapse || false"
49
+ default-expanded
50
+ class="group-infocard"
32
51
  >
33
- <!-- 表头插槽 -->
34
- <template slot="label">
35
- <slot :name="'label_' + key">
36
- <span>{{ formItem.label }}</span>
37
- </slot>
38
- </template>
39
- <!--编辑状态-->
40
- <template v-if="edit">
41
- <div class="flex-box flex-v">
42
- <template
43
- :disabled="formItem.tooltip && !formItem.tooltip.disabled && String(formItem.value) ? false : true"
44
- :content="(formItem.tooltip && formItem.tooltip.content) || String(formItem.value)"
45
- placement="top"
46
- >
47
- <!-- 插槽 -->
48
- <template v-if="formItem.type === 'slot'">
49
- <slot :name="key" :formItem="formItem"></slot>
50
- </template>
51
- <!--判断字段输入类型 如:input元素、select元素-->
52
- <!-- 输入框 -->
53
- <template v-if="formItem.type === 'input'">
54
- <el-input
55
- v-model="value[key]"
56
- v-title
57
- type="text"
58
- class="input-w"
59
- :placeholder="formItem.placeholder || formItem.label"
60
- :clearable="formItem.clearable === false ? false : true"
61
- :maxlength="formItem.maxlength || inputMaxLength"
62
- v-bind="formItem"
63
- @change="(value) => $emit('valueChange', value, key)"
64
- />
65
- <el-button
66
- v-if="showBtn"
67
- type="primary"
68
- size="mini"
69
- class="m-l-s"
70
- @click="$emit('showBtnClick', formItem, key)"
71
- >{{ formItem.btnText || showBtnText }}</el-button
72
- >
73
- </template>
74
- <!-- 金额 -->
75
- <template v-else-if="formItem.type === 'input-number'">
76
- <cl-input-number
77
- v-model="value[key]"
78
- class="input-w"
79
- :is-clearable="formItem.isClearable"
80
- :clearable="formItem.clearable === false ? false : true"
81
- :disabled="formItem.disabled"
82
- :max="formItem.max || 9999999999.99"
83
- :min="formItem.min || formItem.min === 0 ? formItem.min : 0"
84
- :d-num="formItem.dNum || formItem.dNum === 0 ? formItem.dNum : 2"
85
- :type="formItem.numberType || 'money'"
86
- @change="(value) => $emit('valueChange', value, key)"
87
- />
88
- </template>
89
- <!-- 金额区间 -->
90
- <template v-else-if="formItem.type === 'input-number-range'">
91
- <cl-input-number-range
92
- class="input-w"
93
- :is-clearable="formItem.isClearable === false ? false : true"
94
- :disabled="formItem.disabled"
95
- :start-value.sync="value[key][formItem['startKey'] || 'start']"
96
- :end-value.sync="value[key][formItem['endKey'] || 'end']"
97
- @change="
98
- () =>
99
- $emit(
100
- 'valueChange',
101
- { start: value[formItem['startKey']], end: value[formItem['endKey']] },
102
- key
103
- )
104
- "
105
- />
106
- </template>
107
- <!-- 利率区间 -->
108
- <template v-else-if="formItem.type === 'input-rate-range'">
109
- <cl-input-number-range
110
- class="input-w"
111
- :disabled="formItem.disabled"
112
- :is-clearable="formItem.isClearable === false ? false : true"
113
- :start-value.sync="value[key][formItem['startKey'] || 'start']"
114
- :end-value.sync="value[key][formItem['endKey'] || 'end']"
115
- type="rate"
116
- :min="0"
117
- :max="100"
118
- @change="
119
- () =>
120
- $emit(
121
- 'valueChange',
122
- { start: value[formItem['startKey']], end: value[formItem['endKey']] },
123
- key
124
- )
125
- "
126
- />
127
- </template>
128
- <!-- 下拉框 -->
129
- <template v-else-if="formItem.type === 'select'">
130
- <el-select
131
- v-model="value[key]"
132
- v-title
133
- class="input-w"
134
- :placeholder="
135
- formItem.placeholder || (formItem.filterable ? $lc('请输入关键词') : formItem.label)
136
- "
137
- :remote-method="
138
- (query) => formItem.remoteMethod && formItem.remoteMethod(query, { ...$attrs, item, value })
139
- "
140
- :clearable="formItem.clearable === false ? false : true"
141
- :multiple="false"
142
- collapse-tags
143
- :class="{ remote: formItem.remote, valuable: formItem.value }"
144
- v-bind="formItem"
145
- @focus="(event) => formItem.focus && formItem.focus(event)"
146
- @change="(value) => $emit('valueChange', value, key)"
147
- >
148
- <el-option
149
- v-for="(item, index) in (formItem.asyncOptions && formItem.asyncOptions()) ||
150
- formItem.options ||
151
- []"
152
- :key="index + '' + (formItem.option ? item[formItem.option.value] : item.value)"
153
- :label="formItem.option ? item[formItem.option.label] : item.label || item.valueName"
154
- :value="formItem.option ? item[formItem.option.value] : item.value"
155
- @click.native="$emit('formItemClick', key, item)"
156
- >
157
- <div v-if="formItem.isSlot">
158
- ({{ formItem.option ? item[formItem.option.value] : item.value }}){{
159
- formItem.option ? item[formItem.option.label] : item.label
160
- }}
161
- </div>
162
- </el-option>
163
- <template v-if="formItem.remote" v-slot:prefix>
164
- <i class="suffix-icon el-icon-search"></i>
165
- </template>
166
- </el-select>
167
- </template>
168
- <!-- 多选下拉框 -->
169
- <template v-else-if="formItem.type === 'selectMultiple'">
170
- <el-select
171
- v-model="value[key]"
172
- class="input-w"
173
- :placeholder="
174
- formItem.placeholder || (formItem.filterable ? $lc('请输入关键词') : formItem.label)
175
- "
176
- :remote-method="
177
- (query) => formItem.remoteMethod && formItem.remoteMethod(query, { ...$attrs, item, value })
178
- "
179
- :clearable="formItem.clearable === false ? false : true"
180
- :multiple="true"
181
- collapse-tags
182
- :class="{ remote: formItem.remote, valuable: formItem.value }"
183
- v-bind="formItem"
184
- @focus="(event) => formItem.focus && formItem.focus(event)"
185
- @change="(value) => $emit('valueChange', value, key)"
186
- >
187
- <el-option
188
- v-for="(item, index) in (formItem.asyncOptions && formItem.asyncOptions()) ||
189
- formItem.options ||
190
- []"
191
- :key="index + '' + (formItem.option ? item[formItem.option.value] : item.value)"
192
- :label="formItem.option ? item[formItem.option.label] : item.label || item.valueName"
193
- :value="formItem.option ? item[formItem.option.value] : item.value"
194
- @click.native="$emit('formItemClick', key, item)"
195
- >
196
- <div v-if="formItem.isSlot">
197
- ({{ formItem.option ? item[formItem.option.value] : item.value }}){{
198
- formItem.option ? item[formItem.option.label] : item.label
199
- }}
200
- </div>
201
- </el-option>
202
- <template v-if="formItem.remote" v-slot:prefix>
203
- <i class="suffix-icon el-icon-search"></i>
204
- </template>
205
- </el-select>
206
- </template>
207
- <!-- 日期选择 -->
208
- <template v-if="formItem.type === 'date-picker'">
209
- <cl-date-picker
210
- v-model="value[key]"
211
- v-bind="formItem"
212
- class="input-w"
213
- type="date"
214
- value-format="yyyy-MM-dd"
215
- :placeholder="$lc('请选择日期')"
216
- :clearable="formItem.clearable === false ? false : true"
217
- @change="(value) => $emit('valueChange', value, key)"
218
- />
219
- </template>
220
- <!--日期时间段-->
221
- <template v-else-if="formItem.type === 'daterange'">
222
- <cl-date-picker-por
223
- :start-date.sync="value[key][formItem['startKey'] || 'start']"
224
- :end-date.sync="value[key][formItem['endKey'] || 'end']"
225
- type="daterange"
226
- start-stop
227
- value-format="yyyy-MM-dd"
228
- v-bind="formItem"
229
- :clearable="formItem.clearable === false ? false : true"
230
- @change="(value) => $emit('valueChange', value, key)"
231
- />
232
- </template>
233
- <!--大文本-->
234
- <template v-else-if="formItem.type === 'textarea'">
235
- <el-input
236
- v-model="value[key]"
237
- v-title
238
- type="textarea"
239
- :placeholder="$lc('请输入内容')"
240
- :maxlength="formItem.maxlength || '150'"
241
- :show-word-limit="
242
- formItem.showWordLimit === null || formItem.showWordLimit === undefined
243
- ? true
244
- : formItem.showWordLimit
245
- "
246
- v-bind="{ rows: 4, ...formItem }"
247
- :clearable="formItem.clearable === false ? false : true"
248
- @change="(value) => $emit('valueChange', value, key)"
249
- />
250
- <el-button
251
- v-if="showBtn"
252
- type="primary"
253
- size="mini"
254
- class="m-l-s"
255
- @click="$emit('showBtnClick', formItem, key)"
256
- >{{ formItem.btnText || showBtnText }}</el-button
257
- >
258
- </template>
259
- <!--模糊搜索下拉-->
260
- <template v-else-if="formItem.type === 'searchSelect'">
261
- <el-select
262
- v-model="value[key]"
263
- :clearable="formItem.clearable === false ? false : true"
264
- class="remote-icon input-w"
265
- :multiple="formItem.isMultiple"
266
- collapse-tags
267
- filterable
268
- remote
269
- reserve-keyword
270
- :placeholder="$lc('请输入')"
271
- v-bind="formItem"
272
- :remote-method="
273
- (query) => formItem.remoteMethod && formItem.remoteMethod(query, { ...$attrs, item, value })
274
- "
275
- @change="(value) => $emit('valueChange', value, key)"
276
- >
277
- <el-option
278
- v-for="(item, index) in (formItem.asyncOptions && formItem.asyncOptions()) ||
279
- formItem.options ||
280
- []"
281
- :key="index + '' + (formItem.option ? item[formItem.option.value] : item.value)"
282
- :label="formItem.option ? item[formItem.option.label] : item.label || item.valueName"
283
- :value="formItem.option ? item[formItem.option.value] : item.value"
284
- @click.native="$emit('formItemClick', key, item)"
285
- >
286
- <slot :name="key" :option="item.label"></slot>
287
- </el-option>
288
- </el-select>
289
- </template>
290
- <!--带放大镜的输入框-->
291
- <template v-else-if="formItem.type === 'searchinput'">
292
- <cl-input-search
293
- v-model="value[key]"
294
- class="input-w"
295
- :placeholder="formItem.placeholder || formItem.label"
296
- :clearable="formItem.clearable === false ? false : true"
297
- v-bind="formItem"
298
- @search="$emit('formItemClick', key, 'search')"
299
- @change="(value) => $emit('valueChange', value, key)"
300
- />
301
- </template>
302
- <!--自定义-->
303
- <template v-else>
304
- <slot :name="formItem.type"></slot>
305
- </template>
52
+ <template v-for="(groupField, key) in formItem.fields">
53
+ <el-form-item
54
+ v-if="groupField.type"
55
+ v-bind="groupField"
56
+ :key="groupField.fieldCode"
57
+ :prop="groupField.fieldCode"
58
+ :label="groupField.fieldName"
59
+ :class="{
60
+ 'col-span-24': groupField.conType === 'textarea' || groupField.alone,
61
+ 'no-label': groupField.noLabel
62
+ }"
63
+ >
64
+ <!-- 表头插槽 - 支持 suggestInfo -->
65
+ <template slot="label" v-if="!groupField.noLabel">
66
+ <slot :name="'label_' + groupField.fieldCode">
67
+ <span>{{ groupField.fieldName }}</span>
68
+ <i
69
+ v-if="groupField.suggestInfo"
70
+ v-title:br="groupField.suggestInfo"
71
+ class="n20-icon-xinxitishi cur-point m-r-s"
72
+ ></i>
73
+ </slot>
306
74
  <i
307
- v-if="formItem.remark"
308
- v-title="formItem.remark"
75
+ v-if="groupField.remark"
76
+ v-title="groupField.remark"
309
77
  class="n20-icon-xinxitishi m-l-s"
310
78
  style="color: var(--color-text-secondary)"
311
79
  ></i>
312
80
  </template>
313
- </div>
314
- </template>
315
- <!--详情状态-->
316
- <template v-else>
317
- <slot :name="key" :value="formItem.value">
318
- <template v-if="formItem.type === 'input-number'">
319
- <span class="value">{{ formItem.value | toThousands }}</span>
81
+
82
+ <!-- 输入框 -->
83
+ <template v-if="!edit">
84
+ <span>{{ value[groupField.fieldCode] }}</span>
320
85
  </template>
321
- <template v-else-if="formItem.label === '子票区间'">
322
- <span>{{ formItem.value | formatCdRange }}</span>
86
+ <template v-else>
87
+ <div class="flex-box flex-v">
88
+ <!-- 插槽类型 - 优先检查 -->
89
+ <template v-if="groupField.type === 'slot'">
90
+ <slot :name="groupField.fieldCode" :formItem="groupField" />
91
+ </template>
92
+
93
+ <!-- 输入框 -->
94
+ <template v-else-if="groupField.conType === 'input'">
95
+ <el-input
96
+ v-model="value[groupField.fieldCode]"
97
+ v-title
98
+ type="text"
99
+ class="input-w"
100
+ :placeholder="groupField.placeholder || groupField.fieldName"
101
+ :clearable="groupField.clearable === false ? false : true"
102
+ :maxlength="groupField.maxlength || groupField.fieldLength || inputMaxLength"
103
+ :disabled="groupField.isEditable === 0 || groupField.disabled"
104
+ @change="(value) => $emit('valueChange', value, groupField.fieldCode)"
105
+ />
106
+ <el-button
107
+ v-if="showBtn"
108
+ type="primary"
109
+ size="mini"
110
+ class="m-l-s"
111
+ @click="$emit('showBtnClick', groupField, groupField.fieldCode)"
112
+ >
113
+ {{ groupField.btnText || showBtnText }}
114
+ </el-button>
115
+ </template>
116
+
117
+ <!-- 金额 -->
118
+ <template v-else-if="groupField.conType === 'input-number'">
119
+ <cl-input-number
120
+ v-model="value[groupField.fieldCode]"
121
+ class="input-w"
122
+ :is-clearable="groupField.isClearable"
123
+ :clearable="groupField.clearable === false ? false : true"
124
+ :disabled="groupField.isEditable === 0 || groupField.disabled"
125
+ :max="groupField.max || 9999999999.99"
126
+ :min="groupField.min || groupField.min === 0 ? groupField.min : 0"
127
+ :d-num="groupField.dNum || groupField.dNum === 0 ? groupField.dNum : 2"
128
+ :type="groupField.numberType || 'money'"
129
+ @change="(value) => $emit('valueChange', value, groupField.fieldCode)"
130
+ />
131
+ </template>
132
+
133
+ <!-- 金额区间 -->
134
+ <template v-else-if="groupField.conType === 'input-number-range'">
135
+ <cl-input-number-range
136
+ class="input-w"
137
+ :is-clearable="groupField.isClearable === false ? false : true"
138
+ :disabled="groupField.isEditable === 0 || groupField.disabled"
139
+ :start-value.sync="value[groupField.fieldCode][groupField['startKey'] || 'start']"
140
+ :end-value.sync="value[groupField.fieldCode][groupField['endKey'] || 'end']"
141
+ @change="
142
+ () =>
143
+ $emit(
144
+ 'valueChange',
145
+ {
146
+ start: value[groupField.fieldCode][groupField['startKey']],
147
+ end: value[groupField.fieldCode][groupField['endKey']]
148
+ },
149
+ groupField.fieldCode
150
+ )
151
+ "
152
+ />
153
+ </template>
154
+
155
+ <!-- 利率区间 -->
156
+ <template v-else-if="groupField.conType === 'input-rate-range'">
157
+ <cl-input-number-range
158
+ class="input-w"
159
+ :disabled="groupField.isEditable === 0 || groupField.disabled"
160
+ :is-clearable="groupField.isClearable === false ? false : true"
161
+ :start-value.sync="value[groupField.fieldCode][groupField['startKey'] || 'start']"
162
+ :end-value.sync="value[groupField.fieldCode][groupField['endKey'] || 'end']"
163
+ type="rate"
164
+ :min="0"
165
+ :max="100"
166
+ @change="
167
+ () =>
168
+ $emit(
169
+ 'valueChange',
170
+ {
171
+ start: value[groupField.fieldCode][groupField['startKey']],
172
+ end: value[groupField.fieldCode][groupField['endKey']]
173
+ },
174
+ groupField.fieldCode
175
+ )
176
+ "
177
+ />
178
+ </template>
179
+
180
+ <!-- 下拉框 -->
181
+ <template v-else-if="groupField.conType === 'select'">
182
+ <el-select
183
+ v-model="value[groupField.fieldCode]"
184
+ v-title
185
+ class="input-w"
186
+ :placeholder="
187
+ groupField.placeholder || (groupField.filterable ? $lc('请输入关键词') : groupField.fieldName)
188
+ "
189
+ :remote-method="
190
+ (query) =>
191
+ groupField.remoteMethod &&
192
+ groupField.remoteMethod(query, { ...$attrs, item: groupField, value })
193
+ "
194
+ :clearable="groupField.clearable === false ? false : true"
195
+ :multiple="false"
196
+ collapse-tags
197
+ :class="{ remote: groupField.remote, valuable: groupField.value }"
198
+ :disabled="groupField.isEditable === 0 || groupField.disabled"
199
+ @focus="(event) => groupField.focus && groupField.focus(event)"
200
+ @change="(value) => $emit('valueChange', value, groupField.fieldCode)"
201
+ >
202
+ <el-option
203
+ v-for="(item, index) in (groupField.asyncOptions && groupField.asyncOptions()) ||
204
+ parseGroupFieldEnumList(groupField) ||
205
+ groupField.options ||
206
+ []"
207
+ :key="index + '' + (groupField.option ? item[groupField.option.value] : item.value)"
208
+ :label="groupField.option ? item[groupField.option.label] : item.label || item.valueName"
209
+ :value="groupField.option ? item[groupField.option.value] : item.value"
210
+ @click.native="$emit('formItemClick', groupField.fieldCode, item)"
211
+ >
212
+ <div v-if="groupField.isSlot">
213
+ ({{ groupField.option ? item[groupField.option.value] : item.value }}){{
214
+ groupField.option ? item[groupField.option.label] : item.label
215
+ }}
216
+ </div>
217
+ </el-option>
218
+ <template v-if="groupField.remote" v-slot:prefix>
219
+ <i class="suffix-icon el-icon-search"></i>
220
+ </template>
221
+ </el-select>
222
+ </template>
223
+
224
+ <!-- 多选下拉框 -->
225
+ <template v-else-if="groupField.conType === 'selectMultiple'">
226
+ <el-select
227
+ v-model="value[groupField.fieldCode]"
228
+ class="input-w"
229
+ :placeholder="
230
+ groupField.placeholder || (groupField.filterable ? $lc('请输入关键词') : groupField.fieldName)
231
+ "
232
+ :remote-method="
233
+ (query) =>
234
+ groupField.remoteMethod &&
235
+ groupField.remoteMethod(query, { ...$attrs, item: groupField, value })
236
+ "
237
+ :clearable="groupField.clearable === false ? false : true"
238
+ :multiple="true"
239
+ collapse-tags
240
+ :class="{ remote: groupField.remote, valuable: groupField.value }"
241
+ :disabled="groupField.isEditable === 0 || groupField.disabled"
242
+ @focus="(event) => groupField.focus && groupField.focus(event)"
243
+ @change="(value) => $emit('valueChange', value, groupField.fieldCode)"
244
+ >
245
+ <el-option
246
+ v-for="(item, index) in (groupField.asyncOptions && groupField.asyncOptions()) ||
247
+ parseGroupFieldEnumList(groupField) ||
248
+ groupField.options ||
249
+ []"
250
+ :key="index + '' + (groupField.option ? item[groupField.option.value] : item.value)"
251
+ :label="groupField.option ? item[groupField.option.label] : item.label || item.valueName"
252
+ :value="groupField.option ? item[groupField.option.value] : item.value"
253
+ @click.native="$emit('formItemClick', groupField.fieldCode, item)"
254
+ >
255
+ <div v-if="groupField.isSlot">
256
+ ({{ groupField.option ? item[groupField.option.value] : item.value }}){{
257
+ groupField.option ? item[groupField.option.label] : item.label
258
+ }}
259
+ </div>
260
+ </el-option>
261
+ <template v-if="groupField.remote" v-slot:prefix>
262
+ <i class="suffix-icon el-icon-search"></i>
263
+ </template>
264
+ </el-select>
265
+ </template>
266
+
267
+ <!-- 日期选择 -->
268
+ <template v-else-if="groupField.conType === 'date-picker'">
269
+ <cl-date-picker
270
+ v-model="value[groupField.fieldCode]"
271
+ v-bind="groupField"
272
+ class="input-w"
273
+ type="date"
274
+ value-format="yyyy-MM-dd"
275
+ :placeholder="$lc('请选择日期')"
276
+ :clearable="groupField.clearable === false ? false : true"
277
+ :disabled="groupField.isEditable === 0 || groupField.disabled"
278
+ @change="(value) => $emit('valueChange', value, groupField.fieldCode)"
279
+ />
280
+ </template>
281
+
282
+ <!--日期时间段-->
283
+ <template v-else-if="groupField.conType === 'daterange'">
284
+ <cl-date-picker-por
285
+ :start-date.sync="value[groupField.fieldCode][groupField['startKey'] || 'start']"
286
+ :end-date.sync="value[groupField.fieldCode][groupField['endKey'] || 'end']"
287
+ type="daterange"
288
+ start-stop
289
+ value-format="yyyy-MM-dd"
290
+ v-bind="groupField"
291
+ :clearable="groupField.clearable === false ? false : true"
292
+ :disabled="groupField.isEditable === 0 || groupField.disabled"
293
+ @change="(value) => $emit('valueChange', value, groupField.fieldCode)"
294
+ />
295
+ </template>
296
+
297
+ <!--大文本-->
298
+ <template v-else-if="groupField.conType === 'textarea'">
299
+ <el-input
300
+ v-model="value[groupField.fieldCode]"
301
+ v-title
302
+ type="textarea"
303
+ :placeholder="$lc('请输入内容')"
304
+ :maxlength="groupField.maxlength || groupField.fieldLength || '150'"
305
+ :show-word-limit="
306
+ groupField.showWordLimit === null || groupField.showWordLimit === undefined
307
+ ? true
308
+ : groupField.showWordLimit
309
+ "
310
+ v-bind="{ rows: 4, ...groupField }"
311
+ :clearable="groupField.clearable === false ? false : true"
312
+ :disabled="groupField.isEditable === 0 || groupField.disabled"
313
+ @change="(value) => $emit('valueChange', value, groupField.fieldCode)"
314
+ />
315
+ <el-button
316
+ v-if="showBtn"
317
+ type="primary"
318
+ size="mini"
319
+ class="m-l-s"
320
+ @click="$emit('showBtnClick', groupField, groupField.fieldCode)"
321
+ >
322
+ {{ groupField.btnText || showBtnText }}
323
+ </el-button>
324
+ </template>
325
+
326
+ <!--模糊搜索下拉-->
327
+ <template v-else-if="groupField.conType === 'searchSelect'">
328
+ <el-select
329
+ v-model="value[groupField.fieldCode]"
330
+ :clearable="groupField.clearable === false ? false : true"
331
+ class="remote-icon input-w"
332
+ :multiple="groupField.isMultiple"
333
+ collapse-tags
334
+ filterable
335
+ remote
336
+ reserve-keyword
337
+ :placeholder="$lc('请输入')"
338
+ :disabled="groupField.isEditable === 0 || groupField.disabled"
339
+ v-bind="groupField"
340
+ :remote-method="
341
+ (query) =>
342
+ groupField.remoteMethod &&
343
+ groupField.remoteMethod(query, { ...$attrs, item: groupField, value })
344
+ "
345
+ @change="(value) => $emit('valueChange', value, groupField.fieldCode)"
346
+ >
347
+ <el-option
348
+ v-for="(item, index) in (groupField.asyncOptions && groupField.asyncOptions()) ||
349
+ parseGroupFieldEnumList(groupField) ||
350
+ groupField.options ||
351
+ []"
352
+ :key="index + '' + (groupField.option ? item[groupField.option.value] : item.value)"
353
+ :label="groupField.option ? item[groupField.option.label] : item.label || item.valueName"
354
+ :value="groupField.option ? item[groupField.option.value] : item.value"
355
+ @click.native="$emit('formItemClick', groupField.fieldCode, item)"
356
+ >
357
+ <slot :name="groupField.fieldCode" :option="item.label"></slot>
358
+ </el-option>
359
+ </el-select>
360
+ </template>
361
+
362
+ <!--带放大镜的输入框-->
363
+ <template v-else-if="groupField.conType === 'searchinput'">
364
+ <cl-input-search
365
+ v-model="value[groupField.fieldCode]"
366
+ class="input-w"
367
+ :placeholder="groupField.placeholder || groupField.fieldName"
368
+ :clearable="groupField.clearable === false ? false : true"
369
+ :disabled="groupField.isEditable === 0 || groupField.disabled"
370
+ v-bind="groupField"
371
+ @search="$emit('formItemClick', groupField.fieldCode, 'search')"
372
+ @change="(value) => $emit('valueChange', value, groupField.fieldCode)"
373
+ />
374
+ </template>
375
+ </div>
323
376
  </template>
324
- <span v-else class="value">
325
- {{ formItem.value | formatValue(formItem) }}
326
- </span>
327
- </slot>
377
+ </el-form-item>
328
378
  </template>
329
- </el-form-item>
379
+ </InfoCard>
380
+
381
+ <!-- 普通字段容器 -->
382
+ <template v-else>
383
+ <!-- 字段前块级插槽 -->
384
+ <div
385
+ v-if="$slots[`field-top-${key}`] || $scopedSlots[`field-top-${key}`]"
386
+ :key="`top-slot-${key}`"
387
+ class="field-block-slot field-top-slot"
388
+ >
389
+ <slot :name="`field-top-${key}`" :formItem="formItem" :fieldKey="key"></slot>
390
+ </div>
391
+
392
+ <!--只渲染明确输入类型的字段-->
393
+ <el-form-item
394
+ v-if="formItem.type"
395
+ :key="`field-${key}`"
396
+ :prop="key"
397
+ v-bind="formItem"
398
+ :class="{ 'col-span-24': formItem.type === 'textarea' || formItem.alone, 'no-label': formItem.noLabel }"
399
+ >
400
+ <!-- 表头插槽 -->
401
+ <template slot="label" v-if="!formItem.noLabel">
402
+ <slot :name="'label_' + key">
403
+ <span>{{ formItem.label }}</span>
404
+ </slot>
405
+ <i
406
+ v-if="formItem.remark"
407
+ v-title="formItem.remark"
408
+ class="n20-icon-xinxitishi m-l-s"
409
+ style="color: var(--color-text-secondary)"
410
+ ></i>
411
+ </template>
412
+ <!--编辑状态-->
413
+ <template v-if="edit">
414
+ <div class="flex-box flex-v">
415
+ <template
416
+ :disabled="formItem.tooltip && !formItem.tooltip.disabled && String(formItem.value) ? false : true"
417
+ :content="(formItem.tooltip && formItem.tooltip.content) || String(formItem.value)"
418
+ placement="top"
419
+ >
420
+ <!-- 插槽 -->
421
+ <template v-if="formItem.type === 'slot'">
422
+ <slot :name="key" :formItem="formItem"></slot>
423
+ </template>
424
+ <!--判断字段输入类型 如:input元素、select元素-->
425
+ <!-- 输入框 -->
426
+ <template v-if="formItem.type === 'input'">
427
+ <el-input
428
+ v-model="value[key]"
429
+ v-title
430
+ type="text"
431
+ class="input-w"
432
+ :placeholder="formItem.placeholder || formItem.label"
433
+ :clearable="formItem.clearable === false ? false : true"
434
+ :maxlength="formItem.maxlength || inputMaxLength"
435
+ v-bind="formItem"
436
+ @change="(value) => $emit('valueChange', value, key)"
437
+ />
438
+ <el-button
439
+ v-if="showBtn"
440
+ type="primary"
441
+ size="mini"
442
+ class="m-l-s"
443
+ @click="$emit('showBtnClick', formItem, key)"
444
+ >
445
+ {{ formItem.btnText || showBtnText }}
446
+ </el-button>
447
+ </template>
448
+ <!-- 金额 -->
449
+ <template v-else-if="formItem.type === 'input-number'">
450
+ <cl-input-number
451
+ v-model="value[key]"
452
+ class="input-w"
453
+ :is-clearable="formItem.isClearable"
454
+ :clearable="formItem.clearable === false ? false : true"
455
+ :disabled="formItem.disabled"
456
+ :max="formItem.max || 9999999999.99"
457
+ :min="formItem.min || formItem.min === 0 ? formItem.min : 0"
458
+ :d-num="formItem.dNum || formItem.dNum === 0 ? formItem.dNum : 2"
459
+ :type="formItem.numberType || 'money'"
460
+ @change="(value) => $emit('valueChange', value, key)"
461
+ />
462
+ </template>
463
+ <!-- 金额区间 -->
464
+ <template v-else-if="formItem.type === 'input-number-range'">
465
+ <cl-input-number-range
466
+ class="input-w"
467
+ :is-clearable="formItem.isClearable === false ? false : true"
468
+ :disabled="formItem.disabled"
469
+ :start-value.sync="value[key][formItem['startKey'] || 'start']"
470
+ :end-value.sync="value[key][formItem['endKey'] || 'end']"
471
+ @change="
472
+ () =>
473
+ $emit(
474
+ 'valueChange',
475
+ { start: value[formItem['startKey']], end: value[formItem['endKey']] },
476
+ key
477
+ )
478
+ "
479
+ />
480
+ </template>
481
+ <!-- 利率区间 -->
482
+ <template v-else-if="formItem.type === 'input-rate-range'">
483
+ <cl-input-number-range
484
+ class="input-w"
485
+ :disabled="formItem.disabled"
486
+ :is-clearable="formItem.isClearable === false ? false : true"
487
+ :start-value.sync="value[key][formItem['startKey'] || 'start']"
488
+ :end-value.sync="value[key][formItem['endKey'] || 'end']"
489
+ type="rate"
490
+ :min="0"
491
+ :max="100"
492
+ @change="
493
+ () =>
494
+ $emit(
495
+ 'valueChange',
496
+ { start: value[formItem['startKey']], end: value[formItem['endKey']] },
497
+ key
498
+ )
499
+ "
500
+ />
501
+ </template>
502
+ <!-- 下拉框 -->
503
+ <template v-else-if="formItem.type === 'select'">
504
+ <el-select
505
+ v-model="value[key]"
506
+ v-title
507
+ class="input-w"
508
+ :placeholder="
509
+ formItem.placeholder || (formItem.filterable ? $lc('请输入关键词') : formItem.label)
510
+ "
511
+ :remote-method="
512
+ (query) => formItem.remoteMethod && formItem.remoteMethod(query, { ...$attrs, item, value })
513
+ "
514
+ :clearable="formItem.clearable === false ? false : true"
515
+ :multiple="false"
516
+ collapse-tags
517
+ :class="{ remote: formItem.remote, valuable: formItem.value }"
518
+ v-bind="formItem"
519
+ @focus="(event) => formItem.focus && formItem.focus(event)"
520
+ @change="(value) => $emit('valueChange', value, key)"
521
+ >
522
+ <el-option
523
+ v-for="(item, index) in (formItem.asyncOptions && formItem.asyncOptions()) ||
524
+ formItem.options ||
525
+ []"
526
+ :key="index + '' + (formItem.option ? item[formItem.option.value] : item.value)"
527
+ :label="formItem.option ? item[formItem.option.label] : item.label || item.valueName"
528
+ :value="formItem.option ? item[formItem.option.value] : item.value"
529
+ @click.native="$emit('formItemClick', key, item)"
530
+ >
531
+ <div v-if="formItem.isSlot">
532
+ ({{ formItem.option ? item[formItem.option.value] : item.value }}){{
533
+ formItem.option ? item[formItem.option.label] : item.label
534
+ }}
535
+ </div>
536
+ </el-option>
537
+ <template v-if="formItem.remote" v-slot:prefix>
538
+ <i class="suffix-icon el-icon-search"></i>
539
+ </template>
540
+ </el-select>
541
+ </template>
542
+ <!-- 多选下拉框 -->
543
+ <template v-else-if="formItem.type === 'selectMultiple'">
544
+ <el-select
545
+ v-model="value[key]"
546
+ class="input-w"
547
+ :placeholder="
548
+ formItem.placeholder || (formItem.filterable ? $lc('请输入关键词') : formItem.label)
549
+ "
550
+ :remote-method="
551
+ (query) => formItem.remoteMethod && formItem.remoteMethod(query, { ...$attrs, item, value })
552
+ "
553
+ :clearable="formItem.clearable === false ? false : true"
554
+ :multiple="true"
555
+ collapse-tags
556
+ :class="{ remote: formItem.remote, valuable: formItem.value }"
557
+ v-bind="formItem"
558
+ @focus="(event) => formItem.focus && formItem.focus(event)"
559
+ @change="(value) => $emit('valueChange', value, key)"
560
+ >
561
+ <el-option
562
+ v-for="(item, index) in (formItem.asyncOptions && formItem.asyncOptions()) ||
563
+ formItem.options ||
564
+ []"
565
+ :key="index + '' + (formItem.option ? item[formItem.option.value] : item.value)"
566
+ :label="formItem.option ? item[formItem.option.label] : item.label || item.valueName"
567
+ :value="formItem.option ? item[formItem.option.value] : item.value"
568
+ @click.native="$emit('formItemClick', key, item)"
569
+ >
570
+ <div v-if="formItem.isSlot">
571
+ ({{ formItem.option ? item[formItem.option.value] : item.value }}){{
572
+ formItem.option ? item[formItem.option.label] : item.label
573
+ }}
574
+ </div>
575
+ </el-option>
576
+ <template v-if="formItem.remote" v-slot:prefix>
577
+ <i class="suffix-icon el-icon-search"></i>
578
+ </template>
579
+ </el-select>
580
+ </template>
581
+ <!-- 日期选择 -->
582
+ <template v-if="formItem.type === 'date-picker'">
583
+ <cl-date-picker
584
+ v-model="value[key]"
585
+ v-bind="formItem"
586
+ class="input-w"
587
+ type="date"
588
+ value-format="yyyy-MM-dd"
589
+ :placeholder="$lc('请选择日期')"
590
+ :clearable="formItem.clearable === false ? false : true"
591
+ @change="(value) => $emit('valueChange', value, key)"
592
+ />
593
+ </template>
594
+ <!--日期时间段-->
595
+ <template v-else-if="formItem.type === 'daterange'">
596
+ <cl-date-picker-por
597
+ :start-date.sync="value[key][formItem['startKey'] || 'start']"
598
+ :end-date.sync="value[key][formItem['endKey'] || 'end']"
599
+ type="daterange"
600
+ start-stop
601
+ value-format="yyyy-MM-dd"
602
+ v-bind="formItem"
603
+ :clearable="formItem.clearable === false ? false : true"
604
+ @change="(value) => $emit('valueChange', value, key)"
605
+ />
606
+ </template>
607
+ <!--大文本-->
608
+ <template v-else-if="formItem.type === 'textarea'">
609
+ <el-input
610
+ v-model="value[key]"
611
+ v-title
612
+ type="textarea"
613
+ :placeholder="$lc('请输入内容')"
614
+ :maxlength="formItem.maxlength || '150'"
615
+ :show-word-limit="
616
+ formItem.showWordLimit === null || formItem.showWordLimit === undefined
617
+ ? true
618
+ : formItem.showWordLimit
619
+ "
620
+ v-bind="{ rows: 4, ...formItem }"
621
+ :clearable="formItem.clearable === false ? false : true"
622
+ @change="(value) => $emit('valueChange', value, key)"
623
+ />
624
+ <el-button
625
+ v-if="showBtn"
626
+ type="primary"
627
+ size="mini"
628
+ class="m-l-s"
629
+ @click="$emit('showBtnClick', formItem, key)"
630
+ >
631
+ {{ formItem.btnText || showBtnText }}
632
+ </el-button>
633
+ </template>
634
+ <!--模糊搜索下拉-->
635
+ <template v-else-if="formItem.type === 'searchSelect'">
636
+ <el-select
637
+ v-model="value[key]"
638
+ :clearable="formItem.clearable === false ? false : true"
639
+ class="remote-icon input-w"
640
+ :multiple="formItem.isMultiple"
641
+ collapse-tags
642
+ filterable
643
+ remote
644
+ reserve-keyword
645
+ :placeholder="$lc('请输入')"
646
+ v-bind="formItem"
647
+ :remote-method="
648
+ (query) => formItem.remoteMethod && formItem.remoteMethod(query, { ...$attrs, item, value })
649
+ "
650
+ @change="(value) => $emit('valueChange', value, key)"
651
+ >
652
+ <el-option
653
+ v-for="(item, index) in (formItem.asyncOptions && formItem.asyncOptions()) ||
654
+ formItem.options ||
655
+ []"
656
+ :key="index + '' + (formItem.option ? item[formItem.option.value] : item.value)"
657
+ :label="formItem.option ? item[formItem.option.label] : item.label || item.valueName"
658
+ :value="formItem.option ? item[formItem.option.value] : item.value"
659
+ @click.native="$emit('formItemClick', key, item)"
660
+ >
661
+ <slot :name="key" :option="item.label"></slot>
662
+ </el-option>
663
+ </el-select>
664
+ </template>
665
+ <!--带放大镜的输入框-->
666
+ <template v-else-if="formItem.type === 'searchinput'">
667
+ <cl-input-search
668
+ v-model="value[key]"
669
+ class="input-w"
670
+ :placeholder="formItem.placeholder || formItem.label"
671
+ :clearable="formItem.clearable === false ? false : true"
672
+ v-bind="formItem"
673
+ @search="$emit('formItemClick', key, 'search')"
674
+ @change="(value) => $emit('valueChange', value, key)"
675
+ />
676
+ </template>
677
+ <!--自定义-->
678
+ <template v-else>
679
+ <slot :name="formItem.type"></slot>
680
+ </template>
681
+ </template>
682
+ </div>
683
+ </template>
684
+ <!--详情状态-->
685
+ <template v-else>
686
+ <slot :name="key" :value="formItem.value">
687
+ <template v-if="formItem.type === 'input-number'">
688
+ <span class="value">{{ formItem.value | toThousandsFormat }}</span>
689
+ </template>
690
+ <template v-else-if="formItem.label === '子票区间'">
691
+ <span>{{ formItem.value | formatCdRangeFormat }}</span>
692
+ </template>
693
+ <span v-else class="value">
694
+ {{ formItem.value | formatValue(formItem) }}
695
+ </span>
696
+ </slot>
697
+ </template>
698
+ </el-form-item>
699
+
700
+ <!-- 字段后块级插槽 -->
701
+ <div v-if="$slots[`field-bottom-${key}`]" :key="`bottom-${key}`" class="field-block-slot field-bottom-slot">
702
+ <slot :name="`field-bottom-${key}`" :formItem="formItem" :fieldKey="key"></slot>
703
+ </div>
704
+
705
+ <!-- 按索引位置插入的块级插槽 - 在字段之后 -->
706
+ <div
707
+ v-if="$slots[`field-after-index-${index}`]"
708
+ :key="`after-index-${index}`"
709
+ class="field-block-slot field-after-index-slot"
710
+ >
711
+ <slot :name="`field-after-index-${index}`" :formItem="formItem" :fieldKey="key"></slot>
712
+ </div>
713
+ </template>
330
714
  </template>
715
+
716
+ <!-- 当前 tab 底部块级插槽 -->
717
+ <div v-if="$slots[`tab-bottom-${index}`]" class="tab-block-slot tab-bottom-slot">
718
+ <slot :name="`tab-bottom-${index}`"></slot>
719
+ </div>
331
720
  </div>
332
721
  </cl-anchor-item>
333
722
  <slot name="bottom"></slot>
@@ -336,11 +725,13 @@
336
725
 
337
726
  <script>
338
727
  import clAnchorItem from '../Anchor/AnchorItem.vue'
339
- import clInputNumber from '../InputNumber/index.vue'
340
- import clInputNumberRange from '../InputNumber/numberRange.vue'
341
728
  import clDatePicker from '../DatePicker/index.vue'
342
729
  import clDatePickerPor from '../DatePicker/por.vue'
730
+ import InfoCard from '../InfoCard/index.vue'
731
+ import clInputNumber from '../InputNumber/index.vue'
732
+ import clInputNumberRange from '../InputNumber/numberRange.vue'
343
733
  import clInputSearch from '../InputSearch/index.vue'
734
+
344
735
  /*
345
736
  1.初始化值 是否能出发change事件
346
737
  2.当前表单change 触发其他表单的修改(未在自身作用域)
@@ -348,7 +739,15 @@ import clInputSearch from '../InputSearch/index.vue'
348
739
  */
349
740
  export default {
350
741
  name: 'DynamicField',
351
- components: { clInputSearch, clDatePickerPor, clDatePicker, clAnchorItem, clInputNumber, clInputNumberRange },
742
+ components: {
743
+ clInputSearch,
744
+ clDatePickerPor,
745
+ clDatePicker,
746
+ clAnchorItem,
747
+ clInputNumber,
748
+ clInputNumberRange,
749
+ InfoCard
750
+ },
352
751
  filters: {
353
752
  formatValue(value, row) {
354
753
  switch (row.type) {
@@ -408,7 +807,7 @@ export default {
408
807
  },
409
808
  labelPosition: {
410
809
  type: String,
411
- default: 'right'
810
+ default: 'top'
412
811
  },
413
812
  disabled: {
414
813
  type: Boolean,
@@ -448,6 +847,33 @@ export default {
448
847
  // 清空表单验证
449
848
  clearValidate() {
450
849
  return this.$refs.form.clearValidate()
850
+ },
851
+ // 检查组内是否有有效字段
852
+ hasValidGroupFields(fields) {
853
+ // 只要分组中有字段定义就显示,即使某些字段被业务逻辑隐藏
854
+ // 这样可以避免因为所有字段type为空导致整个分组容器不显示
855
+ if (!fields || fields.length === 0) {
856
+ return false
857
+ }
858
+ // 检查是否有任何具有fieldCode的字段(表示这是一个有效的字段定义)
859
+ const validFields = fields.filter((field) => field.fieldCode || field.type)
860
+ return validFields.length > 0
861
+ },
862
+ // 解析分组字段的枚举列表
863
+ // 注意:无 enumList 时必须返回 falsy 值(null),不能返回 [],
864
+ // 否则在 el-option 的 v-for 中 `... || parseGroupFieldEnumList(field) || groupField.options`
865
+ // 空数组是 truthy 会短路,导致 groupField.options(动态设置的注册节点等下拉项)永远不渲染
866
+ parseGroupFieldEnumList(field) {
867
+ if (!field?.enumList) return null
868
+ try {
869
+ return JSON.parse(field.enumList).map(({ name, code }) => ({
870
+ label: name,
871
+ value: code
872
+ }))
873
+ } catch (e) {
874
+ console.error('解析枚举列表失败:', e)
875
+ return null
876
+ }
451
877
  }
452
878
  }
453
879
  }