网上看了几篇文章,都不是想要的答案。于是给下自己的实现思路,各位有好的建议也可以评论。
建议直接看源码: https://github.com/jyj1202/j-admin/blob/dev/src/views/j-form/components/JForm.vue
如果觉得有用,可以去github给我的项目点个star。
核心思路就是利用vue的动态组件渲染需要的组件,而不是v-if判断。这样大大简化了代码。
模板部分代码:
<el-form-item
:label="col.label"
:prop="col.prop"
:rules="col.rules"
>
<slot :name="col.prop" v-bind="{col, size: getProp('size', col)}">
<component
:is="getComponentType(col, getProp('type', col))"
v-model="formData[col.prop]"
v-bind="getComponentProps(col)"
:key="col.prop"
@change="handleFormItemValueChange"
>
<template v-if="!col.component && getSlotComponent(getProp('type', col))">
<!-- 目前只有两种情况
1.有dicData的formItem
2.upload组件里的button
-->
<template v-if="col.dicData">
<component
v-for="dicData in col.dicData"
:key="dicData.value"
:is="getSlotComponent(getProp('type', col))"
v-bind="dicData"
/>
</template>
<el-button
v-if="getSlotComponent(getProp('type', col))=='el-button'"
type="primary"
>Click to upload
</el-button>
</template>
</component>
</slot>
</el-form-item>
需要注意的是,涉及到有插槽的组件,我目前的思路是:
1.条件判断,渲染对应的插槽内组件。
/**
* @description 获取组件插槽内子组件
* @param type
*/
const getSlotComponent = (type: string): string|undefined => {
/* 插槽子组件映射 */
const slotCompMap: Record<string, string> = {
'select': 'option',
'radio-group': 'radio',
'checkbox-group': 'checkbox',
'upload': 'button'
}
return `el-${slotCompMap[type]}`
}
2.二次封装该组件,用props配置项取代插槽,然后使用二次封装后的组件。
3.使用jsx写法,灵活的渲染插槽,但是需要用户传入插槽对应的jsx,提高了用户使用复杂度。
用户传入的表单配置数据解构:
const formOption = reactive<JFormOptionType>({
labelPosition: 'left',
column: [{
label: 'input',
prop: 'input',
type: 'input',
span: 8,
placeholder: 'try to input something',
rules: {
required: true,
message: "Please input something",
trigger: "blur"
}
}, {
label: 'input-number',
prop: 'input-number',
type: 'input-number',
min: 1,
max: 2,
span: 8,
}, {
label: 'password',
prop: 'password',
type: 'password',
showPassword: true,
span: 8
}, {
label: 'select',
prop: 'select',
type: 'select',
multiple: true,
placeholder: 'make a selection',
rules: [{
required: true,
message: "Please select something",
trigger: "blur"
}],
dicData: [{
label: 'select1',
value: 0
}, {
label: 'select2',
value: 1
}]
}, {
label: 'switch',
prop: 'switch',
type: 'switch',
inlinePrompt: true,
activeIcon: "Check",
inactiveIcon: "Close",
}, {
label: 'radio-group',
prop: 'radioGroup',
type: 'radio-group',
rules: {
required: true,
message: "Please select something",
trigger: "blur"
},
dicData: [{
label: 'radio1',
value: 0
}, {
label: 'radio2',
value: 1
}]
}, {
label: 'checkbox-group',
prop: 'checkbox-group',
type: 'checkbox-group',
dicData: [{
label: 'checkbox1',
value: 0
}, {
label: 'checkbox2',
value: 1
}]
}, {
label: 'date-picker',
prop: 'date-picker',
type: 'date',
rules: {
required: true,
message: "Please select date",
trigger: "blur"
},
}, {
label: 'rate',
prop: 'rate',
type: 'rate',
}, {
label: 'slider',
prop: 'slider',
type: 'slider',
span: 24,
formatTooltip: (val: number) => {
return val / 100
},
}, {
label: 'transfer',// FIXME:bug
prop: 'transfer',
type: 'transfer',
span: 24,
data: ref<{
key: number
label: string
disabled: boolean
}[]>([{
key: 1,
label: 'option 1',
disabled: false
}, {
key: 2,
label: 'option 2',
disabled: true
}, {
key: 3,
label: 'option 3',
disabled: false
}, ])
}]
})