基本上每一個項目都須要用到模態框組件,因爲在最近的項目中,alert組件和confirm是兩套徹底不同的設計,因此我將他們分紅了兩個組件,本文主要討論的是confirm組件的實現。javascript
<template> <div class="modal" v-show="show" transition="fade"> <div class="modal-dialog"> <div class="modal-content"> <!--頭部--> <div class="modal-header"> <slot name="header"> <p class="title">{{modal.title}}</p> </slot> <a v-touch:tap="close(0)" class="close" href="javascript:void(0)"></a> </div> <!--內容區域--> <div class="modal-body"> <slot name="body"> <p class="notice">{{modal.text}}</p> </slot> </div> <!--尾部,操做按鈕--> <div class="modal-footer"> <slot name="button"> <a v-if="modal.showCancelButton" href="javascript:void(0)" class="button {{modal.cancelButtonClass}}" v-touch:tap="close(1)">{{modal.cancelButtonText}}</a> <a v-if="modal.showConfirmButton" href="javascript:void(0)" class="button {{modal.confirmButtonClass}}" v-touch:tap="submit">{{modal.confirmButtonText}}</a> </slot> </div> </div> </div> </div> <div v-show="show" class="modal-backup" transition="fade"></div> </template>
模態框結構分爲三部分,分別爲頭部、內部區域和操做區域,都提供了slot,能夠根據須要定製。vue
.modal { position: fixed; left: 0; top: 0; right: 0; bottom: 0; z-index: 1001; -webkit-overflow-scrolling: touch; outline: 0; overflow: scroll; margin: 30/@rate auto; } .modal-dialog { position: absolute; left: 50%; top: 0; transform: translate(-50%,0); width: 690/@rate; padding: 50/@rate 40/@rate; background: #fff; } .modal-backup { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1000; background: rgba(0, 0, 0, 0.5); }
這裏只是一些基本樣式,沒什麼好說的,此次項目是在移動端,用了淘寶的自適應佈局方案,@rate是切稿時候的轉換率。java
/** * modal 模態接口參數 * @param {string} modal.title 模態框標題 * @param {string} modal.text 模態框內容 * @param {boolean} modal.showCancelButton 是否顯示取消按鈕 * @param {string} modal.cancelButtonClass 取消按鈕樣式 * @param {string} modal.cancelButtonText 取消按鈕文字 * @param {string} modal.showConfirmButton 是否顯示肯定按鈕 * @param {string} modal.confirmButtonClass 肯定按鈕樣式 * @param {string} modal.confirmButtonText 肯定按鈕標文字 */ props: ['modalOptions'], computed: { /** * 格式化props進來的參數,對參數賦予默認值 */ modal: { get() { let modal = this.modalOptions; modal = { title: modal.title || '提示', text: modal.text, showCancelButton: typeof modal.showCancelButton === 'undefined' ? true : modal.showCancelButton, cancelButtonClass: modal.cancelButtonClass ? modal.showCancelButton : 'btn-default', cancelButtonText: modal.cancelButtonText ? modal.cancelButtonText : '取消', showConfirmButton: typeof modal.showConfirmButton === 'undefined' ? true : modal.cancelButtonClass, confirmButtonClass: modal.confirmButtonClass ? modal.confirmButtonClass : 'btn-active', confirmButtonText: modal.confirmButtonText ? modal.confirmButtonText : '肯定', }; return modal; }, }, },
這裏定義了接口的參數,能夠自定義標題、內容、是否顯示按鈕和按鈕的樣式,用一個computed來作參數默認值的控制。git
data() { return { show: false, // 是否顯示模態框 resolve: '', reject: '', promise: '', // 保存promise對象 }; }, methods: { /** * 肯定,將promise判定爲完成態 */ submit() { this.resolve('submit'); }, /** * 關閉,將promise判定爲reject狀態 * @param type {number} 關閉的方式 0表示關閉按鈕關閉,1表示取消按鈕關閉 */ close(type) { this.show = false; this.reject(type); }, /** * 顯示confirm彈出,並建立promise對象 * @returns {Promise} */ confirm() { this.show = true; this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); return this.promise; //返回promise對象,給父級組件調用 }, },
在模態框內部定義了三個方法,最核心部分confirm方法,這是一個定義在模態框內部,可是是給使用模態框的父級組件調用的方法,該方法返回的是一個promise對象,並將resolve和reject存放於modal組件的data中,點擊取消按鈕時,判定爲reject狀態,並將模態框關閉掉,點肯定按鈕時,判定爲resolve狀態,模態框沒有關閉,由調用modal組件的父級組件的回調處理完成後手動控制關閉模態框。es6
<!-- template --> <confirm v-ref:dialog :modal-options.sync="modal"></confirm> <!-- methods --> this.$refs.dialog.confirm().then(() => { // 點擊肯定按鈕的回調處理 callback(); this.$refs.dialog.show = false; }).catch(() => { // 點擊取消按鈕的回調處理 callback(); });
用v-ref建立一個索引,就很方便拿到模態框組件內部的方法了。這樣一個模態框組件就完成了。github
在模態框組件中,比較難實現的應該是點擊肯定和取消按鈕時,父級的回調處理,我在作這個組件時,也參考了一些其實實現方案。web
這個方法是個人同事實現的,用在上一個項目,採用的是$dispatch和$broadcast來派發或廣播事件。bootstrap
首先在根組件接收dispatch過來的transmit事件,再將transmit事件傳遞過來的eventName廣播下去segmentfault
events: { /** * 轉發事件 * @param {string} eventName 事件名稱 * @param {object} arg 事件參數 * @return {null} */ 'transmit': function (eventName, arg) { this.$broadcast(eventName, arg); } },
其次是模態框組件內部接收從父級組件傳遞過來的肯定和取消按鈕所觸發的事件名,點擊取消和肯定按鈕的時候觸發api
// 接收事件,得到須要取消和肯定按鈕的事件名 events: { 'tip': function(obj) { this.events = { cancel: obj.events.cancel, confirm: obj.events.confirm } } } // 取消按鈕 cancel:function() { this.$dispatch('transmit',this.events.cancel); } // 肯定按鈕 submit: function() { this.$dispatch('transmit',this.events.submit); }
在父級組件中調用模態框以下:
this.$dispatch('transmit','tip',{ events: { confirm: 'confirmEvent' } }); this.$once('confirmEvent',function() { callback(); }
先是傳遞tip事件,將事件名傳遞給模態框,再用$once監聽肯定或取消按鈕所觸發的事件,事件觸發後進行回調。
這種方法看起來是否是很暈?因此vue 2.0取消了$dispatch和$broadcast,咱們在最近的項目中雖然還在用1.0,可是也再也不用$dispatch和$broadcast,方便之後的升級。
這種方法來自vue-bootstrap-modal,點擊取消和肯定按鈕的時候分別emit一個事件,直接在組件上監聽這個事件,這種作法的好處是事件比較容易追蹤。
// 肯定按鈕 ok () { this.$emit('ok'); if (this.closeWhenOK) { this.show = false; } }, // 取消按鈕 cancel () { this.$emit('cancel'); this.show = false; },
調用:
<modal title="Modal Title" :show.sync="show" @ok="ok" @cancel="cancel"> Modal Text </modal>
可是咱們在使用的時候常常會遇到這樣的場景,在一個組件的內部,常常會用到多個對話框,對話框可能只是文字有點區別,回調不一樣,這時就須要在template中爲每一個對話框都寫一次<modal></modal>,有點麻煩。不想每次寫,能夠用v-for來遍歷,這篇文章關於 vue 彈窗組件的一些感想有我與做者的討論,能夠參考一下。
照例放張公衆號的二維碼,歡迎關注: