项目介绍
在企业级后台系统中,表单是最高频的交互组件之一。有些后台管理系统用户希望可以自主的进行表单字段的配置,再结合前端开发,实现用户自己定义的表单字段配置。
此项目是一个基于 Element UI 的自定义表单组件库,提供了一系列可配置的表单组件,支持表单设计、配置和预览功能。
功能特点
- 支持多种表单组件类型
- 组件配置可视化
- 实时预览
- 表单验证
- 配置自定义组件
项目介绍
表单配置
如下图所示,实现表单配置的可视化,提供新增、编辑、删除表单项的功能。
新增表单项
新增表单项时,提供表单项的项目可选类型。
支持的表单配置项如下:
- 文本输入框 (Input Textarea)
- 数字输入框 (InputNumber)
- 开关 (Switch)
- 单选框 (Radio)
- 复选框 (Checkbox)
- 下拉选择框 (Select)
- 日期选择器 (DatePicker)
- 时间选择器 (TimePicker)
- 日期时间选择器 (DateTimePicker)
- 自定义组件(CustomItem)
表单展示
使用动态渲染is的方式渲染表单组件。
- 通过组件的
type类型去渲染不同的组件。 - 在
created钩子里面将表单数据的key通过$set的方式放入变量form对象中,以便后续数据的响应式。 - 当组件内部将值改变时,用
getValue方法获取最新的值。
<template>
<div>
<section class="tips" v-if="(formSettings || []).length === 0">请先在表单配置页面进行配置,然后即可展示配置的表单。</section>
<el-form ref="form" v-else :model="form" :rules="rules" label-width="200px">
<el-form-item
v-for="item in formSettings"
:key="item.enName"
:label="`${item.name}:`"
:prop="item.enName"
>
<component
:is="item.type !== 'CustomItem' ? `Cu${item.type}` : item.customName"
:item-settings="item"
:item-value="form[item.enName]"
@get-value="getValue"
></component>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('form')">提交</el-button>
<el-button type="primary" plain @click="clearValidate('form')">清空校验</el-button>
<el-button @click="resetForm('form')">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
computed: {
...mapState(['formSettings'])
},
data() {
return {
form: {},
rules: {},
}
},
created() {
this.formSettings.map((item) => {
// 处理表单的初始值
this.$set(this.form, item.enName, undefined);
// 处理表单验证
this.$set(this.rules, item.enName, []);
// 必填
if (item.isRequired) {
this.rules[item.enName].push({
required: true,
message: `请输入${item.name}`,
trigger: 'blur'
})
}
// 字数
if (item.isShowWordLimit) {
let message = '';
let limitLength = {}
if (item.maxlength && !item.minlength) {
limitLength.max = item.maxlength;
message = `长度不能超过${item.maxlength}个字符`;
}
if (!item.maxlength && item.minlength) {
limitLength.min = item.minlength;
message = `长度不能少于${item.minlength}个字符`;
}
if (item.maxlength && item.minlength) {
limitLength = {
max: item.maxlength,
min: item.minlength
}
message = `长度需要在${item.minlength}-${item.maxlength}个字符之间`;
}
this.rules[item.enName].push({
...limitLength,
message: message,
trigger: 'blur'
})
}
})
this.$nextTick(() => {
this.$refs.form && this.$refs.form.clearValidate();
})
},
methods: {
getValue(enName, value) {
this.form[enName] = value;
},
// 表单-提交
submitForm(formName) {
this.$refs[formName].validate((valid) => {
// 单独处理固定时间(范围)的必填验证
const timePickerRequiredList = this.formSettings.filter((item) => item.type === 'TimePicker' && item.timePickerType === 2 && item.isRange && item.isRequired);
timePickerRequiredList.forEach((item) => {
const targetValue = this.form[item.enName];
if (Array.isArray(targetValue) && (!targetValue[0] || !targetValue[1])) {
const targetField = this.$refs[formName].fields.find(field => field.prop === item.enName);
if (targetField) {
targetField.validateState = 'error';
targetField.validateMessage = '请输入' + item.name;
}
}
})
if (valid) {
alert('提交');
} else {
return false;
}
});
},
// 表单-重置
resetForm(formName) {
this.$refs[formName].resetFields();
},
// 表单-清空校验
clearValidate(formName) {
this.$refs[formName].clearValidate();
}
}
}
</script>
对每一个element-ui的组件进行二次封装,以Input组件为例:
- 组件内部设置
value变量,接收父组件传入的值,如果传入的值为undefined,就使用默认值。 - 监听
value值的变化,当value改变时,使用$emit向form派发最新的value值。
<template>
<el-input
v-model="value"
:disabled="itemSettings.isDisabled"
:clearable="itemSettings.isClearable"
:show-word-limit="itemSettings.isShowWordLimit"
:show-password="itemSettings.isShowPassword"
v-bind="attrs"
:placeholder="itemSettings.placeholder ? itemSettings.placeholder : `请输入${itemSettings.name}`"
>
<template v-if="itemSettings.prependSlot" slot="prepend">{{ itemSettings.prependSlot }}</template>
<template v-if="itemSettings.appendSlot" slot="append">{{ itemSettings.appendSlot }}</template>
</el-input>
</template>
<script>
export default {
name: 'CuInput',
props: ['itemSettings', 'itemValue'],
data() {
return {
attrs: {},
value: ''
}
},
watch: {
itemValue(newVal) {
this.value = newVal;
},
value() {
this.emitVal();
},
},
mounted() {
// 初始值处理
this.value = this.itemValue !== undefined ? this.itemValue : this.getDefaultValue();
this.emitVal();
// 字数属性处理
if (this.itemSettings.isLimitLength) {
// 最小字数
if (this.itemSettings.minlength) {
this.attrs.minlength = this.itemSettings.minlength;
}
// 最大字数
if (this.itemSettings.maxlength) {
this.attrs.maxlength = this.itemSettings.maxlength;
}
}
// 图标属性处理
if (this.itemSettings.prefixIcon) {
this.attrs['prefix-icon'] = this.itemSettings.prefixIcon;
}
if (this.itemSettings.suffixIcon) {
this.attrs['suffix-icon'] = this.itemSettings.suffixIcon;
}
},
methods: {
// 获取默认值
getDefaultValue() {
if (this.itemSettings.defaultValue) {
return this.itemSettings.defaultValue;
}
return '';
},
// 向Form派发value值
emitVal() {
this.$emit('get-value', this.itemSettings.enName, this.value);
}
}
}
</script>
使用说明
- 安装依赖
npm install
- 运行项目
npm run serve
- 构建项目
npm run build
项目结构
src/
├── components/
│ ├── FormItem/ # 表单组件
│ │ ├── CuInput.vue
│ │ ├── CuInputNumber.vue
│ │ ├── CuRadio.vue
│ │ ├── CuCheckbox.vue
│ │ ├── CuSelect.vue
│ │ ├── CuSwitch.vue
│ │ ├── CuTimePicker.vue
│ │ ├── CuDatePicker.vue
│ │ └── CuDateTimePicker.vue
│ └── FormSettings/ # 组件配置面板
│ ├── FormInput.vue
│ ├── FormInputNumber.vue
│ ├── FormRadio.vue
│ ├── FormCheckbox.vue
│ ├── FormSelect.vue
│ ├── FormSwitch.vue
│ ├── FormTimePicker.vue
│ ├── FormDatePicker.vue
│ └── FormDateTimePicker.vue
├── constants/ # 常量定义
└── utils/ # 工具函数
使用指南
配置表单
- 在表单配置页面中配置表单项
- 在表单展示中可以看到配置的表单项
配置自定义组件
- 在配置表单项的“项目类型”中选择“CustomItem 自定义组件”,自定义组件名称需要与组件的
name属性提供的组件名称一致 - 在
FormCustom目录下创建自定义组件文件
添加新组件
- 在
FormItem目录下创建组件文件 - 在
FormSettings目录下创建对应的配置面板 - 在组件中实现必要的属性和方法
- 在配置面板中实现组件的配置项
注意事项
- 所有组件都基于 Element UI 开发
- 组件配置面板中的验证规则需要根据实际需求进行配置
- 日期时间组件的格式需要符合 Element UI 的要求
- 数字输入框的精度和步长需要合理配置