美文网首页
vue封装一个可处理表单的组件

vue封装一个可处理表单的组件

作者: 不语u | 来源:发表于2021-06-07 10:12 被阅读0次

    一、需求。

    最近在开发一个新系统,有很多功能以及表单需要处理,为了偷懒,想着封装一个组件统一处理下相关逻辑,这样下来,每个功能只需写一个列表查询的组件和一个表单组件,省时省心。
    最终效果:


    最终效果图

    二、需要实现的点:

    1、 增加、修改、查看三个功能都在这里进行处理
    2、每个功能只需要写好表单以及对应事件,用该组件统一处理外部样式以及触发子组件中的表单事件(这里的表单作为子组件)
    3、因为有左侧边栏的缘故,还需要动态调整宽度

    三、具体实现

    <!--基于element封装的抽屉组件,具体api见:https://element.eleme.io/#/zh-CN/component/drawer-->
    <template>
      <div>
        <el-drawer ref="drawer"
                   append-to-body
                   :size="drawer.width"
                   :before-close="beforeClose"
                   :visible="drawer.show"
                   :direction="direction"
                   :destroy-on-close="destroy"
                   :wrapperClosable="wrapperClosable"
                   @open="getNewWidth"
                   @close="closeHander">
          <!-- 标题部分 -->
          <template slot="title">
            <p>{{ titleText }}</p>
          </template>
          <!-- 主要内容 -->
          <slot name="main"></slot>
          <div class="drawer__footer">
            <Button size="large"
                    icon="md-close"
                    @click="CloseDrawer">{{
              showSubmitBtn ? "取消" : "关闭"
            }}</Button>
            <Button v-if="showSubmitBtn"
                    size="large"
                    type="primary"
                    @click="submit"
                    icon="md-checkbox-outline"
                    :loading="loading">{{ loading ? "提交中 ..." : "确 定" }}</Button>
          </div>
        </el-drawer>
      </div>
    </template>
    <script>
    export default {
      name: 'JcDrawer',
      props: {
        //模式:1新增  2修改  3查看
        mode: {
          default: 1,
          type: Number,
        },
        //是否显示
        show: {
          default: false,
        },
        //标题
        title: {
          type: String,
        },
        //打开方向
        direction: {
          default: 'rtl',
          type: String,
        },
        //控制是否在关闭 Drawer 之后将子元素全部销毁
        destroy: {
          default: true,
          type: Boolean,
        },
        //点击遮罩层是否可以关闭 Drawer
        wrapperClosable: {
          default: false,
          type: Boolean,
        },
        //是否显示提交按钮(有的界面无需提交)
        showSubmitBtn: {
          default: true,
          type: Boolean,
        },
      },
      data() {
        return {
          isAuto: false, // 是否自动关闭
          Bus: this.$BusFactory(this),
          loading: false,
          drawer: {
            width: 0,
            show: this.show,
            mode: this.mode,
          },
        }
      },
      mounted() {
        //初始化完成时添加一个事件用于监听屏幕大小变化,自适应调整宽度
        window.onresize = () => {
          return (() => {
            this.getNewWidth()
          })()
        }
      },
    
      watch: {
        //监听父组件的值控制是否显示
        show: {
          immediate: true,
          deep: true,
          handler(newvalue, oldvalue) {
            this.drawer.show = newvalue
          },
        },
        mode: {
          immediate: true,
          handler(newvalue, oldvalue) {
            this.drawer.mode = newvalue
          },
        },
      },
      computed: {
        /**
         * @description 标题
         */
        titleText() {
          if (this.drawer.mode === 1) return `添加${this.title}信息`
          if (this.drawer.mode === 2) return `修改${this.title}信息`
          if (this.drawer.mode === 3) return `查看${this.title}信息`
        },
      },
      methods: {
        /**
         * @description 关闭之后的事件
         */
        closeHander() {
          //取消自动关闭
          this.isAuto = false
        },
        /**
         * @description 关闭窗口
         */
        CloseDrawer() {
          if (this.showSubmitBtn) {
            this.isAuto = false
          } else {
            this.isAuto = true
          }
          this.$refs.drawer.closeDrawer()
        },
    
        /**
         * @description 关闭抽屉的事件
         * @param {function} done 关闭的回调
         */
        beforeClose(done) {
          if (!this.isAuto && this.showSubmitBtn) {
            this.$confirm('确定要关闭吗?', '提示', {
              confirmButtonText: '确定',
              cancelButtonText: '取消',
              type: 'warning',
            }).then((_) => {
              this.$emit('close')
              done()
            })
          } else {
            this.$emit('close')
            done()
          }
        },
        //提交事件
        submit(callback) {
          var that = this
          that.loading = true
          if (this.drawer.mode == 1) {
            //使用事件总线的方式触发表单组件中的事件
            this.Bus.$emit('Add', function (res) {
              if (res.success) {
                that
                  .$confirm('添加成功!', '提示', {
                    confirmButtonText: '确定',
                    showCancelButton: false,
                    type: 'success',
                    closeOnClickModal: false,
                    closeOnPressEscape: false,
                    showClose: false,
                  })
                  .then(() => {
                    //点击确认之后直接退出并销毁表单
                    that.isAuto = true
                    that.$refs.drawer.closeDrawer()
                  })
              }
            })
          }
          if (this.drawer.mode == 2) {
            this.Bus.$emit('Edit', function (res) {
              if (res.success) {
                that
                  .$confirm('修改成功!', '提示', {
                    confirmButtonText: '确定',
                    showCancelButton: false,
                    type: 'success',
                    closeOnClickModal: false,
                    closeOnPressEscape: false,
                    showClose: false,
                  })
                  .then(() => {
                    that.isAuto = true
                    that.$refs.drawer.closeDrawer()
                  })
              }
            })
          }
          if (this.drawer.mode == 3) {
            this.Bus.$emit('Detail', function () {
              that.isAuto = true
              that.$refs.drawer.closeDrawer()
            })
            that.loading = false
          }
        },
        /**
         * @description 设置主界面的宽度
         */
        getNewWidth() {
          //组件宽度=当前屏幕宽度-侧边栏宽度
          this.drawer.width =
            document.body.clientWidth -
            (parseInt(document.getElementById('left_nav')?.style?.width) || 0)
        },
      },
    }
    </script>
    <style scoped>
    .drawer__footer {
      width: 100%;
      border-top: 1px solid #e8eaec;
      position: absolute;
      bottom: 0;
      padding: 10px;
      display: flex;
      justify-content: center;
      background-color: white;
      z-index: 99;
    }
    .drawer__footer button {
      margin: 0 10px;
    }
    </style>
    
    

    使用(列表查询组件):

    <!--列表组件-->
    <template>
      <!--...列表展示内容-->
      <jc-drawer :show="formShow"
                 @close="formShow = false"
                 :title="title"
                 :mode="mode">
        <!-- 使用插槽插入表单 -->
        <template slot="main">
          <role-form :roleId="roleId"
                     @getList="getList"
                     :mode="mode"></role-form>
        </template>
      </jc-drawer>
    </template>
    <script>
    import jcDrawer from '@/components/jc-cpn/jc-drawer.vue'
    import RoleForm from './form'
    export default {
      components: { jcDrawer, RoleForm },
      name: 'role',
      data() {
        return {
          roleId: null,
          mode: 1,
          title: '角色',
          formShow: false,
        }
      },
    
      methods: {
        // 添加角色
        roleAdd() {
          this.mode = 1
          this.formShow = true
        },
        //编辑角色
        roleEdit() {
          this.mode = 2
          this.roleId = 点击编辑按钮时行的id
          this.formShow = true
        },
        //查看角色
        roleDetail() {
          this.mode = 3
          this.roleId = 点击查看按钮时行的id
          this.formShow = true
        },
      },
    }
    </script>
    

    表单组件:

    <!-- 角色的form表单 -->
    <template>
      <div>
        <!-- 表单元素 -->
      </div>
    </template>
    <script>
    export default {
      name: 'RoleForm',
      props: {
        roleId: {
          type: Number,
          default: null,
        },
        mode: {
          type: Number,
          default: 1,
        },
      },
      data() {
        return {
          // 事件总线实例。详情见:https://zhuanlan.zhihu.com/p/32029461
          Bus: this.$BusFactory(this),
          loading: false,
        }
      },
      watch: {
        //监听父组件的值控制是否显示
        roleId: {
          immediate: true,
          deep: true,
          handler(newvalue, oldvalue) {
            if (this.mode == 2) {
              //回填数据
              this.setCurrentData()
            }
          },
        },
      },
      mounted() {
        //采用事件总线的方式绑定drawer组件中定义的方法,采用事件回调的方式通知drawer此次事件的完成情况,在drawer中统一处理
        //新增方法
        this.Bus.$on('Add', (callback) => {
          this.RoleAdd(callback)
        })
        //新增方法
        this.Bus.$on('Edit', (callback) => {
          this.RoleEdit(callback)
        })
      },
      methods: {
        /**
         * @description 角色添加
         * @param {function} callback 回调函数,请求完成后用于通知抽屉组件完成状态
         */
        RoleAdd(callback) {
          callback(...res)
        },
        /**
         * @description 角色修改
         * @param {function} callback 回调函数,请求完成后用于通知抽屉组件完成状态
         */
        RoleEdit(name, callback) {
          callback(...res)
        },
      },
    }
    </script>
    
    
    最终目录结构
    总结:完成这样一套逻辑之后,对应功能节点只需要完成如图表单组件和列表查询组件,而且表单中只需处理数据,提示或者报错都放到drawer组件中进行处理,极大程度上保护了自己的头发。

    另:有关事件总线不明白的地方可参考:Vue自动销毁的vue event Bus

    相关文章

      网友评论

          本文标题:vue封装一个可处理表单的组件

          本文链接:https://www.haomeiwen.com/subject/erffeltx.html