需求:在页面的代码太多,想把弹窗代码提取为子组件,复用也方便。
这里涉及到弹窗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指令,因为这个指令是动态渲染内容的(文中有用到)。