lw-cdp-ui 1.1.27 → 1.1.28

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.
@@ -0,0 +1,454 @@
1
+ <template>
2
+ <el-skeleton v-if="renderLoading || Object.keys(form).length == 0"
3
+ animated />
4
+
5
+ <el-form v-else
6
+ ref="form"
7
+ :model="form"
8
+ :label-width="config.labelWidth"
9
+ :label-position="$i18n.locale == 'en-us' ? 'top' : config.labelPosition"
10
+ v-loading="loading"
11
+ :disabled="isView"
12
+ element-loading-text="Loading...">
13
+ <el-row :gutter="15">
14
+ <template v-for="(item, index) in config.formItems"
15
+ :key="index">
16
+ <el-col :span="item.span || 24"
17
+ v-if="!hideHandle(item)">
18
+ <!-- 间隔标题 -->
19
+ <div v-if="item.component == 'divider'"
20
+ class="title-name">
21
+ {{ item.label }}
22
+ <el-tooltip v-if="item.tips"
23
+ :content="item.tips">
24
+ <el-icon><el-icon-question-filled /></el-icon>
25
+ </el-tooltip>
26
+ </div>
27
+ <!-- 表单内容 -->
28
+ <el-form-item v-else
29
+ :prop="item.name"
30
+ :rules="rulesHandle(item)">
31
+ <template #label>
32
+ {{ item.label }}
33
+ <el-tooltip v-if="item.tips"
34
+ :content="item.tips">
35
+ <el-icon><el-icon-question-filled /></el-icon>
36
+ </el-tooltip>
37
+ </template>
38
+
39
+ <!-- input -->
40
+ <template v-if="item.component == 'input'">
41
+ <el-input v-model="form[item.name]"
42
+ :placeholder="item.options?.placeholder"
43
+ clearable
44
+ :type="item.options.type"
45
+ :disabled="item.options.disabled"
46
+ :maxlength="item.options.maxlength"
47
+ show-word-limit>
48
+ <template v-if="item.options.prepend"
49
+ #prepend>{{ item.options.prepend }}</template>
50
+ <template v-if="item.options.append"
51
+ #append>{{ item.options.append }}</template>
52
+ </el-input>
53
+ </template>
54
+ <!-- upload -->
55
+ <template v-else-if="item.component == 'upload'">
56
+ <template v-for="(_item, _index) in item.options.items"
57
+ :key="_index">
58
+ <lw-upload v-model="form[_item.name]"
59
+ :maxSize="_item.maxSize"
60
+ :accept="_item.accept"
61
+ :title="_item.label"
62
+ :parseData="item.parseData"
63
+ :disabled="item.options.disabled"
64
+ :apiObj="item.apiObj"></lw-upload>
65
+
66
+ </template>
67
+ </template>
68
+ <!-- checkbox -->
69
+ <template v-else-if="item.component == 'checkbox'">
70
+ <el-checkbox v-model="form[_item.name]"
71
+ :label="_item.label"
72
+ v-for="(_item, _index) in item.options.items"
73
+ :key="_index"></el-checkbox>
74
+ </template>
75
+ <!-- checkboxGroup -->
76
+ <template v-else-if="item.component == 'checkboxGroup'">
77
+ <el-checkbox-group v-model="form[item.name]">
78
+ <el-checkbox v-for="_item in item.options.items"
79
+ :key="_item.value"
80
+ :label="_item.value">{{ _item.label }}</el-checkbox>
81
+ </el-checkbox-group>
82
+ </template>
83
+
84
+ <!-- switch -->
85
+ <template v-else-if="item.component == 'switch'">
86
+ <el-switch v-model="form[item.name]"
87
+ inline-prompt
88
+ :active-text="item.options?.activeText"
89
+ :inactive-text="item.options?.inactiveText" />
90
+ </template>
91
+ <!-- select -->
92
+ <template v-else-if="item.component == 'select'">
93
+ <el-select v-model="form[item.name]"
94
+ :multiple="item.options?.multiple"
95
+ :allow-create="item.options?.allowCreate"
96
+ default-first-option
97
+ :placeholder="item.options?.placeholder || ''"
98
+ :clearable="item.options?.clearable"
99
+ :disabled="item.options.disabled"
100
+ filterable
101
+ style="width: 100%;">
102
+ <el-option v-for="option in item.options.items"
103
+ :key="option.value"
104
+ :label="option.label"
105
+ :value="option.value"></el-option>
106
+ </el-select>
107
+
108
+ </template>
109
+ <!-- cascader -->
110
+ <template v-else-if="item.component == 'cascader'">
111
+ <el-cascader v-model="form[item.name]"
112
+ style="width: 100%;"
113
+ :options="item.options.items"
114
+ clearable></el-cascader>
115
+ </template>
116
+ <!-- date -->
117
+ <template v-else-if="item.component == 'date'">
118
+ <el-date-picker v-model="form[item.name]"
119
+ style="width: 100%;"
120
+ :type="item.options.type"
121
+ :start-placeholder="item.options.startPlaceholder"
122
+ :end-placeholder="item.options.endPlaceholder"
123
+ :shortcuts="item.options.shortcuts"
124
+ :disabled-date="item.options.disabledDate"
125
+ :default-time="item.options.defaultTime"
126
+ :disabled="item.options.disabled"
127
+ :value-format="item.options.valueFormat"
128
+ :format="item.options.format"
129
+ :placeholder="item.options.placeholder || '请选择'"></el-date-picker>
130
+ </template>
131
+ <!-- number -->
132
+ <template v-else-if="item.component == 'number'">
133
+ <el-input-number v-model="form[item.name]"
134
+ :disabled="item.options?.disabled"
135
+ :min="item?.options?.min"
136
+ :max="item?.options?.max"
137
+ :step="item?.options?.step || 1"
138
+ :precision="item?.options?.precision"
139
+ :placeholder="item?.options?.placeholder || ''"
140
+ :controls-position="item?.options?.controlsPosition || 'right'">
141
+ <template v-if="item?.options?.suffix"
142
+ #suffix>
143
+ <span>{{ item.options.suffix }}</span>
144
+ </template>
145
+ </el-input-number>
146
+ </template>
147
+ <!-- radio -->
148
+ <template v-else-if="item.component == 'radio'">
149
+ <el-radio-group v-model="form[item.name]"
150
+ :disabled="item.options.disabled">
151
+ <el-radio v-for="_item in item.options.items"
152
+ :key="_item.value"
153
+ :label="_item.value">{{ _item.label }}</el-radio>
154
+ </el-radio-group>
155
+ </template>
156
+ <!-- color -->
157
+ <template v-else-if="item.component == 'color'">
158
+ <el-color-picker v-model="form[item.name]" />
159
+ </template>
160
+ <!-- rate -->
161
+ <template v-else-if="item.component == 'rate'">
162
+ <el-rate style="margin-top: 6px;"
163
+ v-model="form[item.name]"></el-rate>
164
+ </template>
165
+ <!-- slider -->
166
+ <template v-else-if="item.component == 'slider'">
167
+ <el-slider v-model="form[item.name]"
168
+ :marks="item.options.marks"></el-slider>
169
+ </template>
170
+
171
+ <!-- tags -->
172
+ <template v-else-if="item.component == 'tags'">
173
+ <div class="tags-list">
174
+ <el-tag v-for="tag in form[item.name]"
175
+ :key="tag"
176
+ closable
177
+ :disable-transitions="false"
178
+ @close="tagClose(tag, form[item.name])">
179
+ {{ tag }}
180
+ </el-tag>
181
+ <el-input v-if="tagVisible"
182
+ v-model="tagValue"
183
+ class="w-20"
184
+ size="small"
185
+ @keyup.enter="tagInputConfirm(item, form)"
186
+ @blur="tagInputConfirm(item, form)" />
187
+ <el-button v-else
188
+ class="button-new-tag"
189
+ size="small"
190
+ @click="tagVisible = true">
191
+ + 添加
192
+ </el-button>
193
+ </div>
194
+ </template>
195
+
196
+ <!-- 没有组件是component值 就是插槽名称 -->
197
+ <template v-else>
198
+ <slot :name="item.component"
199
+ :itemCur="item"
200
+ :formCur="form">
201
+ <el-tag type="danger">[{{ item.component }}]
202
+ 没有这个默认组件也未自定义插槽内容</el-tag>
203
+ </slot>
204
+ </template>
205
+ <div v-if="item.message"
206
+ class="el-form-item-msg">{{ item.message }}</div>
207
+ </el-form-item>
208
+ </el-col>
209
+ </template>
210
+ <el-col :span="24">
211
+ <el-form-item>
212
+ <slot>
213
+ <el-button type="primary"
214
+ @click="submit">提交</el-button>
215
+ </slot>
216
+ </el-form-item>
217
+ </el-col>
218
+ </el-row>
219
+ </el-form>
220
+ </template>
221
+
222
+ <script>
223
+ import lwUpload from '../lwUpload'
224
+ export default {
225
+ components: {
226
+ lwUpload
227
+ },
228
+ props: {
229
+ modelValue: { type: Object, default: () => { } },
230
+ /**
231
+ * 配置项
232
+ * {
233
+ * formItems: [{
234
+ * span: 24,
235
+ * component: 'input', // 可选项有 input, textarea, select, cascader, date, number, radio, checkbox, switch, color, rate, slider, tags, divider, upload, 自定义名称
236
+ * name: 'name', // 表单字段名称
237
+ * label: '名称', // 表单字段名称
238
+ * message: '提示信息', // 表单字段名称
239
+ * value: '1', // 表单字段值
240
+ * options: {
241
+ * name: 'name', // 表单字段名称
242
+ * type: 'input|', // 输入框类型
243
+ * placeholder: '请输入', // 表单字段名称
244
+ * disabled: false, // 表单字段名称
245
+ * maxlength: 10, // 表单字段名称
246
+ * append: '元', // 表单字段名称
247
+ * items: [{
248
+ * value: '1',
249
+ * label: '选项1'
250
+ * }, {
251
+ * value: '2',
252
+ * label: '选项2'
253
+ * }]
254
+ * }
255
+ * ...
256
+ * }]
257
+ * labelWidth: '100px', // 表单域标签的宽度
258
+ * labelPosition: 'top', // 表单域标签的位置,如果值为 left 或者 right 时,则需要设置 label-width
259
+ * ...
260
+ * }
261
+ * **/
262
+ config: { type: Object, default: () => { } },
263
+ /**
264
+ * 是否显示加载中
265
+ */
266
+ loading: { type: Boolean, default: false },
267
+ /**
268
+ * 是否是查看模式
269
+ */
270
+ isView: { type: Boolean, default: false },
271
+ },
272
+ data() {
273
+ return {
274
+ form: {},
275
+ tagValue: '',
276
+ tagVisible: false,
277
+ isChange: true,
278
+ renderLoading: false
279
+ }
280
+ },
281
+ watch: {
282
+ modelValue(val, old) {
283
+ if (this.hasConfig && this.isChange) {
284
+ this.form = this.flattenObject(val)
285
+ }
286
+ this.isChange = true;
287
+ },
288
+ config() {
289
+ this.render()
290
+ },
291
+ form: {
292
+ handler(val) {
293
+ this.$emit("update:modelValue", this.unflattenObject(val))
294
+ },
295
+ deep: true
296
+ }
297
+ },
298
+ computed: {
299
+ hasConfig() {
300
+ return Object.keys(this.config).length > 0
301
+ },
302
+ hasValue() {
303
+ return Object.keys(this.modelValue).length > 0
304
+ },
305
+
306
+ },
307
+ mounted() {
308
+ if (this.hasConfig) {
309
+ this.render()
310
+ }
311
+ },
312
+ methods: {
313
+ /**
314
+ * 渲染表单数据。
315
+ * 遍历配置中的表单项,根据不同组件类型初始化表单数据。
316
+ * 对于复选框和上传组件,将选项值存储在对象中。
317
+ * 对于其他组件,直接将值赋给表单数据。
318
+ * 如果存在当前值,则将其与表单数据进行深度合并。
319
+ */
320
+ render() {
321
+ this.form = {}
322
+ this.config.formItems.forEach((item) => {
323
+ if (item.component == 'checkbox' || item.component == 'upload') {
324
+ if (item.name) {
325
+ const value = {}
326
+ item.options.items.forEach((option) => {
327
+ value[option.name] = option.value
328
+ })
329
+ this.form[item.name] = value
330
+ } else if (item?.options?.items) {
331
+ item.options.items.forEach((option) => {
332
+ this.form[option.name] = option.value
333
+ })
334
+ }
335
+ } else {
336
+ this.form[item.name] = item.value
337
+ }
338
+ })
339
+
340
+ if (this.hasValue) {
341
+ this.form = this.flattenObject(this.modelValue)
342
+ }
343
+ },
344
+ flattenObject(obj, prefix = '') {
345
+ let result = {};
346
+
347
+ for (let key in obj) {
348
+ if (obj.hasOwnProperty(key)) {
349
+ const newKey = prefix ? `${prefix}.${key}` : key;
350
+
351
+ if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
352
+ // 递归处理嵌套对象
353
+ Object.assign(result, this.flattenObject(obj[key], newKey));
354
+ } else {
355
+ result[newKey] = obj[key];
356
+ }
357
+ }
358
+ }
359
+
360
+ return result;
361
+ },
362
+ unflattenObject(obj) {
363
+ const result = {};
364
+ this.isChange = false;
365
+
366
+ for (let key in obj) {
367
+ if (obj.hasOwnProperty(key)) {
368
+ const keys = key.split('.'); // 按 `.` 分割键名
369
+ keys.reduce((acc, part, index) => {
370
+ if (index === keys.length - 1) {
371
+ acc[part] = obj[key];
372
+ } else {
373
+ acc[part] = acc[part] || {}; // 如果没有该键,创建一个空对象
374
+ }
375
+ return acc[part];
376
+ }, result);
377
+ }
378
+ }
379
+
380
+ return result;
381
+ },
382
+ //处理动态隐藏
383
+ hideHandle(item) {
384
+ if (typeof item.hideHandle === 'string') {
385
+ // eslint-disable-next-line no-eval
386
+ const exp = eval(item.hideHandle.replace(/\$/g, "this.form"))
387
+ return exp
388
+ } else if (typeof item.hideHandle === 'boolean') {
389
+ return item.hideHandle
390
+ }
391
+ return false
392
+ },
393
+ //处理动态必填
394
+ rulesHandle(item) {
395
+ if (item.requiredHandle) {
396
+ // eslint-disable-next-line no-eval
397
+ const exp = eval(item.requiredHandle.replace(/\$/g, "this.form"))
398
+ var requiredRule = item.rules.find(t => 'required' in t)
399
+ requiredRule.required = exp
400
+ }
401
+ return item.rules
402
+ },
403
+ //数据验证
404
+ validate(valid, obj) {
405
+ return this.$refs.form.validate(valid, obj)
406
+ },
407
+ scrollToField(prop) {
408
+ return this.$refs.form.scrollToField(prop)
409
+ },
410
+ resetFields() {
411
+ return this.$refs.form.resetFields()
412
+ },
413
+ //提交
414
+ submit() {
415
+ this.$emit("submit", this.form)
416
+ },
417
+ // 删除tag
418
+ tagClose(tag, item) {
419
+ item.splice(item.indexOf(tag), 1)
420
+ },
421
+ // 增加tag
422
+ tagInputConfirm(item, row) {
423
+ if (this.tagValue) {
424
+ if (item?.options?.name) {
425
+ row[item.name] = row[item.name] || {};
426
+ row[item.name][item.options.name] = row[item.name][item.options.name] || [];
427
+ row[item.name][item.options.name].push(this.tagValue)
428
+ } else {
429
+ row[item.name] = row[item.name] || []
430
+ row[item.name].push(this.tagValue)
431
+ }
432
+ }
433
+ this.tagVisible = false
434
+ this.tagValue = ''
435
+ }
436
+ }
437
+ }
438
+ </script>
439
+ <style lang="scss" scoped>
440
+ .button-new-tag {
441
+ margin-left: 10px;
442
+ }
443
+
444
+ .w-20 {
445
+ width: 100px;
446
+ margin-left: 10px;
447
+ }
448
+
449
+ .title-name {
450
+ font-size: 18px;
451
+ font-weight: bold;
452
+ margin-bottom: 10px;
453
+ }
454
+ </style>