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