背景:系统中表单的使用频率高,为了减少表单页面的搭建而频繁编写标签,使用组件,通过配置参数实现表单的搭建,可以有效提高开发效率。
意义:提高开发效率,方便维护
代码:vue组件,可直接使用,内置input、select、radio、checkbox、datetimerange、text、slot等,可根据自己的系统丰富控件类型。
1. 组件封装
<template>
<el-form
ref="dynamicForm"
class="form-wrap"
:model="tempForm"
:rules="rules"
:label-width="labelWidth"
:label-position="labelPosition"
>
<div v-if="title" class="icon-title">{{ title }}</div>
<el-row :gutter="24">
<el-col
v-for="(item,index) in formConfig"
:key="index" :span="item.span"
>
<el-form-item
:label="item.label"
:prop="item.key"
:rules="item.rules"
:label-width="item.labelWidth"
>
<el-input
v-if="item.type === 'input'"
v-model.trim="tempForm[item.key]"
:clearable="item.clearable === undefined ? true : item.clearable"
:disabled="item.disabled"
:placeholder="item.placeholder || '请输入'"
:maxlength="item.maxlength"
:show-word-limit="item.maxlength &&!(item.hasOwnProperty('showWordLimit') && !item.showWordLimit)"
:show-password="item.showPassword"
@input="change(item)"
/>
<el-input
v-if="item.type === 'textarea'"
v-model="tempForm[item.key]"
type="textarea"
:disabled="item.disabled"
:placeholder="item.placeholder || '请输入'"
:maxlength="item.maxlength"
:show-word-limit="item.maxlength &&!(item.hasOwnProperty('showWordLimit') && !item.showWordLimit)"
:rows="item.rows"
:autosize="item.autosize"
@input="change(item)"
/>
<el-select
v-else-if="item.type === 'select'"
v-model.trim="tempForm[item.key]"
:clearable="item.clearable === undefined ? true : item.clearable"
:disabled="item.disabled"
:placeholder="item.placeholder || '请选择'"
:collapse-tags="item.multiple || true"
:multiple="item.multiple || false"
:allow-create="item.allowCreate"
:filterable="!!(item.allowCreate || item.filterable)"
default-first-option
@change="change(item)"
>
<el-option
v-for="(option, selIndex) in item.options"
:key="selIndex"
:label="option.name"
:value="option.id"
/>
</el-select>
<el-cascader
v-else-if="item.type === 'cascader'"
v-model="tempForm[item.key]"
:placeholder="item.placeholder || '请选择'"
:options="item.options"
:show-all-levels="!!item.showAllLevels"
:clearable="item.clearable === undefined ? true : item.clearable"
filterable
@change="change(item)"
/>
<el-date-picker
v-else-if="['datetrange','date','datetime','datetimerange'].includes(item.type)"
v-model="tempForm[item.key]"
:type="item.type"
range-separator="至"
:start-placeholder="item.startPlaceholder || '开始日期'"
:end-placeholder="item.endPlaceholder || '结束日期'"
:clearable="item.clearable === undefined ? true : item.clearable"
:value-format="item.valueFormat || 'timestamp'"
:disabled="item.disabled"
:picker-options="item.pickerOptions"
@change="change(item)"
/>
<el-radio-group
v-else-if="item.type === 'radio'"
v-model="tempForm[item.key]"
@change="change(item)"
>
<el-radio
v-for="(radio,radioIndex) in item.options" :key="radioIndex"
:disabled="item.disabled" :label="radio.id"
>{{ radio.name }}</el-radio>
</el-radio-group>
<el-checkbox-group
v-else-if="item.type === 'checkbox'"
v-model="tempForm[item.key]"
@change="change(item)"
>
<el-checkbox
v-for="(checkbox,checkboxIndex) in item.options" :key="checkboxIndex"
:disabled="item.disabled" :label="checkbox.id"
>{{ checkbox.name }}</el-checkbox>
</el-checkbox-group>
<div v-else-if="item.type === 'inputNumber'" class="flex-between">
<el-input-number
v-model="tempForm[item.key]"
:controls-position="item.controlsPosition || 'right'" :min="item.min || 0"
:placeholder="item.placeholder || '请输入'"
:max="item.max" @change="change(item)"
/>
<div v-if="item.unit" class="ml-3">{{ item.unit }}</div>
</div>
<template v-else-if="item.type === 'text'">
{{ tempForm[item.key] }}
</template>
<template v-else-if="item.type === 'slot'">
<slot :name="item.slotName"/>
</template>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
export default {
name: 'DynamicForm',
model: {
prop: 'form',
event: 'change'
},
props: {
title: {
type: String,
default: ''
},
labelWidth: {
type: String,
default: '100px'
},
labelPosition: {
type: String,
default: ''
},
rules: {
type: Object,
default: () => {
return {}
}
},
formConfig: {
type: Array,
default: () => {
return []
}
},
form: {
type: Object,
default: () => {
return {}
}
}
},
watch: {
form: {
handler (val) {
this.tempForm = _.cloneDeep(val)
},
deep: true,
immediate: true
}
},
data () {
return {
tempForm: {}
}
},
methods: {
change (item) {
this.$nextTick(() => {
if (item && item.change) {
this.$emit(item.change, item, this.tempForm[item.key])
}
this.$emit('change', this.tempForm)
})
},
validate (callback) {
return this.$refs.dynamicForm.validate(callback)
},
clearValidate () {
this.$refs.dynamicForm.clearValidate()
},
validateField (field) {
this.$refs.dynamicForm.validateField(field)
},
resetFields () {
this.$refs.dynamicForm.resetFields()
this.$emit('change', this.tempForm)
}
}
}
</script>
<style lang="scss" scoped>
.form-wrap{
.unify-height{
height: 40px;
}
.el-cascader{
width: 100%;
}
/deep/.el-date-editor .el-range-separator{
min-width: 30px;
}
.el-date-editor{
width: 100%;
}
.el-input-number{
width: auto;
flex: 1;
}
/deep/.el-input{
.el-input__inner{
padding: 0 46px 0 15px;
}
&.el-date-editor{
.el-input__inner{
padding: 0 46px 0 30px;
}
}
}
}
</style>
2. 组件使用
// html
<DynamicForm
ref="dynamicForm"
title="DynamicForm演示"
v-model="form"
:rules="rules"
:form-config="formConfig"
></DynamicForm>
//参数配置
const componentConstant={
departmentOptions: [
{id: '研发部', name: '研发部'},
{id: '测试部', name: '测试部'},
{id: '财务部', name: '财务部'},
{id: '人事部', name: '人事部'}
],
genderOptions: [
{id: '0', name: '男'},
{id: '1', name: '女'}
],
technologyOptions: [
{id: 'vue', name: 'vue'},
{id: 'java', name: 'java'},
{id: 'webpack', name: 'webpack'}
]
}
form: {
technology: [],
time: []
},
formConfig: [
{label: '姓名', key: 'name', type: 'input'},
{label: '部门', key: 'department', type: 'select', options: componentConstant.departmentOptions},
{label: '性别', key: 'gender', type: 'radio', options: componentConstant.genderOptions},
{label: '技术', key: 'technology', type: 'checkbox', options: componentConstant.technologyOptions},
{label: '入职时间', key: 'time', type: 'datetimerange'},
{label: '描述', key: 'description', type: 'textarea', placeholder: '请输入描述,限制在500字以内', rows: 4, maxlength: 500, showWordLimit: true}
],
rules: {
name: [{required: true, message: '请输入姓名', trigger: 'blur'}],
gender: [{required: true, message: '请选择性别', trigger: 'blur'}],
department: [{required: true, message: '请选择部门', trigger: 'blur'}]
}
3.效果截图