關於elememt-ui中dialog彈窗多重巢狀的問題

五角六芒星_發表於2017-11-14

近期我司正在開發一個新的專案,期間遇到過很多問題,但是通過努力都得道瞭解決,現在專案還在一步一步的進行中,今天就給大家說一下我在使用vue+element專案開發中遇到的dialog問題:

1.彈框不支援巢狀

在element 2.0版本之前dialog是不支援巢狀的,後面element升級到2.0之後是支援巢狀的,只需要給dialog新增 append-to-body 屬性即可。但是雖然之前彈窗專案巢狀,但是element中的dialog並不支援,多級彈窗之前的連續回撥,即子彈窗無法控制父彈窗的關閉,或者其他操作。

2.多個彈框之間遮罩層會相互影響

當專案中一個頁面中使用多級彈窗的時候,再不考慮回撥函式的同時,還有一個很大的問題,就是多個彈窗之間的遮罩層會相互影響,你會發想彈窗越來越多,遮罩會變得越來越黑 =_=||

解決方案 ==> setTimeout

針對上面闡述的兩個問題,我做了一些簡單的操作,首先是通過在父元件的監聽函式裡面加了一個setTimeout,並且這個方式達到了我們預期的效果,能夠讓子元件控制父元件甚至父父元件的關閉。程式碼如下:

子元件程式碼:

/*
        * 監聽子元件通訊的方法
        * 作用:根據不同的引數關閉對應的模態
        * @param targer string example:"add""edit"
        * */
        cancel(targer){
          let that = this;
          this[targer+'Modal'] = false;
          setTimeout(function(){
            that.$emit('cancel','edit');
          },10)
        }複製程式碼

父元件程式碼:

/*
       * 監聽子元件通訊的方法
       * 作用:根據不同的引數關閉對應的模態
       * @param targer string example:"add""edit"
       * */
      cancel(targer){
        that[targer+'Modal'] = false;
      },複製程式碼

這種方式通過延時器的方式,可以說是取了一下巧,但是當你的頁面出現很多的彈窗的時候,你需要分別定義不同的名稱,然後對不同的彈窗做不同的處理,非常麻煩。

解決方案二 ==> 通過例項vue物件

尤大曾在知乎某個相關的問題說過這樣的話:
為什麼一定要非同步插入?
其實以前也有一些使用者跟我糾結過這個問題,他們覺得一定要在需要的時候建立這個元件才是符合他們思維的做法。在我看來,這是沒有理解『狀態驅動的介面』的一種表現。
傳統的命令式 (Imperative) 的思維寫出來的程式碼:

$('.open-modal').on('click', function () {
 var modal = new Modal()
 modal.$appendTo('body')
 modal.open()
})複製程式碼

// 在 modal 內部還要處理關閉、銷燬自身的邏輯
狀態驅動的思維寫出來的程式碼:

this.showModal = true

// 關掉
this.showModal = false複製程式碼

不可否認,尤大所說的狀態驅動確實是vue的精髓,但是在實際應用中,dialog往往需要直接在body下才能避免這樣那樣的問題,後來在csdn上看到的一篇文章,確實解決了我的問題。原文連結

原理就是在方法被呼叫時,在body裡create一個div,並且建立一個Vue例項,指定el屬性為這個div。

下面是我定義的dialog.js

import Vue from 'vue'

function Dialog (option)  {
    var dom = document.createElement('div');
    document.getElementsByTagName('body')[0].appendChild(dom);
    let tpl = '\
        <el-dialog \
            :close-on-click-modal="false" \
            :custom-class="customClass" \
            :title="title" \
            :visible.sync="show" \
            :width="width" \
            :before-close="handleClose" \
            @close="close">\
                <dialogContent  @close="closeDialog" @confirm="confirmDialog" v-model="dialogData"></dialogContent>\
        </el-dialog>';
    var vue = new Vue({
        el: dom,
        data: function () {
            return {
                title: option.title,
                width: option.width || '1000px',
                show: true,
                dialogData: option.data,
            };
        },
        template: tpl,
        computed: {
            customClass(){
                return `el-dialog--width-${option.width || 'auto'}`;
            }
        },
        methods: {
            handleClose(done){
                if (option.beforeClose) {
                    option.beforeClose(done);
                } else {
                    done();
                }
            },
            close() {
                if (option.close) {
                    option.close();
                }
            },

            closeDialog(){
                this.show = false
            },
            confirmDialog(result){
                this.show = false
                option.confirm && option.confirm(result)
            }
        },
        components: {
            dialogContent: option.component,
        },
    });
    return vue;
}
export default Dialog複製程式碼

專案地址

我將程式碼整體整理了一個demo,demo中包含了,單一彈窗,多級彈窗(多級彈窗分為兩個,一個是關閉當前,一個是關閉所有),希望能對大家有所幫助。

專案地址

喜歡的可以關注我,有時間就會更新一些自己工作或生活中遇到的一些小問題以及解決方案

相關文章