ElementUI对话框(dialog)提取为子组件

需求:在页面的代码太多,想把弹窗代码提取为子组件,复用也方便。
 
这里涉及到弹窗el-dialog的一个属性show-close:
show-close="false"是设置不显示关闭按钮,因为弹窗显示状态值(:visible.sync)是从父组件传递的参数,如果使用自带的关闭按钮,会报出一个错误:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "visible"
虽然弹窗会关闭,但却会导致弹窗的显示状态没有及时修改为false,需要点击两次触发弹窗click事件才能再打开弹窗。
所以,为了避免这个错误,这里提供两个方案:
1.关闭按钮另外添加,因为要触发click事件来触发父组件修改变量(使用$emit通知父组件来操作)。
2.使用自带关闭按钮,给弹窗添加before-close事件(这是elementUI提供的),在这个方法里也是使用$emit修改父组件的visible状态。
 
第一种方案:
 
父组件:
<template>
  <div class="main-wrap">
    <el-button type="primary" @click="add('addOrder')">添加</el-button>
    <add-order ref="addOrder" v-if="addOrderVisible" :visible.sync="addOrderVisible"></add-order>
  </div>
</template>
<script>
  import Add from './add.vue'
  export default {
    data(){
      return {
        addOrderVisible: false
      }
    },
    methods: {
      add(refForm){
        if(this.$refs[refForm]){
          this.$refs[refForm].initForm();
        }
        this.addOrderVisible= true;
      }
    },
    components: {
      'add-order': Add
    }
  }
</script>
 
子组件:
<template>
  <el-dialog :visible.sync="visible" :show-close="false" width="600px" :modal="true" :close-on-click-modal="false" :close-on-press-escape="false">
    <h2 slot="title">添加订单</h2>
    <button type="button" aria-label="Close" class="el-dialog__headerbtn" @click.stop="cancelModal"><i class="el-dialog__close el-icon el-icon-close"></i></button>
    <el-form class="form-wrapper" ref="orderForm" :model="orderForm" :rules="addRules" label-width="110px">
      <el-form-item label="联系人:" prop="fromContact">
        <el-input v-model="orderForm.fromContact" type="text" placeholder="请输入联系人名称"></el-input>
      </el-form-item>
      <el-form-item label="联系电话:" prop="fromPhone">
        <el-input v-model="orderForm.fromPhone" type="text" placeholder="请输入联系电话"></el-input>
      </el-form-item>
    </el-form>
    <div slot="footer" class="buttons-wrap">
      <el-button type="primary">确定</el-button>
    </div>
  </el-dialog>
</template>
<script>
  export default {
    props: {
      visible: {
        type: Boolean,
        default: false
      }
    },
    data(){
      return {
        orderForm: {},
        addRules: {
          fromContact: [{ required: true, message: "请输入联系人姓名", trigger: 'blur'}],
          fromPhone: [{required: true, message: "请输入", trigger: 'blur'}]
        }
      }
    },
    methods: {
      initForm(){
        this.orderForm = {
          fromContact: '',
          fromPhone: ''
        };
        if(this.$refs.orderForm){
          this.$refs.orderForm.resetFields();
        }
      },
      cancelModal(){
        // 关闭弹窗,触发父组件修改visible值
        this.$emit('update:visible', false);
      }
    }
  }
</script>
<style lang="scss" scoped>
  .buttons-wrap {

    .el-button {
      margin-right: 20px;
      min-width: 100px;
    }
  }
</style>
 
第二种方案:(添加before-close)

父组件:

<template>
  <div class="main-wrap">
    <el-button type="primary" @click="toAdd">添加</el-button>
    <add-order ref="orderAdd" v-if="addOrderVisible" :visible.sync="addOrderVisible"></add-order>
  </div>
</template>
<script>
  import Add from './add.vue'
  export default {
    data(){
      return {
        addOrderVisible: false
      }
    },
    methods: {
      toAdd() {
        this.addOrderVisible = true;
      }
    },
    components: {
      'add-order': Add
    }
  }
</script>

子组件:

<template>
  <el-dialog
    title="添加"
    v-loading="loading"
    :visible.sync="visible"
    width="600px"
    :before-close="modalClose"
    :close-on-click-modal="false"
    :close-on-press-escape="false">
    <div>弹窗内容</div>
  </el-dialog>
</template>
<script>
  export default {
    data() {
      return {
        loading: false
      }
    },
    props: {
      visible: {
        type: Boolean,
        default: false
      }
    },
    methods: {
      modalClose() {
        this.$emit('update:visible', false); // 直接修改父组件的属性
      }
    }
  }
</script>

这里提另外一种情况:

在只需要显示详情内容的情况下,也可以采取只把内容放到子组件中,头部和底部按钮都放在父组件中,这就不需要考虑弹窗显示状态的问题,把需要显示的参数传到子组件中即可。
 
父组件:
<template>
  <div class="main-wrap">
    <el-button type="primary" @click="showDetail()">详情</el-button>
    <el-dialog v-if="detailVisible" :visible.sync="detailVisible" width="600px" :modal="true">
      <h2 slot="title">详情</h2>
      <detail ref="newOrderDetail" :newOrderDetail="newOrderDetail"></detail>
      <div slot="footer" class="detail-wrap-bottom">
        <el-button type="primary">确认</el-button>
        <el-button type="default">退回</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
  import detail from './detail'
  export default {
    data(){
      return {
        detailVisible: false,
        newOrderDetail: {}
      }
    },
    methods: {
      showDetail(){
        this.newOrderDetail = {id: 8, fromContact: 'Thomas', fromPhone: '15812345678'};
        this.detailVisible= !this.detailVisible;
      }
    },
    components: {
      detail: detail
    }
  }
</script>
子组件:
<template>
  <div class="detail-wrap" :data="newOrderDetail">
    <ul>
      <li><span>用车联系人:</span><div>{{newOrderDetail.fromContact}}</div></li>
      <li><span>联系人电话:</span><div>{{newOrderDetail.fromPhone}}</div></li>
    </ul>
  </div>
</template>
<script>
  export default {
    props: {
      newOrderDetail: {
        type: Object,
        default: null
      }
    },
    data() {
      return {
        loading: false
      }
    }
  }
</script>
<style lang="scss" scoped>
  .detail-wrap ul {
    max-height: 400px;
    overflow: auto;

    li {
      position: relative;
      padding: 8px;
      font-size: 1rem;

      span {
        position: absolute;
        width: 90px;
      }

      & > div {
        margin-left: 90px;
      }
    }
  }
</style>
 

温馨提示:

如果弹窗内容较多,出现了滚动条,需要每次打开还原到顶部,则需要添加v-if指令,因为这个指令是动态渲染内容的(文中有用到)。

 
 
 
原文地址:https://www.cnblogs.com/yeqrblog/p/9141701.html