el-drawer+el-form实现动态表单

文章描述了如何在ElementUI的el-drawer中实现一个可校验的动态表单,展示了如何根据不同的组件类型动态生成输入控件,并使用计算属性管理表单验证规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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的属性,其他属性是无法控制的。
最后附上效果图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值