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指令,因為這個指令是動態渲染內容的(文中有用到)。

 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM