后台管理系统中,经常会出现需要表单的情况,但是又不想每个页面都实现一个,这样不方便维护,因此封装了表单组件,方便在页面使用
<template>
<div>
<el-form
:model="formData"
:rules="rules"
ref="ruleForm"
:label-width="labelWidth"
:label-position="labelPosition"
>
<el-form-item
v-for="item in formConfig"
:label="item.label"
:prop="item.prop"
:key="item.label"
>
<el-input
v-if="item.type === 'input'"
v-model="formData[item.prop]"
:disabled="item.disabled"
:style="{ width: item.width }"
@change="item.change && item.change(formData[item.prop])"
clearable
>el-input>
<el-input
v-if="item.type === 'password'"
type="password"
v-model="formData[item.prop]"
autocomplete="off"
:style="{ width: item.width }"
@change="item.change && item.change(formData[item.prop])"
clearable
show-password
>el-input>
<el-input
v-if="item.type === 'textarea'"
type="textarea"
v-model="formData[item.prop]"
:disabled="item.disabled"
:style="{ width: item.width }"
:maxlength="item.maxlength"
:rows="item.rows"
:show-word-limit="item.showWordLimit"
@change="item.change && item.change(formData[item.prop])"
>el-input>
<el-select
v-if="item.type === 'select'"
v-model="formData[item.prop]"
:disabled="item.disabled"
:style="{ width: item.width }"
@change="item.change && item.change(formData[item.prop])"
clearable
>
<el-option
v-for="option in item.options"
:label="option.label"
:value="option.value"
:key="option.value"
>el-option>
el-select>
<el-radio-group
v-if="item.type === 'radio'"
v-model="formData[item.prop]"
:disabled="item.disabled"
@change="item.change && item.change(formData[item.prop])"
>
<el-radio
v-for="radio in item.radios"
:label="radio.label"
:key="radio.label"
>el-radio>
el-radio-group>
<el-checkbox-group
v-if="item.type === 'checkbox'"
v-model="formData[item.prop]"
:disabled="item.disabled"
@change="item.change && item.change(formData[item.prop])"
>
<el-checkbox
v-for="checkbox in item.checkboxs"
:label="checkbox.label"
:key="checkbox.label"
>el-checkbox>
el-checkbox-group>
<el-switch
v-if="item.type === 'switch'"
v-model="formData[item.prop]"
:disabled="item.disabled"
@change="item.change && item.change(formData[item.prop])"
>el-switch>
<el-upload
v-if="item.type === 'upload'"
class="avatar-uploader"
ref="uploadxls"
:action="item.action"
:limit="item.limit"
:show-file-list="false"
:on-success="handleSuccess"
:on-change="handleFileChange"
:before-upload="beforeUpload"
>
<slot name="upload">slot>
el-upload>
<el-cascader
v-if="item.type === 'cascader'"
size="large"
v-model="formData[item.prop]"
:options="options"
:style="{ width: item.width }"
@change="item.change && item.change(formData[item.prop])"
clearable
>
el-cascader>
<template v-if="item.type === 'slotName'">
<slot :name="item.slotName">slot>
template>
el-form-item>
el-form>
div>
template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
// 表单域标签的宽度
labelWidth: {
type: String,
default: '100px'
},
// 表单域标签的位置
labelPosition: {
type: String,
default: 'right'
},
// 表单配置
formConfig: {
type: Array,
required: true
},
// 表单数据
formData: {
type: Object,
required: true
},
// 表单规则
rules: {
type: Object
},
// 级联数据
options: {
type: Array
}
},
methods: {
// 上传成功的方法
handleSuccess (res: any, file: any) {
this.$emit('uploadSuccess');
console.log('ok');
},
// 文件状态改变时的方法
handleFileChange (file: any, fileList: any) {
// URL.createObjectURL(file.raw) 一般在上传成功的函数中,这里是为了测试方便
this.$emit('uploadFileChange', URL.createObjectURL(file.raw));
console.log(file, fileList);
},
// 上传文件之前的方法
beforeUpload (file: any) {
this.$emit('uploadbefore', file);
console.log(file);
// return false;
},
// 表单字段校验方法(自定义检验)
validateField (valid: string) {
(this.$refs as any).ruleForm.validateField(valid);
},
// 提交判断方法
submitForm (callback: (arg0: any) => void) {
(this.$refs as any).ruleForm.validate((valid: any) => {
callback(valid);
});
},
// 重置方法
resetForm () {
(this.$refs as any).ruleForm.resetFields();
}
}
});
script>
<style lang="less" scoped>
/deep/.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
/deep/.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
/deep/textarea {
resize: none;
}
style>
<template>
<div class="form">
<my-form
:labelWidth="labelWidth"
:formConfig="formConfig"
:formData="formData"
:rules="rules"
:options="options"
ref="ruleForm"
@uploadFileChange="uploadFileChange"
@uploadbefore="uploadbefore"
>
<template slot="upload">
<img v-if="formData.sfz" :src="formData.sfz" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon">i>
template>
<template slot="button">
<el-button type="primary" @click.prevent="submitForm()">
立即创建
el-button>
<el-button @click.prevent="resetForm()">重置el-button>
template>
my-form>
div>
template>
<script lang="ts">
import Vue from 'vue';
import { regionData, CodeToText } from 'element-china-area-data';
import MyForm from '@/components/Form/index.vue';
export default Vue.extend({
name: 'Form',
data () {
const validatePassword = (rule: any, value: string, callback: any) => {
if (value === '') {
callback(new Error('请输入密码'));
} else {
if ((this as any).formData.rePwd !== '') {
(this.$refs as any).ruleForm.validateField('rePwd');
}
callback();
}
};
const validaterePwd = (rule: any, value: string, callback: any) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== (this as any).formData.newPwd) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
const self = this;
return {
// 表单域标签的宽度
labelWidth: '150px',
// 表单配置
formConfig: [
{
label: '活动名称',
prop: 'name',
type: 'input',
width: '150px'
},
{
label: '活动区域',
prop: 'region',
type: 'select',
width: '200px',
options: [
{
label: '区域一',
value: 'shanghai'
},
{
label: '区域二',
value: 'beijing'
}
]
},
{
label: '即时配送',
prop: 'delivery',
type: 'switch'
},
{
label: '活动性质',
prop: 'type',
type: 'checkbox',
checkboxs: [
{
label: '线上品牌商赞助'
},
{
label: '线下场地免费'
}
]
},
{
label: '特殊资源',
prop: 'resource',
type: 'radio',
radios: [
{
label: '线上品牌商赞助'
},
{
label: '线下场地免费'
}
]
},
{
label: '身份证正面照',
prop: 'sfz',
type: 'upload',
action: '/',
limit: 1
},
{
label: '活动形式',
prop: 'desc',
type: 'textarea',
width: '200px',
rows: '5',
maxlength: '30',
showWordLimit: true
},
{
label: '密码',
prop: 'newPwd',
type: 'password',
width: '200px'
},
{
label: '确认密码',
prop: 'rePwd',
type: 'password',
width: '200px'
},
{
label: '地址',
prop: 'selectedOptions',
type: 'cascader',
width: '300px',
change: (data: any) => {
(self as any).changAddress(data);
}
},
{
type: 'slotName',
slotName: 'button'
}
],
// 表单数据
formData: {
name: '',
region: '',
delivery: false,
type: [],
resource: '',
sfz: '',
desc: '',
rePwd: '',
newPwd: '',
selectedOptions: []
},
// 表单规则
rules: {
name: [{ required: true, message: '请输入活动名称', trigger: 'blur' }],
region: [
{ required: true, message: '请选择活动区域', trigger: 'change' }
],
type: [
{
type: 'array',
required: true,
message: '请至少选择一个活动性质',
trigger: 'change'
}
],
resource: [
{ required: true, message: '请选择活动资源', trigger: 'change' }
],
sfz: [{ required: true, message: '请上传身份证' }],
desc: [{ required: true, message: '请填写活动形式', trigger: 'blur' }],
newPwd: [
{ required: true, validator: validatePassword, trigger: 'blur' }
],
rePwd: [{ required: true, validator: validaterePwd, trigger: 'blur' }],
selectedOptions: [{ required: true, message: '地址' }]
},
// 省市区三级联动数据
options: regionData
};
},
methods: {
// 转化省市区为汉字
changAddress (value: any) {
console.log(CodeToText[value[0]]);
console.log(CodeToText[value[1]]);
console.log(CodeToText[value[2]]);
},
submitForm () {
(this.$refs as any).ruleForm.submitForm((valid: any) => {
if (valid) {
console.log(this.formData);
console.log('ok');
} else {
console.log('请填写表单的必要信息');
}
});
},
resetForm () {
(this.$refs as any).ruleForm.resetForm();
},
uploadFileChange (localUrl: string) {
console.log(localUrl);
this.formData.sfz = localUrl;
},
uploadbefore (file: any) {
const isJPG = file.type === 'image/jpeg';
if (!isJPG) {
this.$tipMessage('error', '上传头像图片只能是 JPG 格式!');
}
}
},
mounted () {
console.log('form');
},
components: { MyForm }
});
script>
<style lang="less" scoped>
.form {
height: 100%;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
style>
文章仅供参考,具体需求根据项目更改