8428c4797a1371f6da76f6916d42ea2a1694a8f6b9abfbbd89f3ee0c330387c9ffb1a05118f1b4779eedf8a1c96f7b3b3bd44a6fec1622ebf78e385c6437dd 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. <template>
  2. <transition name="msgbox-fade">
  3. <div
  4. class="el-message-box__wrapper"
  5. tabindex="-1"
  6. v-show="visible"
  7. @click.self="handleWrapperClick"
  8. role="dialog"
  9. aria-modal="true"
  10. :aria-label="title || 'dialog'">
  11. <div class="el-message-box" :class="[customClass, center && 'el-message-box--center']">
  12. <div class="el-message-box__header" v-if="title !== null">
  13. <div class="el-message-box__title">
  14. <div
  15. :class="['el-message-box__status', icon]"
  16. v-if="icon && center">
  17. </div>
  18. <span>{{ title }}</span>
  19. </div>
  20. <button
  21. type="button"
  22. class="el-message-box__headerbtn"
  23. aria-label="Close"
  24. v-if="showClose"
  25. @click="handleAction(distinguishCancelAndClose ? 'close' : 'cancel')"
  26. @keydown.enter="handleAction(distinguishCancelAndClose ? 'close' : 'cancel')">
  27. <i class="el-message-box__close el-icon-close"></i>
  28. </button>
  29. </div>
  30. <div class="el-message-box__content">
  31. <div class="el-message-box__container">
  32. <div
  33. :class="['el-message-box__status', icon]"
  34. v-if="icon && !center && message !== ''">
  35. </div>
  36. <div class="el-message-box__message" v-if="message !== ''">
  37. <slot>
  38. <p v-if="!dangerouslyUseHTMLString">{{ message }}</p>
  39. <p v-else v-html="message"></p>
  40. </slot>
  41. </div>
  42. </div>
  43. <div class="el-message-box__input" v-show="showInput">
  44. <el-input
  45. v-model="inputValue"
  46. :type="inputType"
  47. @keydown.enter.native="handleInputEnter"
  48. :placeholder="inputPlaceholder"
  49. ref="input"></el-input>
  50. <div class="el-message-box__errormsg" :style="{ visibility: !!editorErrorMessage ? 'visible' : 'hidden' }">{{ editorErrorMessage }}</div>
  51. </div>
  52. </div>
  53. <div class="el-message-box__btns">
  54. <el-button
  55. :loading="cancelButtonLoading"
  56. :class="[ cancelButtonClasses ]"
  57. v-if="showCancelButton"
  58. :round="roundButton"
  59. size="small"
  60. @click.native="handleAction('cancel')"
  61. @keydown.enter="handleAction('cancel')">
  62. {{ cancelButtonText || t('el.messagebox.cancel') }}
  63. </el-button>
  64. <el-button
  65. :loading="confirmButtonLoading"
  66. ref="confirm"
  67. :class="[ confirmButtonClasses ]"
  68. v-show="showConfirmButton"
  69. :round="roundButton"
  70. size="small"
  71. @click.native="handleAction('confirm')"
  72. @keydown.enter="handleAction('confirm')">
  73. {{ confirmButtonText || t('el.messagebox.confirm') }}
  74. </el-button>
  75. </div>
  76. </div>
  77. </div>
  78. </transition>
  79. </template>
  80. <script type="text/babel">
  81. import Popup from 'element-ui/src/utils/popup';
  82. import Locale from 'element-ui/src/mixins/locale';
  83. import ElInput from 'element-ui/packages/input';
  84. import ElButton from 'element-ui/packages/button';
  85. import { addClass, removeClass } from 'element-ui/src/utils/dom';
  86. import { t } from 'element-ui/src/locale';
  87. import Dialog from 'element-ui/src/utils/aria-dialog';
  88. let messageBox;
  89. let typeMap = {
  90. success: 'success',
  91. info: 'info',
  92. warning: 'warning',
  93. error: 'error'
  94. };
  95. export default {
  96. mixins: [Popup, Locale],
  97. props: {
  98. modal: {
  99. default: true
  100. },
  101. lockScroll: {
  102. default: true
  103. },
  104. showClose: {
  105. type: Boolean,
  106. default: true
  107. },
  108. closeOnClickModal: {
  109. default: true
  110. },
  111. closeOnPressEscape: {
  112. default: true
  113. },
  114. closeOnHashChange: {
  115. default: true
  116. },
  117. center: {
  118. default: false,
  119. type: Boolean
  120. },
  121. roundButton: {
  122. default: false,
  123. type: Boolean
  124. }
  125. },
  126. components: {
  127. ElInput,
  128. ElButton
  129. },
  130. computed: {
  131. icon() {
  132. const { type, iconClass } = this;
  133. return iconClass || (type && typeMap[type] ? `el-icon-${ typeMap[type] }` : '');
  134. },
  135. confirmButtonClasses() {
  136. return `el-button--primary ${ this.confirmButtonClass }`;
  137. },
  138. cancelButtonClasses() {
  139. return `${ this.cancelButtonClass }`;
  140. }
  141. },
  142. methods: {
  143. getSafeClose() {
  144. const currentId = this.uid;
  145. return () => {
  146. this.$nextTick(() => {
  147. if (currentId === this.uid) this.doClose();
  148. });
  149. };
  150. },
  151. doClose() {
  152. if (!this.visible) return;
  153. this.visible = false;
  154. this._closing = true;
  155. this.onClose && this.onClose();
  156. messageBox.closeDialog(); // 解绑
  157. if (this.lockScroll) {
  158. setTimeout(this.restoreBodyStyle, 200);
  159. }
  160. this.opened = false;
  161. this.doAfterClose();
  162. setTimeout(() => {
  163. if (this.action) this.callback(this.action, this);
  164. });
  165. },
  166. handleWrapperClick() {
  167. if (this.closeOnClickModal) {
  168. this.handleAction(this.distinguishCancelAndClose ? 'close' : 'cancel');
  169. }
  170. },
  171. handleInputEnter() {
  172. if (this.inputType !== 'textarea') {
  173. return this.handleAction('confirm');
  174. }
  175. },
  176. handleAction(action) {
  177. if (this.$type === 'prompt' && action === 'confirm' && !this.validate()) {
  178. return;
  179. }
  180. this.action = action;
  181. if (typeof this.beforeClose === 'function') {
  182. this.close = this.getSafeClose();
  183. this.beforeClose(action, this, this.close);
  184. } else {
  185. this.doClose();
  186. }
  187. },
  188. validate() {
  189. if (this.$type === 'prompt') {
  190. const inputPattern = this.inputPattern;
  191. if (inputPattern && !inputPattern.test(this.inputValue || '')) {
  192. this.editorErrorMessage = this.inputErrorMessage || t('el.messagebox.error');
  193. addClass(this.getInputElement(), 'invalid');
  194. return false;
  195. }
  196. const inputValidator = this.inputValidator;
  197. if (typeof inputValidator === 'function') {
  198. const validateResult = inputValidator(this.inputValue);
  199. if (validateResult === false) {
  200. this.editorErrorMessage = this.inputErrorMessage || t('el.messagebox.error');
  201. addClass(this.getInputElement(), 'invalid');
  202. return false;
  203. }
  204. if (typeof validateResult === 'string') {
  205. this.editorErrorMessage = validateResult;
  206. addClass(this.getInputElement(), 'invalid');
  207. return false;
  208. }
  209. }
  210. }
  211. this.editorErrorMessage = '';
  212. removeClass(this.getInputElement(), 'invalid');
  213. return true;
  214. },
  215. getFirstFocus() {
  216. const btn = this.$el.querySelector('.el-message-box__btns .el-button');
  217. const title = this.$el.querySelector('.el-message-box__btns .el-message-box__title');
  218. return btn || title;
  219. },
  220. getInputElement() {
  221. const inputRefs = this.$refs.input.$refs;
  222. return inputRefs.input || inputRefs.textarea;
  223. },
  224. handleClose() {
  225. this.handleAction('close');
  226. }
  227. },
  228. watch: {
  229. inputValue: {
  230. immediate: true,
  231. handler(val) {
  232. this.$nextTick(_ => {
  233. if (this.$type === 'prompt' && val !== null) {
  234. this.validate();
  235. }
  236. });
  237. }
  238. },
  239. visible(val) {
  240. if (val) {
  241. this.uid++;
  242. if (this.$type === 'alert' || this.$type === 'confirm') {
  243. this.$nextTick(() => {
  244. this.$refs.confirm.$el.focus();
  245. });
  246. }
  247. this.focusAfterClosed = document.activeElement;
  248. messageBox = new Dialog(this.$el, this.focusAfterClosed, this.getFirstFocus());
  249. }
  250. // prompt
  251. if (this.$type !== 'prompt') return;
  252. if (val) {
  253. setTimeout(() => {
  254. if (this.$refs.input && this.$refs.input.$el) {
  255. this.getInputElement().focus();
  256. }
  257. }, 500);
  258. } else {
  259. this.editorErrorMessage = '';
  260. removeClass(this.getInputElement(), 'invalid');
  261. }
  262. }
  263. },
  264. mounted() {
  265. this.$nextTick(() => {
  266. if (this.closeOnHashChange) {
  267. window.addEventListener('hashchange', this.close);
  268. }
  269. });
  270. },
  271. beforeDestroy() {
  272. if (this.closeOnHashChange) {
  273. window.removeEventListener('hashchange', this.close);
  274. }
  275. setTimeout(() => {
  276. messageBox.closeDialog();
  277. });
  278. },
  279. data() {
  280. return {
  281. uid: 1,
  282. title: undefined,
  283. message: '',
  284. type: '',
  285. iconClass: '',
  286. customClass: '',
  287. showInput: false,
  288. inputValue: null,
  289. inputPlaceholder: '',
  290. inputType: 'text',
  291. inputPattern: null,
  292. inputValidator: null,
  293. inputErrorMessage: '',
  294. showConfirmButton: true,
  295. showCancelButton: false,
  296. action: '',
  297. confirmButtonText: '',
  298. cancelButtonText: '',
  299. confirmButtonLoading: false,
  300. cancelButtonLoading: false,
  301. confirmButtonClass: '',
  302. confirmButtonDisabled: false,
  303. cancelButtonClass: '',
  304. editorErrorMessage: null,
  305. callback: null,
  306. dangerouslyUseHTMLString: false,
  307. focusAfterClosed: null,
  308. isOnComposition: false,
  309. distinguishCancelAndClose: false
  310. };
  311. }
  312. };
  313. </script>