需求:在頁面的代碼太多,想把彈窗代碼提取為子組件,復用也方便。
這里涉及到彈窗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指令,因為這個指令是動態渲染內容的(文中有用到)。