web-component-gallery 2.0.21 → 2.0.23
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/dist/js.umd.js +1 -1
- package/lib/form-comp/ASelectCustom.vue +140 -74
- package/lib/modal/style/index.less +1 -0
- package/lib/model/Model.js +68 -50
- package/lib/model/utils/render.js +71 -48
- package/lib/table/index.vue +4 -4
- package/package.json +1 -1
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
<Select
|
|
12
12
|
show-search
|
|
13
13
|
:style="{ width: width || '100%' }"
|
|
14
|
-
:mode="mode"
|
|
14
|
+
:mode="mode"
|
|
15
15
|
:open="isSelectOpen"
|
|
16
16
|
:placeholder="placeholder"
|
|
17
|
-
:filterOption="false"
|
|
17
|
+
:filterOption="listPageHandler ? false : filterOption"
|
|
18
18
|
:getPopupContainer="tirggerNode => tirggerNode.parentNode"
|
|
19
19
|
@select="handleSelect"
|
|
20
20
|
@search="handleSearch"
|
|
21
21
|
@popupScroll="handleScroll"
|
|
22
22
|
v-bind="attrs"
|
|
23
|
-
v-model="
|
|
23
|
+
v-model="innerValue"
|
|
24
24
|
>
|
|
25
25
|
<!-- v-on="$listeners" -->
|
|
26
26
|
<template v-for="(index, name) in $slots" v-slot:[name]>
|
|
@@ -28,16 +28,16 @@
|
|
|
28
28
|
</template>
|
|
29
29
|
<template v-for="(index, name) in $scopedSlots" v-slot:[name]="data">
|
|
30
30
|
<slot :name="name" v-bind="data"></slot>
|
|
31
|
-
</template>
|
|
31
|
+
</template>
|
|
32
32
|
<div slot="dropdownRender" slot-scope="menu">
|
|
33
|
-
<Checkbox v-if="mode" :checked="
|
|
33
|
+
<Checkbox v-if="mode" :checked="checkedAll" @change="handleSelectAll">全选</Checkbox>
|
|
34
34
|
<v-nodes :vnodes="menu" />
|
|
35
35
|
<div v-if="isLoadingMore" class="loading-more">
|
|
36
36
|
<Icon type="loading" />
|
|
37
37
|
加载中...
|
|
38
38
|
</div>
|
|
39
39
|
</div>
|
|
40
|
-
<SelectOption v-for="(item, index) in
|
|
40
|
+
<SelectOption v-for="(item, index) in innerOptions" :key="index" :value="item[valueKey]">
|
|
41
41
|
{{ customLabel ? customLabelHandler(item) : item[labelKey] }}
|
|
42
42
|
</SelectOption>
|
|
43
43
|
</Select>
|
|
@@ -61,53 +61,72 @@ export default {
|
|
|
61
61
|
Checkbox
|
|
62
62
|
},
|
|
63
63
|
props: {
|
|
64
|
+
// 基础配置
|
|
64
65
|
value: {
|
|
65
66
|
type: [String, Number, Array, Boolean, Object],
|
|
66
67
|
default: undefined
|
|
67
68
|
},
|
|
68
|
-
// 是否多选
|
|
69
69
|
mode: String,
|
|
70
|
-
// 是否支持输入保留字符
|
|
71
70
|
isInput: {
|
|
72
71
|
type: Boolean,
|
|
73
72
|
default: false
|
|
74
73
|
},
|
|
75
|
-
// 提示文字
|
|
76
74
|
placeholder: {
|
|
77
75
|
type: String,
|
|
78
76
|
default: '请选择'
|
|
79
77
|
},
|
|
80
78
|
width: String,
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
|
|
80
|
+
// 数据映射
|
|
83
81
|
valueKey: {
|
|
84
82
|
type: String,
|
|
85
83
|
default: 'value'
|
|
86
84
|
},
|
|
87
85
|
labelKey: {
|
|
88
86
|
type: String,
|
|
89
|
-
default: 'label'
|
|
87
|
+
default: 'label'
|
|
90
88
|
},
|
|
89
|
+
customLabel: String,
|
|
90
|
+
|
|
91
91
|
// 数据源
|
|
92
92
|
options: {
|
|
93
93
|
type: Array,
|
|
94
|
-
default: () =>
|
|
94
|
+
default: () => []
|
|
95
|
+
},
|
|
96
|
+
listPageHandler: Function,
|
|
97
|
+
|
|
98
|
+
// 分页配置
|
|
99
|
+
pSize: {
|
|
100
|
+
type: Number,
|
|
101
|
+
default: 50
|
|
95
102
|
},
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
searchKey: {
|
|
104
|
+
type: String,
|
|
105
|
+
default: 'keyword'
|
|
106
|
+
}
|
|
100
107
|
},
|
|
108
|
+
|
|
101
109
|
data() {
|
|
102
110
|
return {
|
|
111
|
+
// 分页状态
|
|
112
|
+
pagination: {
|
|
113
|
+
current: 1,
|
|
114
|
+
size: 10,
|
|
115
|
+
total: 0
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
// 当前数据
|
|
119
|
+
innerOptions: [],
|
|
120
|
+
|
|
121
|
+
// UI状态
|
|
103
122
|
isSelectOpen: false,
|
|
104
|
-
currentPage: 1,
|
|
105
123
|
isLoadingMore: false,
|
|
106
|
-
|
|
124
|
+
searchValue: ''
|
|
107
125
|
}
|
|
108
126
|
},
|
|
127
|
+
|
|
109
128
|
computed: {
|
|
110
|
-
|
|
129
|
+
innerValue: {
|
|
111
130
|
get() {
|
|
112
131
|
return this.value
|
|
113
132
|
},
|
|
@@ -115,7 +134,7 @@ export default {
|
|
|
115
134
|
this.$emit('input', val)
|
|
116
135
|
}
|
|
117
136
|
},
|
|
118
|
-
attrs() {
|
|
137
|
+
attrs() {
|
|
119
138
|
return {
|
|
120
139
|
allowClear: true,
|
|
121
140
|
showSearch: true,
|
|
@@ -123,95 +142,142 @@ export default {
|
|
|
123
142
|
}
|
|
124
143
|
},
|
|
125
144
|
hasMore() {
|
|
126
|
-
return this.
|
|
145
|
+
return this.innerOptions.length < this.pagination.total
|
|
127
146
|
},
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
147
|
+
checkedAll() {
|
|
148
|
+
return this.innerValue?.length === this.innerOptions?.length
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
watch: {
|
|
153
|
+
options: {
|
|
154
|
+
handler(newVal) {
|
|
155
|
+
this.innerOptions = [...newVal]
|
|
131
156
|
},
|
|
132
|
-
|
|
133
|
-
this.$emit('input', val)
|
|
134
|
-
}
|
|
157
|
+
immediate: true
|
|
135
158
|
}
|
|
136
159
|
},
|
|
160
|
+
|
|
137
161
|
created() {
|
|
138
|
-
this.debouncedSearch = debounce(this.handleSearch, 800)
|
|
139
|
-
this.debouncedLoadMore = debounce(this.loadMoreData, 300)
|
|
162
|
+
this.debouncedSearch = debounce(this.handleSearch, 800, { leading: true, trailing: false })
|
|
163
|
+
this.debouncedLoadMore = debounce(this.loadMoreData, 300, { leading: false, trailing: true })
|
|
140
164
|
},
|
|
165
|
+
|
|
141
166
|
mounted() {
|
|
142
|
-
|
|
167
|
+
this.pagination.size = this.pSize
|
|
168
|
+
this.fetchData()
|
|
169
|
+
document.addEventListener('click', this.handleDocumentClick)
|
|
143
170
|
},
|
|
171
|
+
|
|
144
172
|
beforeDestroy() {
|
|
145
|
-
document.removeEventListener('click', this.
|
|
173
|
+
document.removeEventListener('click', this.handleDocumentClick)
|
|
174
|
+
// 清理debounce定时器
|
|
175
|
+
this.debouncedSearch.cancel()
|
|
176
|
+
this.debouncedLoadMore.cancel()
|
|
146
177
|
},
|
|
178
|
+
|
|
147
179
|
methods: {
|
|
180
|
+
async fetchData() {
|
|
181
|
+
if (!this.listPageHandler) return
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
this.isLoadingMore = true
|
|
185
|
+
const { total, records } = await this.listPageHandler({
|
|
186
|
+
...this.pagination,
|
|
187
|
+
[this.searchKey]: this.searchValue
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
this.pagination.current === 1 ?
|
|
191
|
+
this.innerOptions = [...records] :
|
|
192
|
+
this.innerOptions = [...this.innerOptions, ...records]
|
|
193
|
+
|
|
194
|
+
this.pagination.total = total
|
|
195
|
+
} catch (error) {
|
|
196
|
+
this.pagination.current--
|
|
197
|
+
console.error('分页加载失败:', error)
|
|
198
|
+
} finally {
|
|
199
|
+
this.isLoadingMore = false
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
|
|
148
203
|
// 下拉滚动加载更多
|
|
149
204
|
handleScroll(e) {
|
|
150
205
|
const { scrollTop, scrollHeight, clientHeight } = e.target
|
|
151
206
|
const reachBottom = scrollTop + clientHeight >= scrollHeight - 10
|
|
152
207
|
if (reachBottom && !this.isLoadingMore && this.hasMore) this.debouncedLoadMore()
|
|
153
208
|
},
|
|
154
|
-
|
|
209
|
+
|
|
210
|
+
// 搜索事件处理
|
|
155
211
|
handleSearch(value) {
|
|
156
|
-
this.
|
|
157
|
-
this.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
212
|
+
this.searchValue = value
|
|
213
|
+
this.pagination.current = 1
|
|
214
|
+
|
|
215
|
+
this.fetchData()
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
// 本地搜索
|
|
219
|
+
filterOption(input, option) {
|
|
220
|
+
this.isInput && (this.innerValue = input)
|
|
221
|
+
return option.componentOptions.children[0].text
|
|
222
|
+
.toLowerCase()
|
|
223
|
+
.indexOf(input.toLowerCase()) >= 0
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
// 点击选择器外内容关闭下拉
|
|
227
|
+
handleDocumentClick(e) {
|
|
169
228
|
if (this.$refs.main && !this.$refs.main.contains(e.target)) this.isSelectOpen = false
|
|
170
229
|
},
|
|
171
|
-
|
|
230
|
+
|
|
231
|
+
// 安全的自定义label处理
|
|
172
232
|
customLabelHandler(item) {
|
|
233
|
+
if (!this.customLabel) return item[this.labelKey]
|
|
234
|
+
|
|
173
235
|
try {
|
|
174
|
-
//
|
|
175
|
-
const
|
|
176
|
-
|
|
236
|
+
// 使用安全的模板字符串解析
|
|
237
|
+
const template = this.customLabel
|
|
238
|
+
.replace(/{{([^{}]+)}}/g, (match, key) => {
|
|
239
|
+
const path = key.trim().split('.')
|
|
240
|
+
let value = item
|
|
241
|
+
for (const p of path) {
|
|
242
|
+
if (value == null) return ''
|
|
243
|
+
value = value[p]
|
|
244
|
+
}
|
|
245
|
+
return value == null ? '' : value
|
|
246
|
+
})
|
|
247
|
+
return template
|
|
177
248
|
} catch (e) {
|
|
178
249
|
console.error('自定义label解析错误:', e)
|
|
179
250
|
return item[this.labelKey]
|
|
180
251
|
}
|
|
181
252
|
},
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const selectOptions =
|
|
186
|
-
|
|
253
|
+
|
|
254
|
+
// 选中全部选项
|
|
255
|
+
handleSelectAll({target}) {
|
|
256
|
+
const selectOptions = target.checked
|
|
257
|
+
? this.innerOptions.map(item => item[this.valueKey])
|
|
258
|
+
: []
|
|
259
|
+
|
|
187
260
|
this.isSelectOpen = false
|
|
261
|
+
this.$emit('input', selectOptions)
|
|
188
262
|
},
|
|
189
|
-
|
|
263
|
+
|
|
264
|
+
// 选中单个选项
|
|
190
265
|
handleSelect(value, option) {
|
|
191
266
|
if (!value) return
|
|
267
|
+
|
|
192
268
|
this.isSelectOpen = false
|
|
193
|
-
const selectedOption = this.
|
|
269
|
+
const selectedOption = this.innerOptions.find(
|
|
270
|
+
item => item[this.valueKey] === value
|
|
271
|
+
)
|
|
194
272
|
selectedOption && this.$emit('select', value, selectedOption)
|
|
195
273
|
},
|
|
196
|
-
|
|
197
|
-
|
|
274
|
+
|
|
275
|
+
// 加载更多数据
|
|
276
|
+
async loadMoreData() {
|
|
198
277
|
if (this.isLoadingMore || !this.hasMore) return
|
|
199
278
|
|
|
200
|
-
this.
|
|
201
|
-
this.
|
|
202
|
-
|
|
203
|
-
try {
|
|
204
|
-
this.$emit('load-more', {
|
|
205
|
-
keyword: this.searchKeyword,
|
|
206
|
-
current: this.currentPage,
|
|
207
|
-
size: this.pSize
|
|
208
|
-
})
|
|
209
|
-
} catch (error) {
|
|
210
|
-
this.currentPage--
|
|
211
|
-
console.error('加载更多失败:', error)
|
|
212
|
-
} finally {
|
|
213
|
-
this.isLoadingMore = false
|
|
214
|
-
}
|
|
279
|
+
this.pagination.current++
|
|
280
|
+
await this.fetchData()
|
|
215
281
|
}
|
|
216
282
|
}
|
|
217
283
|
}
|
package/lib/model/Model.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { FormModel } from 'ant-design-vue'
|
|
2
2
|
import { getFormWidth, setFormItemRule, setFormItem } from './utils/render'
|
|
3
|
+
import _ from 'lodash'
|
|
3
4
|
|
|
4
5
|
export default {
|
|
5
6
|
name: 'WModel',
|
|
6
7
|
model: {
|
|
7
8
|
prop: 'value',
|
|
8
9
|
event: 'Change:Model'
|
|
9
|
-
},
|
|
10
|
+
},
|
|
10
11
|
data() {
|
|
11
12
|
return {
|
|
12
13
|
formRules: {}
|
|
@@ -16,7 +17,7 @@ export default {
|
|
|
16
17
|
value: {
|
|
17
18
|
type: Object,
|
|
18
19
|
default: () => ({})
|
|
19
|
-
},
|
|
20
|
+
},
|
|
20
21
|
/* Form布局方式 */
|
|
21
22
|
layout: {
|
|
22
23
|
type: String,
|
|
@@ -39,7 +40,7 @@ export default {
|
|
|
39
40
|
/** 校验单独赋值延迟的字段 */
|
|
40
41
|
for (const k in this.value) {
|
|
41
42
|
Object.hasOwnProperty.call(this.formRules, k) && this.$refs.FormModel?.validateField(k)
|
|
42
|
-
}
|
|
43
|
+
}
|
|
43
44
|
return this.value
|
|
44
45
|
},
|
|
45
46
|
set(value) {
|
|
@@ -47,15 +48,13 @@ export default {
|
|
|
47
48
|
}
|
|
48
49
|
},
|
|
49
50
|
formAttrs() {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
delete attr.wrapperCol
|
|
58
|
-
)
|
|
51
|
+
const attr =
|
|
52
|
+
this.layout === 'vertical' ?
|
|
53
|
+
this.$attrs :
|
|
54
|
+
{
|
|
55
|
+
...getDefaultFormAttrs(),
|
|
56
|
+
...this.$attrs
|
|
57
|
+
}
|
|
59
58
|
return attr
|
|
60
59
|
},
|
|
61
60
|
filterSetting() {
|
|
@@ -76,58 +75,77 @@ export default {
|
|
|
76
75
|
this.$emit('update:refForm', this.$refs.FormModel)
|
|
77
76
|
},
|
|
78
77
|
methods: {
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
getDefaultFormAttrs() {
|
|
79
|
+
return {
|
|
80
|
+
labelCol: { span: 4 },
|
|
81
|
+
wrapperCol: { span: 20 }
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
renderSingleFormItem(h, props) {
|
|
86
|
+
return setFormItem.call(this, h, this.form, props)
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
renderDynamicFormItems(h, dynamicModel, props) {
|
|
90
|
+
// multipleConfig为配置的动态项数据
|
|
91
|
+
return dynamicModel.map((modelItem, key) => {
|
|
92
|
+
// 为动态项时重新定义绑定key、prop等来进行检验
|
|
93
|
+
props.multipleConfig.map((configItem) => {
|
|
94
|
+
const childAttrs = {
|
|
95
|
+
key,
|
|
96
|
+
prop: `${props.model}.${key}.${configItem.model}`,
|
|
97
|
+
style: getFormWidth.call(this, configItem, this.layoutSize ?? props.layoutSize),
|
|
98
|
+
rules: setFormItemRule.call(this, configItem, props),
|
|
99
|
+
parentModel: props.model
|
|
100
|
+
}
|
|
101
|
+
return setFormItem.call(this, h, modelItem, configItem, childAttrs)
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
setModelRender(h, props) {
|
|
81
107
|
const dynamicModel = this.form[props.model]
|
|
82
108
|
return (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
{/*
|
|
109
|
+
<div
|
|
110
|
+
class={props.multiple && ['MultipleForm']}
|
|
111
|
+
{...{ attrs: { style: getFormWidth.call(this, props, this.layoutSize, props.multiple ? 0 : 24) } }}
|
|
112
|
+
>
|
|
113
|
+
{/* 添加分层提示信息及其他隔层操作 */}
|
|
88
114
|
{this.$scopedSlots[`${props.model}Tips`] && this.$scopedSlots[`${props.model}Tips`](props)}
|
|
89
115
|
{
|
|
90
|
-
|
|
91
|
-
dynamicModel instanceof Array && props.multiple
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
/** 为动态项时重新定义绑定key、prop等来进行检验 */
|
|
95
|
-
/** 目前还有一个动态项layoutSize排版问题 */
|
|
96
|
-
const childAttrs = {
|
|
97
|
-
key: index,
|
|
98
|
-
prop: `${props.model}.${index}.${configItem.model}`,
|
|
99
|
-
style: getFormWidth.call(this, configItem, this.layoutSize ?? props.layoutSize),
|
|
100
|
-
rules: setFormItemRule.call(this, configItem, props),
|
|
101
|
-
parentModel: props.model
|
|
102
|
-
}
|
|
103
|
-
return setFormItem.call(this, h, modelItem, configItem, childAttrs)
|
|
104
|
-
} )
|
|
105
|
-
) :
|
|
106
|
-
setFormItem.call(this, h, this.form, props)
|
|
116
|
+
// multiple为动态多项(使用场景:处理动态增减表单
|
|
117
|
+
dynamicModel instanceof Array && props.multiple
|
|
118
|
+
? this.renderDynamicFormItems(h, dynamicModel, props)
|
|
119
|
+
: this.renderSingleFormItem(h, props)
|
|
107
120
|
}
|
|
108
121
|
{/* 使用场景:如另起一行添加其他额外信息及操作 */}
|
|
109
122
|
{this.$scopedSlots[`${props.model}Operate`] && this.$scopedSlots[`${props.model}Operate`](props)}
|
|
110
123
|
</div>
|
|
111
124
|
)
|
|
112
125
|
},
|
|
113
|
-
|
|
126
|
+
|
|
127
|
+
getFormWidth: _.memoize(function(props, layoutSize, multiple) {
|
|
128
|
+
return getFormWidth.call(this, props, layoutSize, multiple ? 24 : 0)
|
|
129
|
+
}),
|
|
130
|
+
|
|
114
131
|
formSubmit() {
|
|
115
132
|
return new Promise((resolve, reject) => {
|
|
116
133
|
this.$refs.FormModel.validate()
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
134
|
+
.then(resolve)
|
|
135
|
+
.catch(err => {
|
|
136
|
+
console.error('表单验证失败:', err)
|
|
137
|
+
this.$nextTick(() => {
|
|
138
|
+
const errorDiv = document.getElementsByClassName('has-error')
|
|
139
|
+
errorDiv[0].scrollIntoView({
|
|
140
|
+
behavior: "smooth",
|
|
141
|
+
block: "center"
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
})
|
|
127
145
|
})
|
|
128
146
|
}
|
|
129
147
|
},
|
|
130
|
-
render() {
|
|
148
|
+
render(h) {
|
|
131
149
|
const {layout, formAttrs, setModelRender} = this
|
|
132
150
|
|
|
133
151
|
return (
|
|
@@ -143,9 +161,9 @@ export default {
|
|
|
143
161
|
>
|
|
144
162
|
{
|
|
145
163
|
this.$slots.default ??
|
|
146
|
-
this.filterSetting.map(props => setModelRender(props))
|
|
164
|
+
this.filterSetting.map(props => setModelRender(h, props))
|
|
147
165
|
}
|
|
148
166
|
</FormModel>
|
|
149
167
|
)
|
|
150
168
|
}
|
|
151
|
-
}
|
|
169
|
+
}
|
|
@@ -3,81 +3,104 @@ import RenderComp from '../../form-comp/RenderComp.vue'
|
|
|
3
3
|
|
|
4
4
|
const FormModelItem = FormModel.Item
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
6
|
+
/**
|
|
7
|
+
* 根据布局大小计算表单宽度
|
|
8
|
+
* @param {Object} child - 表单项配置
|
|
9
|
+
* @param {number} layoutSize - 一行显示的表单项数量
|
|
10
|
+
* @param {number} [gap=24] - 表单项间距
|
|
11
|
+
* @returns {string} - 计算后的CSS样式
|
|
12
|
+
*/
|
|
13
|
+
export function getFormWidth(child, layoutSize, gap = 24) {
|
|
14
|
+
const width = (100 / layoutSize) * (child.size ?? 1)
|
|
15
|
+
if (this.layout === 'vertical') return `flex: 0 1 calc(${width}% - ${gap}px); margin-right: ${gap}px;`
|
|
16
|
+
return `flex: 0 1 ${width}%;`
|
|
17
|
+
}
|
|
12
18
|
|
|
13
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* 动态设置表单项验证规则
|
|
21
|
+
* @param {Object} node - 当前表单项配置
|
|
22
|
+
* @param {Object} [nodeParent={}] - 父表单项配置
|
|
23
|
+
* @returns {Array} - 验证规则数组
|
|
24
|
+
*/
|
|
14
25
|
export function setFormItemRule(node, nodeParent = {}) {
|
|
26
|
+
// 确定是否必填
|
|
15
27
|
const required = (node.required ?? nodeParent.required ?? this.formAttrs.required) ?? true
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const message =
|
|
19
|
-
node.attrs?.placeholder ||
|
|
28
|
+
|
|
29
|
+
// 生成提示信息
|
|
30
|
+
const message = node.attrs?.placeholder ||
|
|
20
31
|
(/(select|picker|radio|upload)/.test((node.is ?? 'Input').toLowerCase()) ? '请选择' : '请输入') +
|
|
21
|
-
|
|
32
|
+
(node.label || '')
|
|
22
33
|
|
|
23
|
-
|
|
34
|
+
// 基础规则
|
|
35
|
+
const baseRules = [
|
|
24
36
|
{
|
|
25
37
|
required,
|
|
26
38
|
message,
|
|
27
|
-
trigger: ['change', 'blur']
|
|
39
|
+
trigger: ['change', 'blur']
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
// 合并自定义规则
|
|
44
|
+
const customRules = (node.rules || []).filter(rule => {
|
|
45
|
+
if (rule.pattern) {
|
|
46
|
+
rule.pattern = new RegExp(rule.pattern)
|
|
28
47
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
i.pattern && (i.pattern = new RegExp(i.pattern))
|
|
32
|
-
return i
|
|
33
|
-
}))
|
|
48
|
+
return rule
|
|
49
|
+
})
|
|
34
50
|
|
|
35
|
-
return
|
|
51
|
+
return [...baseRules, ...customRules]
|
|
36
52
|
}
|
|
37
53
|
|
|
38
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* 动态渲染表单项
|
|
56
|
+
* @param {Function} h - Vue的createElement函数
|
|
57
|
+
* @param {Object} vModel - 表单数据对象
|
|
58
|
+
* @param {Object} child - 表单项配置
|
|
59
|
+
* @param {Object} [childAttrs] - 表单项属性
|
|
60
|
+
* @returns {VNode} - 渲染结果
|
|
61
|
+
*/
|
|
39
62
|
export function setFormItem(h, vModel, child, childAttrs) {
|
|
40
|
-
|
|
41
63
|
const {layout, layoutSize, $scopedSlots} = this
|
|
42
64
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
65
|
+
// 设置表单项属性
|
|
66
|
+
const formItemAttrs = childAttrs ?? {
|
|
67
|
+
key: child.model,
|
|
68
|
+
prop: child.model,
|
|
69
|
+
colon: !(layout === 'inline')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 生成插槽名称
|
|
73
|
+
const slotsName = (childAttrs?.parentModel ?? '') + child.model
|
|
50
74
|
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
// 渲染标签
|
|
76
|
+
const renderLabel = customLabel =>
|
|
77
|
+
customLabel ? customLabel(vModel) : <span>{child.label}</span>
|
|
53
78
|
|
|
54
|
-
|
|
79
|
+
// 渲染表单项内容
|
|
80
|
+
const renderContent = () => {
|
|
81
|
+
if ($scopedSlots[child.model]) return $scopedSlots[child.model](child)
|
|
82
|
+
return (
|
|
83
|
+
<RenderComp
|
|
84
|
+
v-model={vModel[child.model]}
|
|
85
|
+
component={child.is}
|
|
86
|
+
{...{ on: child.event, attrs: child.attrs }}
|
|
87
|
+
/>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
55
90
|
|
|
56
91
|
return (
|
|
57
92
|
<FormModelItem
|
|
58
93
|
class={[
|
|
59
|
-
{ FormLineVertical: child.size === layoutSize && layout === 'vertical' },
|
|
60
|
-
{ FormLine: child.size === layoutSize }
|
|
94
|
+
{ 'FormLineVertical': child.size === layoutSize && layout === 'vertical' },
|
|
95
|
+
{ 'FormLine': child.size === layoutSize }
|
|
61
96
|
]}
|
|
62
97
|
{...{ attrs: formItemAttrs }}
|
|
63
98
|
>
|
|
64
99
|
<template slot="label">
|
|
65
|
-
{
|
|
66
|
-
customLabel ?
|
|
67
|
-
customLabel(vModel) :
|
|
68
|
-
<span>{label}</span>
|
|
69
|
-
}
|
|
100
|
+
{renderLabel(child.customLabel)}
|
|
70
101
|
</template>
|
|
71
102
|
{/* 当前表单项可slot进行单独开发配置 (目前仅支持非动态增减情况) */}
|
|
72
|
-
{
|
|
73
|
-
$scopedSlots[child.model](child)
|
|
74
|
-
) : (
|
|
75
|
-
<RenderComp
|
|
76
|
-
v-model={vModel[child.model]}
|
|
77
|
-
component={child.is}
|
|
78
|
-
{...{ on: child.event, attrs: child.attrs }}
|
|
79
|
-
/>
|
|
80
|
-
)}
|
|
103
|
+
{renderContent()}
|
|
81
104
|
{/* 如若再某个model后需添加按钮等场景(例如地图的选址按钮 */}
|
|
82
105
|
{$scopedSlots[`${slotsName}Handle`] && $scopedSlots[`${slotsName}Handle`](formItemAttrs)}
|
|
83
106
|
</FormModelItem>
|