el-drawer内实现可校验的动态表单
先上代码,由于本人之前一直做后端,前端代码比较水,不喜勿喷。
element-ui版本为 2.13.2
<template>
<div>
<el-drawer
ref="submitDrawer"
:title="title"
:visible.sync="open"
size="40%"
direction="rtl"
:before-close="handlerClose"
>
<el-form ref="permissionForm" :model="tempPermission" label-position="right" :rules="formRules" label-width="150px" label-suffix=":">
<template v-for="(value, key) in column">
<el-form-item
v-if="dynamicItemInfo(value.label)"
:key="key"
:label="dynamicItemInfo(value.label)"
:prop="key"
>
<!-- 普通输入框 -->
<el-input v-if="value.component.type === 1" v-model="tempPermission[key]" :placeholder="dynamicItemInfo(value.placeholder)" :disabled="type === 3" />
<!-- 树形选择器 -->
<el-select
v-if="value.component.type === 2"
v-model="tempPermission[key]"
:placeholder="dynamicItemInfo(value.placeholder)"
style="width: 100%"
popper-class="tree-select"
clearable
@visible-change="visibleChange"
>
<el-tree
ref="tree"
:data="allPermission"
node-key="id"
auto-expand-parent
:default-checked-keys="[tempPermission.parentId]"
:expand-on-click-node="false"
:filter-node-method="filterPermission"
>
<template slot-scope="{ data }">
<el-option
:key="data.id"
style="width: 100%"
:value="data.id"
:label="data.name"
/>
</template>
</el-tree>
</el-select>
<!-- 尾部按钮输入框 -->
<el-input v-if="value.component.type === 3" v-model="tempPermission[key]" :placeholder="dynamicItemInfo(value.placeholder)">
<el-button slot="append" icon="el-icon-setting" size="mini" />
</el-input>
<!-- 请求下拉选择器 -->
<el-select v-if="value.component.type === 4" v-model="tempPermission[key]" :placeholder="dynamicItemInfo(value.placeholder)" style="width: 100%">
<el-option
v-for="method in ['GET','POST','PUT','DELETE']"
:key="method"
:label="method"
:value="method"
/>
</el-select>
<!-- 开关 -->
<el-switch
v-if="value.component.type === 5"
v-model="tempPermission[key]"
active-color="#13ce66"
:active-text="value.component.activeText"
:inactive-text="value.component.inactiveText"
:active-value="value.component.value"
:inactive-value="!value.component.value"
/>
<el-input-number v-if="value.component.type === 6" v-model="tempPermission[key]" controls-position="right" size="mini" :min="0" />
</el-form-item>
</template>
</el-form>
<div v-show="type !== 3" style="width: 100%;border-top: #409EFF solid 1px;text-align: right; padding: 5px 5%">
<el-button size="mini" type="primary" @click="$refs.permissionForm.clearValidate()">清空</el-button>
<el-button size="mini" type="primary" @click="submitPermission">提交</el-button>
<el-button size="mini" @click="$refs.submitDrawer.closeDrawer()">取消</el-button>
</div>
</el-drawer>
</div>
</template>
<script>
import { getRequest, postRequest, putRequest } from '@/api'
export default {
name: 'PermissionDrawer',
data() {
return {
type: '',
open: false,
cancel: false,
tempPermission: {
id: '',
path: '',
component: '',
redirect: '',
name: '',
status: true,
method: '',
hidden: false,
alwaysShow: false,
icon: '',
breadcrumb: true,
activeMenu: '',
permissionType: '',
sort: 0,
parentId: ''
},
resetData: {
id: undefined,
path: undefined,
component: undefined,
redirect: undefined,
name: undefined,
status: true,
method: undefined,
hidden: false,
alwaysShow: false,
icon: undefined,
breadcrumb: true,
activeMenu: undefined,
permissionType: undefined,
sort: 0,
parentId: undefined
},
allPermission: [],
column: {
name: {
component: { type: 1 },
label: ['菜单名称', '子菜单名称', '按钮/权限名称'],
placeholder: ['请输入菜单名称', '请输入子菜单名称', '请输入按钮/权限名称'],
rules: ['菜单名称不能为空', '子菜单名称不能为空', '按钮/权限名称不能为空']
},
parentId: {
component: { type: 2 },
label: ['', '上级菜单', '所属菜单'],
placeholder: ['', '请选择上级菜单', '请选择所属菜单'],
rules: ['', '上级菜单不能为空', '所属菜单不能为空']
},
path: {
component: { type: 1 },
label: ['菜单路由', '子菜单路由', '请求URL'],
placeholder: ['请输入菜单路由', '请输入子菜单路由', '请输入请求URL'],
rules: ['菜单路由不能为空', '子菜单路由不能为空', '请求URL不能为空']
},
component: {
component: { type: 1 },
label: ['组件包路径', '组件包路径', ''],
placeholder: ['请输入组件包路径', '请输入组件包路径', ''],
rules: ['组件包路径不能为空', '组件包路径不能为空', '']
},
redirect: {
component: { type: 1 },
label: ['默认跳转页面', '默认跳转页面', ''],
placeholder: ['请输入默认跳转页面路由', '请输入默认跳转页面路由', '']
},
icon: {
component: { type: 3 },
label: ['菜单图标', '子菜单图标', ''],
placeholder: ['点击右侧按钮选择菜单图标', '点击右侧按钮选择子菜单图标', '']
},
method: {
component: { type: 4 },
label: ['', '', '请求方式'],
placeholder: ['', '', '请选择接口的请求方式']
},
hidden: {
component: {
type: 5,
value: false,
activeText: '显示',
inactiveText: '不显示'
},
label: ['在菜单显示', '在菜单显示', '']
},
alwaysShow: {
component: {
type: 5,
value: true,
activeText: '显示',
inactiveText: '不显示'
},
label: ['为父菜单时一直显示', '为父菜单时一直显示', '']
},
breadcrumb: {
component: {
type: 5,
value: true,
activeText: '显示',
inactiveText: '不显示'
},
label: ['在面包屑显示', '在面包屑显示', '']
},
status: {
component: {
type: 5,
value: true,
activeText: '是',
inactiveText: '否'
},
label: ['是否可用', '是否可用', '是否可用']
},
sort: {
component: {
type: 6
},
label: ['排序', '排序', '排序']
}
}
}
},
computed: {
title: function() {
const type = this.type
let title
if (type === 1) {
title = '添加'
} else if (type === 2) {
title = '编辑'
} else {
title = '查看'
}
const permissionType = this.tempPermission.permissionType
if (permissionType === 1) {
title = title + '路由菜单'
} else if (permissionType === 2) {
title = title + '路由子菜单'
} else {
title = title + '按钮/权限'
}
return title
},
formRules: function() {
// 构造非空的校验规则
if (!this.tempPermission.permissionType) {
return {}
}
const rules = {}
for (const columnKey in this.column) {
if (this.column[columnKey].rules) {
rules[columnKey] = [{ required: true, message: this.dynamicItemInfo(this.column[columnKey].rules), trigger: 'blur' }]
}
}
return rules
}
},
methods: {
async openDrawer(id, type, permissionType) {
this.$set(this, 'type', type)
this.$set(this.tempPermission, 'permissionType', permissionType)
// 根据id查询用户信息渲染
let data
if (type !== 1) {
await getRequest(`/sys/permission/${id}`)
.then(res => {
data = res.data
})
}
if (permissionType !== 1) {
getRequest('sys/permission/queryAll').then(res => {
this.allPermission = res.data
})
}
this.open = true
this.$nextTick(() => {
this.$refs.permissionForm.resetFields()
if (data) {
this.tempPermission = data
}
})
},
submitPermission() {
this.$refs.permissionForm.validate((valid) => {
if (valid) {
this.submitForm()
}
})
},
async submitForm() {
const url = '/sys/permission/'
let result
if (this.type === 1) {
result = postRequest(url, this.tempPermission)
} else {
result = putRequest(url, this.tempPermission)
}
await result.then(res => {
if (res.code === 200) {
this.$message({
message: res.message,
type: 'success'
})
}
})
this.$refs.submitDrawer.closeDrawer()
this.$emit('reload')
},
handlerClose(done) {
this.tempPermission.parentId = ''
this.tempPermission.id = ''
// this.tempPermission.permissionType = ''
this.$refs.permissionForm.resetFields()
done()
},
dynamicItemInfo(arr) {
if (arr === undefined) {
return ''
}
return arr[this.tempPermission.permissionType - 1]
},
visibleChange() {
this.$refs.tree.filter()
},
filterPermission(value, data) {
return data.permissionType !== 3
}
}
}
</script>
<style scoped>
.el-form {
padding: 5% 5% 0;
overflow-y: auto;
overflow-x: hidden;
height: calc(100vh - 120px);
}
.el-form .el-form-item {
margin-bottom: 20px !important;
height: auto;
}
>>> .el-form .el-form-item__label {
height: 30px;
line-height: 30px;
}
>>> .el-form .el-form-item__content {
line-height: 30px;
}
.el-form >>> .el-form-item .el-input__inner {
height: 30px;
}
>>> .el-form .el-input__icon {
height: 30px;
line-height: 30px;
}
.el-form >>> .el-date-editor.el-input {
width: 100% !important;
}
>>> .el-input-number__decrease, >>> .el-input-number__increase {
height: 28px;
line-height: 28px;
}
</style>
<style>
.tree-select {
padding: 5px 2px;
}
.tree-select .el-tree {
max-height: 200px;
}
.tree-select .el-select-dropdown__item {
padding: 0 5px;
height: 30px;
line-height: 30px;
border-radius: 5px;
}
.tree-select .el-select-dropdown__item.hover {
background-color: #E6F7FF;
}
.tree-select .el-tree .el-tree-node__content:hover {
background: #ffffff;
}
.tree-select .el-tree .el-tree-node:focus > .el-tree-node__content {
background-color: #ffffff;
}
</style>
实现思路:表单内容部分是预先写在data方法的colum内,column包含了每个字段以及字段的校验规则信息,通过计算属性的formRules动态创建校验规则,再在el-form内循环el-form-item,事先在里面写好需要用到的输入框根据component.type来选择每个表单项需要显示的输入框。
需要注意的方法有两个openDrawer和handlerClose
openDrawer是给父组件用来开启drawer并传入drawer需要的信息
如果是编辑预览之类的表单可以先请求表单内容放入data,等到open状态为true后在$nextTick内再对表单赋值,防止表单关闭后无法清空数据!!!
close方法是关闭drawer的方法需要对一些非公共的字段初始化,因为resetFields只能清空设置了prop的属性,其他属性是无法控制的。
最后附上效果图