main.vue 8.38 KB
<template>
  <transition name="msgbox-fade">
    <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']">
        <div class="el-message-box__header" v-if="title !== null">
          <div class="el-message-box__title">
            <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 v-if="message !== ''" class="richText" v-html="message"></div>
        </div>
        <div class="el-message-box__btns">
          <el-button :loading="cancelButtonLoading" :class="[cancelButtonClasses]" :round="roundButton" size="small"
            @click.native="handleAction('cancel')" @keydown.enter="handleAction('cancel')">
            {{ cancelButtonText || t('el.messagebox.cancel') }}
          </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: {
      default: true
    },
    showClose: {
      type: Boolean,
      default: true
    },
    closeOnClickModal: {
      default: true
    },
    closeOnPressEscape: {
      default: true
    },
    closeOnHashChange: {
      default: true
    },
    center: {
      default: false,
      type: Boolean
    },
    roundButton: {
      default: false,
      type: Boolean
    }
  },

  components: {
    ElInput,
    ElButton
  },

  computed: {
    icon () {
      const { type, iconClass } = this;
      return iconClass || (type && typeMap[type] ? `el-icon-${typeMap[type]}` : '');
    },

    confirmButtonClasses () {
      return `el-button--primary ${this.confirmButtonClass}`;
    },
    cancelButtonClasses () {
      return `${this.cancelButtonClass}`;
    }
  },

  methods: {
    /**
     * @description: getSafeClose
     * @author: renchao
     */
    getSafeClose () {
      const currentId = this.uid;
      return () => {
        this.$nextTick(() => {
          if (currentId === this.uid) this.doClose();
        });
      };
    },
    /**
     * @description: doClose
     * @author: renchao
     */
    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);
      });
    },

    /**
     * @description: handleWrapperClick
     * @author: renchao
     */
    handleWrapperClick () {
      if (this.closeOnClickModal) {
        this.handleAction(this.distinguishCancelAndClose ? 'close' : 'cancel');
      }
    },

    /**
     * @description: handleInputEnter
     * @author: renchao
     */
    handleInputEnter () {
      if (this.inputType !== 'textarea') {
        return this.handleAction('confirm');
      }
    },

    /**
     * @description: handleAction
     * @param {*} action
     * @author: renchao
     */
    handleAction (action) {
      if (this.$type === 'prompt' && action === 'confirm' && !this.validate()) {
        return;
      }
      this.action = action;
      if (typeof this.beforeClose === 'function') {
        this.close = this.getSafeClose();
        this.beforeClose(action, this, this.close);
      } else {
        this.doClose();
      }
    },

    /**
     * @description: validate
     * @author: renchao
     */
    validate () {
      if (this.$type === 'prompt') {
        const inputPattern = this.inputPattern;
        if (inputPattern && !inputPattern.test(this.inputValue || '')) {
          this.editorErrorMessage = this.inputErrorMessage || t('el.messagebox.error');
          addClass(this.getInputElement(), 'invalid');
          return false;
        }
        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;
          }
          if (typeof validateResult === 'string') {
            this.editorErrorMessage = validateResult;
            addClass(this.getInputElement(), 'invalid');
            return false;
          }
        }
      }
      this.editorErrorMessage = '';
      removeClass(this.getInputElement(), 'invalid');
      return true;
    },
    /**
     * @description: getFirstFocus
     * @author: renchao
     */
    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;
    },
    /**
     * @description: getInputElement
     * @author: renchao
     */
    getInputElement () {
      const inputRefs = this.$refs.input.$refs;
      return inputRefs.input || inputRefs.textarea;
    },
    /**
     * @description: handleClose
     * @author: renchao
     */
    handleClose () {
      this.handleAction('close');
    }
  },

  watch: {
    inputValue: {
      immediate: true,
      handler (val) {
        this.$nextTick(_ => {
          if (this.$type === 'prompt' && val !== null) {
            this.validate();
          }
        });
      }
    },

    visible (val) {
      if (val) {
        this.uid++;
        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(() => {
      if (this.closeOnHashChange) {
        window.addEventListener('hashchange', this.close);
      }
    });
  },

  beforeDestroy () {
    if (this.closeOnHashChange) {
      window.removeEventListener('hashchange', this.close);
    }
    setTimeout(() => {
      messageBox.closeDialog();
    });
  },

  data () {
    return {
      uid: 1,
      title: undefined,
      message: '',
      type: '',
      iconClass: '',
      customClass: '',
      showInput: false,
      inputValue: null,
      inputPlaceholder: '',
      inputType: 'text',
      inputPattern: null,
      inputValidator: null,
      inputErrorMessage: '',
      showConfirmButton: true,
      showCancelButton: false,
      action: '',
      confirmButtonText: '',
      cancelButtonText: '',
      confirmButtonLoading: false,
      cancelButtonLoading: false,
      confirmButtonClass: '',
      confirmButtonDisabled: false,
      cancelButtonClass: '',
      editorErrorMessage: null,
      callback: null,
      dangerouslyUseHTMLString: false,
      focusAfterClosed: null,
      isOnComposition: false,
      distinguishCancelAndClose: false
    };
  }
};
</script>
<style scoped>
/deep/.el-message-box {
  width: 500px;
  max-height: 95%;
}

/deep/.message-title {
  font-size: 18px;
  font-weight: 700;
  margin-bottom: 5px;
}

/deep/.el-message-box__content img {
  width: 100%;
  height: 100%;
}

/deep/.el-message-box__content {
  padding-top: 0;
}
</style>