MessageBox组件源码,有添加部分注释javascript
<template> <transition name="msgbox-fade"> <!--包裹弹框的div--> <div class="el-message-box__wrapper" tabindex="-1" v-show="visible" @click.self="handleWrapperClick" role="dialog" aria-modal="true" :aria-label="title || 'dialog'"> <!--中间的弹框--> <div class="el-message-box" :class="[customClass, center && 'el-message-box--center']"> <!--弹窗头部,包含:标题和关闭按钮;title必须设置,若是不设置不显示头部信息--> <div class="el-message-box__header" v-if="title !== null"> <!--头部标题--> <div class="el-message-box__title"> <!--center为true时,为居中布局,可设置图标,图标和标题居中显示--> <div :class="['el-message-box__status', icon]" v-if="icon && center"> </div> <span>{{ title }}</span> </div> <!--头部关闭按钮--> <button type="button" class="el-message-box__headerbtn" aria-label="Close" v-if="showClose" @click="handleAction(distinguishCancelAndClose ? 'close' : 'cancel')" @keydown.enter="handleAction(distinguishCancelAndClose ? 'close' : 'cancel')"> <i class="el-message-box__close el-icon-close"></i> </button> </div> <!--弹框内容部分--> <div class="el-message-box__content"> <!--消息类型的图标--> <div :class="['el-message-box__status', icon]" v-if="icon && !center && message !== ''"> </div> <!--弹框的主要内容--> <div class="el-message-box__message" v-if="message !== ''"> <slot> <!--dangerouslyUseHTMLString是否将 message 属性做为 HTML 片断处理,若是该字段不存在时,直接显示message--> <p v-if="!dangerouslyUseHTMLString">{{ message }}</p> <!--若是存在将message做为HTML处理--> <p v-else v-html="message"></p> </slot> </div> <!--输入框部分,根据设置的showInput显示--> <div class="el-message-box__input" v-show="showInput"> <el-input v-model="inputValue" :type="inputType" @keydown.enter.native="handleInputEnter" :placeholder="inputPlaceholder" ref="input"> </el-input> <!--检验错误的提示信息--> <div class="el-message-box__errormsg" :style="{ visibility: !!editorErrorMessage ? 'visible' : 'hidden' }">{{ editorErrorMessage }}</div> </div> </div> <!--弹框底部,包含:确认、取消按钮--> <div class="el-message-box__btns"> <el-button :loading="cancelButtonLoading" :class="[ cancelButtonClasses ]" v-if="showCancelButton" :round="roundButton" size="small" @click.native="handleAction('cancel')" @keydown.enter="handleAction('cancel')"> {{ cancelButtonText || t('el.messagebox.cancel') }} </el-button> <el-button :loading="confirmButtonLoading" ref="confirm" :class="[ confirmButtonClasses ]" v-show="showConfirmButton" :round="roundButton" size="small" @click.native="handleAction('confirm')" @keydown.enter="handleAction('confirm')"> {{ confirmButtonText || t('el.messagebox.confirm') }} </el-button> </div> </div> </div> </transition> </template> <script type="text/babel"> import Popup from 'element-ui/src/utils/popup'; import Locale from 'element-ui/src/mixins/locale'; import ElInput from 'element-ui/packages/input'; import ElButton from 'element-ui/packages/button'; import { addClass, removeClass } from 'element-ui/src/utils/dom'; import { t } from 'element-ui/src/locale'; import Dialog from 'element-ui/src/utils/aria-dialog'; let messageBox; //定义的图标类型 let typeMap = { success: 'success', info: 'info', warning: 'warning', error: 'error' }; export default { mixins: [Popup, Locale], props: { modal: { default: true }, lockScroll: { //是否在 MessageBox 出现时将 body 滚动锁定 default: true }, showClose: { //MessageBox 是否显示右上角关闭按钮 type: Boolean, default: true }, closeOnClickModal: { //是否可经过点击遮罩关闭 MessageBox default: true }, closeOnPressEscape: { //是否可经过按下 ESC 键关闭 MessageBox default: true }, closeOnHashChange: { //是否在 hashchange 时关闭 MessageBox default: true }, center: { //是否居中布局 default: false, type: Boolean }, roundButton: { //是否使用圆角按钮 default: false, type: Boolean } }, components: { ElInput, ElButton }, computed: { icon() { const { type, iconClass } = this; //若是用户设置了自定义图标的类名,就显示自定义图标;若是没有就显示设置的type图标,不然就不显示图标 return iconClass || (type && typeMap[type] ? `el-icon-${ typeMap[type] }` : ''); }, //添加肯定按钮的自定义类名 confirmButtonClasses() { return `el-button--primary ${ this.confirmButtonClass }`; }, //添加取消按钮的自定义类名 cancelButtonClasses() { return `${ this.cancelButtonClass }`; } }, methods: { getSafeClose() { const currentId = this.uid; return () => { this.$nextTick(() => { if (currentId === this.uid) this.doClose(); }); }; }, doClose() { if (!this.visible) return; this.visible = false; this._closing = true; this.onClose && this.onClose(); messageBox.closeDialog(); // 解绑 if (this.lockScroll) { setTimeout(this.restoreBodyStyle, 200); } this.opened = false; this.doAfterClose(); setTimeout(() => { if (this.action) this.callback(this.action, this); }); }, //点击弹框时,根据closeOnClickModal来是否可经过点击遮罩关闭 MessageBox handleWrapperClick() { // 若是closeOnClickModal设置为true if (this.closeOnClickModal) { //判断是否将取消(点击取消按钮)与关闭(点击关闭按钮或遮罩层、按下 ESC 键)进行区分 //若是区分则this.handleAction('close');不然this.handleAction('cancel'); this.handleAction(this.distinguishCancelAndClose ? 'close' : 'cancel'); } }, handleInputEnter() { if (this.inputType !== 'textarea') { return this.handleAction('confirm'); } }, handleAction(action) { // 若是当前是this.$prompt if (this.$type === 'prompt' && action === 'confirm' && !this.validate()) { return; } this.action = action; //判断beforeClose是不是函数,也就是用户是否认义了beforeClose函数 if (typeof this.beforeClose === 'function') { this.close = this.getSafeClose(); this.beforeClose(action, this, this.close); } else { //若是用户没有定义beforeClose,就调doClose直接关闭弹框 this.doClose(); } }, //该方法主要是用于用户在调用$prompt方法打开消息提示时,校验input输入框的值 validate() { //$prompt方法便可打开消息提示,它模拟了系统的 prompt if (this.$type === 'prompt') { //获取用户本身定义的匹配模式 const inputPattern = this.inputPattern; //若是用户本身定义了匹配模式,而且用户输入校验不经过 if (inputPattern && !inputPattern.test(this.inputValue || '')) { //显示用户本身定义的校验不经过时的提示信息;当用户未定义校验不经过的提示信息时,t('el.messagebox.error')输出提示:输入的数据不合法! this.editorErrorMessage = this.inputErrorMessage || t('el.messagebox.error'); //这里主要是在校验不经过时,给input加上的类名中加上invalid,变成class="el-input__inner invalid" //经过.el-message-box__input input.invalid{border-color: #f56c6c;}改变input的border为红色 addClass(this.getInputElement(), 'invalid'); return false; } //输入框的校验函数;能够返回布尔值或字符串,若返回一个字符串, 则返回结果会被赋值给 inputErrorMessage const inputValidator = this.inputValidator; //若是校验函数存在 if (typeof inputValidator === 'function') { const validateResult = inputValidator(this.inputValue); //校验不经过,显示校验不经过的红色提示信息 if (validateResult === false) { this.editorErrorMessage = this.inputErrorMessage || t('el.messagebox.error'); addClass(this.getInputElement(), 'invalid'); return false; } //若返回一个字符串, 则返回结果会被赋值给 inputErrorMessage if (typeof validateResult === 'string') { this.editorErrorMessage = validateResult; addClass(this.getInputElement(), 'invalid'); return false; } } } //若是校验经过,则不显示错误提示,并删除类名invalid this.editorErrorMessage = ''; removeClass(this.getInputElement(), 'invalid'); return true; }, getFirstFocus() { const btn = this.$el.querySelector('.el-message-box__btns .el-button'); const title = this.$el.querySelector('.el-message-box__btns .el-message-box__title'); return btn || title; }, //获取input元素 getInputElement() { const inputRefs = this.$refs.input.$refs; return inputRefs.input || inputRefs.textarea; } }, watch: { inputValue: { immediate: true, handler(val) { this.$nextTick(_ => { if (this.$type === 'prompt' && val !== null) { this.validate(); } }); } }, visible(val) { if (val) { this.uid++; if (this.$type === 'alert' || this.$type === 'confirm') { this.$nextTick(() => { this.$refs.confirm.$el.focus(); }); } this.focusAfterClosed = document.activeElement; messageBox = new Dialog(this.$el, this.focusAfterClosed, this.getFirstFocus()); } // prompt if (this.$type !== 'prompt') return; if (val) { setTimeout(() => { if (this.$refs.input && this.$refs.input.$el) { this.getInputElement().focus(); } }, 500); } else { this.editorErrorMessage = ''; removeClass(this.getInputElement(), 'invalid'); } } }, mounted() { this.$nextTick(() => { //根据设置的closeOnHashChange参数判断是否在 hashchange 时关闭 MessageBox if (this.closeOnHashChange) { //若是须要,则在元素挂载以后,给window添加hashchange事件 window.addEventListener('hashchange', this.close); } }); }, beforeDestroy() { // 组件销毁时移除hashchange事件 if (this.closeOnHashChange) { window.removeEventListener('hashchange', this.close); } setTimeout(() => { messageBox.closeDialog(); }); }, data() { return { uid: 1, title: undefined, //MessageBox 标题 message: '', //MessageBox 消息正文内容 type: '', //消息类型,用于显示图标 iconClass: '', //自定义图标的类名,会覆盖 type customClass: '', showInput: false, //是否显示输入框 inputValue: null, //输入框的初始文本 inputPlaceholder: '', //输入框的占位符 inputType: 'text', //输入框的类型 inputPattern: null,//输入框的校验表达式 inputValidator: null, //输入框的校验函数。能够返回布尔值或字符串,若返回一个字符串, 则返回结果会被赋值给 inputErrorMessage inputErrorMessage: '', //校验未经过时的提示文本 showConfirmButton: true, //是否显示肯定按钮 showCancelButton: false, //是否显示取消按钮 action: '', confirmButtonText: '', //肯定按钮的文本内容 cancelButtonText: '', //取消按钮的文本内容 confirmButtonLoading: false, cancelButtonLoading: false, confirmButtonClass: '', //肯定按钮的自定义类名 confirmButtonDisabled: false, cancelButtonClass: '', //取消按钮的自定义类名 editorErrorMessage: null, callback: null, dangerouslyUseHTMLString: false, //是否将取消(点击取消按钮)与关闭(点击关闭按钮或遮罩层、按下 ESC 键)进行区分 focusAfterClosed: null, isOnComposition: false, distinguishCancelAndClose: false //是否将取消(点击取消按钮)与关闭(点击关闭按钮或遮罩层、按下 ESC 键)进行区分 }; } }; </script>
const defaults = { title: null, //MessageBox 标题 message: '', //MessageBox 消息正文内容 type: '', //消息类型,用于显示图标 iconClass: '', //自定义图标的类名,会覆盖 type showInput: false, //是否显示输入框 showClose: true, //MessageBox 是否显示右上角关闭按钮 modalFade: true, lockScroll: true,//是否在 MessageBox 出现时将 body 滚动锁定 closeOnClickModal: true, //是否可经过点击遮罩关闭 MessageBox closeOnPressEscape: true, //是否可经过按下 ESC 键关闭 MessageBox closeOnHashChange: true, //是否在 hashchange 时关闭 MessageBox inputValue: null, //输入框的初始文本 inputPlaceholder: '', //输入框的占位符 inputType: 'text', //输入框的类型 inputPattern: null, //输入框的校验表达式 inputValidator: null, //输入框的校验函数。能够返回布尔值或字符串,若返回一个字符串, 则返回结果会被赋值给 inputErrorMessage inputErrorMessage: '', //校验未经过时的提示文本 showConfirmButton: true, //是否显示肯定按钮 showCancelButton: false, //是否显示取消按钮 confirmButtonPosition: 'right', // confirmButtonHighlight: false, cancelButtonHighlight: false, confirmButtonText: '', //肯定按钮的文本内容 cancelButtonText: '', //取消按钮的文本内容 confirmButtonClass: '', //肯定按钮的自定义类名 cancelButtonClass: '', //取消按钮的自定义类名 customClass: '', //MessageBox 的自定义类名 beforeClose: null, //MessageBox 关闭前的回调,会暂停实例的关闭 dangerouslyUseHTMLString: false, //是否将取消(点击取消按钮)与关闭(点击关闭按钮或遮罩层、按下 ESC 键)进行区分 center: false, //是否居中布局 roundButton: false, //是否使用圆角按钮 distinguishCancelAndClose: false //是否将取消(点击取消按钮)与关闭(点击关闭按钮或遮罩层、按下 ESC 键)进行区分 }; import Vue from 'vue'; import msgboxVue from './main.vue'; import merge from 'element-ui/src/utils/merge'; import { isVNode } from 'element-ui/src/utils/vdom'; //建立MessageBox的构造器,包含msgboxVue组件选项的对象做为Vue.extend的参数,返回一个VueComponent类,VueComponent类是Vue类的子类 //Vue.extend是一个类构造器,用来建立一个子类vue并返回构造函数,而Vue.component它的任务是将给定的构造函数与字符串ID相关联,以便Vue.js能够在模板中接收它。 const MessageBoxConstructor = Vue.extend(msgboxVue); let currentMsg, instance; let msgQueue = []; const defaultCallback = action => { if (currentMsg) { let callback = currentMsg.callback; if (typeof callback === 'function') { if (instance.showInput) { callback(instance.inputValue, action); } else { callback(action); } } if (currentMsg.resolve) { // 点击肯定或者去下关闭按钮时,在此处调对应的方法进行处理 if (action === 'confirm') { //执行确认后的回调方法 if (instance.showInput) { currentMsg.resolve({ value: instance.inputValue, action }); } else { currentMsg.resolve(action); } } else if (currentMsg.reject && (action === 'cancel' || action === 'close')) { //执行取消和关闭后的回调方法 currentMsg.reject(action); } } } }; const initInstance = () => { //instance为messageBox的实例 instance = new MessageBoxConstructor({ el: document.createElement('div') }); instance.callback = defaultCallback; }; const showNextMsg = () => { if (!instance) { // 调用initInstance初始化实例,返回messageBox的实例对象 initInstance(); } instance.action = ''; if (!instance.visible || instance.closeTimer) { if (msgQueue.length > 0) { currentMsg = msgQueue.shift(); let options = currentMsg.options; //将用户设置的属性和方法挂载到messageBox的实例对象instance上去 for (let prop in options) { if (options.hasOwnProperty(prop)) { instance[prop] = options[prop]; } } //当用户未设置callback时,将defaultCallback赋值给instance.callback if (options.callback === undefined) { instance.callback = defaultCallback; } let oldCb = instance.callback; instance.callback = (action, instance) => { oldCb(action, instance); showNextMsg(); }; if (isVNode(instance.message)) { instance.$slots.default = [instance.message]; instance.message = null; } else { delete instance.$slots.default; } ['modal', 'showClose', 'closeOnClickModal', 'closeOnPressEscape', 'closeOnHashChange'].forEach(prop => { if (instance[prop] === undefined) { instance[prop] = true; } }); document.body.appendChild(instance.$el); Vue.nextTick(() => { instance.visible = true; }); } } }; const MessageBox = function(options, callback) { if (Vue.prototype.$isServer) return; if (typeof options === 'string' || isVNode(options)) { options = { message: options }; if (typeof arguments[1] === 'string') { options.title = arguments[1]; } } else if (options.callback && !callback) { callback = options.callback; } if (typeof Promise !== 'undefined') { return new Promise((resolve, reject) => { // eslint-disable-line // options合并默认的全部参数和用户设置的参数 msgQueue.push({ options: merge({}, defaults, MessageBox.defaults, options), callback: callback, resolve: resolve, reject: reject }); showNextMsg(); }); } else { msgQueue.push({ options: merge({}, defaults, MessageBox.defaults, options), callback: callback }); showNextMsg(); } }; MessageBox.setDefaults = defaults => { MessageBox.defaults = defaults; }; //this.$alert方法 MessageBox.alert = (message, title, options) => { //若是title的类型为object时,用户可能没传title,则将title的值赋值给options if (typeof title === 'object') { options = title; title = ''; } else if (title === undefined) { title = ''; } //合并用户传的参数,并调用MessageBox方法 return MessageBox(merge({ title: title, message: message, $type: 'alert', closeOnPressEscape: false, closeOnClickModal: false }, options)); }; //this.$confirm方法,分析同MessageBox.alert方法 MessageBox.confirm = (message, title, options) => { if (typeof title === 'object') { options = title; title = ''; } else if (title === undefined) { title = ''; } return MessageBox(merge({ title: title, message: message, $type: 'confirm', showCancelButton: true }, options)); }; //this.$prompt方法,分析同MessageBox.alert方法 MessageBox.prompt = (message, title, options) => { if (typeof title === 'object') { options = title; title = ''; } else if (title === undefined) { title = ''; } return MessageBox(merge({ title: title, message: message, showCancelButton: true, showInput: true, $type: 'prompt' }, options)); }; MessageBox.close = () => { instance.doClose(); instance.visible = false; msgQueue = []; currentMsg = null; }; export default MessageBox; export { MessageBox };