使用 mixins 实现 el-dialog 的封装与复用

473 阅读2分钟

概览

el-dialogelement-ui 的一个组件,在本项目的管理端使用场景中,用于新增或编辑资源,vue 的优势之一就是组件化,所以新增或编辑也写成组件,经过几次改变后,我选择了以下的封装方式,更便于使用。

介绍

对需要弹窗展示的子组件中,直接用 el-dialog 组件,组件中提供一个能够控制 visible 显示的方法,本项目中显示的方法为 open() { this.visible = true },由父组件通过子组件的 ref 进行调用,控制 e-dialog 的显示。

场景

本项目中 el-dialog 主要用于展示、新增、编辑三种情况,新增与编辑一般会涉及到表单,即 el-form 组件的表单校验、表单提交等;展示与编辑时可能涉及到数据的接口获取等,所以将这些基础数据与 el-dialog 的一些常用方法,如 before-close 等做了 mixin 复用。

实现

mixinmodal.js

模态框的混入复用

export const modal = {
  // 可能用到的一些数据初始化
  data() {
    return {
      id: null,             // 当前编辑的资源 ID
      loading: false,       // 展示与编辑时调用接口的加载状态
      submitLoading: false, // 提交表单加载状态
      visible: false,       // 是否显示 el-dialog
      isEdit: false,        // 是否为编辑状态
      form: {}              // 默认表单
    }
  },

  methods: {
    /**
     * @desc 父级调用打开模态框
     * @param {Object} row 编辑时回显数据
     */ 
    open(row) {
      // 此处可通过接口获取需要的数据
      
      // 有数据时为编辑状态
      if (row) {
        this.isEdit = true
        // copy 传入数据,以防变更时影响到父组件数据
        this.form = Object.assign(this.form, row)
      }
      // 显示 el-dialog
      this.visible = true
      
      // 清空表单验证,当el-dialog渲染显示时才可调用
      this.$nextTick(() => {
        this.$refs['form'] && this.$refs['form'].clearValidate()
      })
    },

    // 对应 el-dialog 的 before-close 方法,关闭前回调,
    // 若接口请求未处理完成,则禁止关闭
    beforeClose(done) {
      if (this.submitLoading) {
        this.$message.warning('请等待操作完成后关闭')
      } else {
        done()
      }
    },

    // 关闭模态框
    close() {
      // 重置表单内容
      this.$refs['form'] && this.$refs['form'].resetFields()
      // 初始化表单数据,vue 的 this.$options.data() 内部方法中存放这 data 的初始数据
      this.form = this.$options.data().form
      this.isEdit = false
      // 关闭 el-dialog
      this.visible = false
    },

    // 保存或更新时校验表单
    submitForm() {
      // 防止用户重新表单 ref 名称导致不存在时报错
      if (this.$refs['form']) {
        this.$refs['form'].validate((valid) => {
          if (valid) {
            this._submitForm()
          } else {
            return false;
          }
        });
      } else {
        this._submitForm()
      }
    },
    
    // 表单提交 this.submit() 方法是在组件中定义的 ajax 请求方法
    async _submitForm() {
      this.submitLoading = true;
      let res = await this.submit()
      this.submitLoading = false;

      if (res && res.code === 200) {
        // 成功后将消息发送给父组件,以便父组件做处理,比如更新列表数据等
        this.$emit("on-success");
        this.visible = false;
      }
    }
  },
}

子组件

子组件中使用 el-dialog 组件并无新加功能,注意与优化:

  1. 新增或编辑时 close-on-click-modal 置为 false ,防止鼠标误触(特别是 el-select 多选情况)导致模态框关闭,表单数据被清空;
  2. 新增或更新时,加上 before-close 回调,防止用户在提交未完成时关闭模态框;
  3. 模态框关闭时,需要重置表单数据、清空表单验证结果;
  4. 当表单内只有 <input type="text"></input> 一个输入项时,需在表单中添加 @submit.native.prevent 方法,阻止按回车键时提交表单从而刷新页面。
<el-dialog
  :title="isEdit ? '编辑资源' : '新建资源'"
  :visible.sync="visible"
  :close-on-click-modal="false"
  :before-close="beforeClose"
  @close="close()"
>
  <!-- 表单 -->
  <el-form
    :model="form"
    :rules="rules"
    ref="form"
    label-width="110px"
    @submit.native.prevent
  >
    <el-form-item label="标题:" prop="title">
      <el-input v-model="form.title" placeholder="请输入名称" />
    </el-form-item>
  </el-form>
  
  <!-- 关闭弹窗或提交表单 -->
  <div slot="footer">
    <m-button type="cancel" @click="visible = false" />
    <m-button type="submit" @click="submitForm()" :loading="submitLoading" />
  </div>
</el-dialog>
import { createDemo, updateDemo } from "@/api/demo";
// 引入混入
import { modal } from "@/mixins/modal";

export default {
  name: "DemoModal",
  mixins: [modal],
  data() {
    return {
      // 校验规则
      rules: {
        title: [
          { 
            required: true, 
            message: "请输入标题", 
            trigger: "blur" 
          }
        ],
      },
      form: {
        id: null,
        title: "",
      },
    };
  },

  methods: {
    // 父组件调用,编辑时需要传入被编辑的数据,以便在表单中回显
    open(row) {
      if (row) {
        this.isEdit = true;
        // copy 传入数据到表单,否则会影响到父组件数据
        this.form = Object.assign(this.form, row);
      }
      this.visible = true;
    },
    
    // 表单提交,由 mixin 中的 submitFor() 方法调用
    async submit() {
      return this.isEdit
        ? await updateDemo(this.form.id, this.form)
        : await createDemo(this.form);
    }
  }
};

使用

使用时,只需要引入 mixinmodal.js 与上面定义的弹窗表单组件,通过弹窗组件的 open() 方法即可控制弹窗的显示,代码量极少,且不影响 el-dialog 的任何 api 的使用。

<!-- 新增资源 -->
<m-button type="create" @click="showModal()" />

<m-table v-loading="loading" :data="list">
  <m-table-column prop="title" min-width="100" label="标题" />
  <m-table-column prop="content" min-width="100" label="内容" />
  <m-table-column fixed="right" width="80" label="操作">
    <template slot-scope="scope">
      <!-- 编辑资源,需传入资源信息以在表单中回显 -->
      <m-button type="edit" @click="showModal(scope.row)" />
    </template>
  </m-table-column>
</m-table>

<!-- 新增或编辑的表单模态框,数据变更成功后刷新列表数据 -->
<demo-modal ref="modal" @on-success="getData()" />
// 引入接口
import { getDemoList } from "@/api/demo";
// 引入组件(页面组件一般放于该父级下的 components 文件夹中)
import DemoModal from "./components/DemoModal";

export default {
  name: "DemoList",
  components: {
    DemoModal,
  },
  data() {
    return {
      loading: false,
      list: []
    };
  },
  
  mounted() {
    this.getData();
  },
  
  methods: {
    // 获取数据列表
    async getData() {
      this.loading = true;
      let { code, data } = await getDemoList();
      this.loading = false;
      if(code === 200) {
        this.list = data;
      }
    },
  
    // 显示新增或编辑的模态框
    showModal(row) {
      this.$refs["modal"].open(row);
    },
  }
 })

扩展

如果 el-dialog 中的内容较为复杂,如在关闭前或者关闭时需要做一些事情处理,可在组件中重写对应的方法覆盖,如下:

methods: {
  // 关闭前做一些事务处理
  async beforeClose() {
    try {
      await this.$confirm("还有文件未操作, 确定要离开吗?", "提示", {
        type: "warning",
      });
    } catch (e) {
      return;
    }

    this.visible = false;
    this.$emit("on-close");
  },

  // 关闭模态框
  close() {
    // 重置 data 数据,this.$options.data() 方法中存放着 data 的初始化数据
    this.data = this.$options.data();
    this.visible = false;
  }
}

慕影网管理端介绍文章
慕影网管理端演示地址
慕影网管理端源码地址
慕影网管理端开发文档
慕影网管理端接口文档

本项目会长期更新