利用 Vue.extend写一个全局toast提示
程序员文章站
2022-03-11 21:41:31
...
此人很懒,就不写文字描述了,这篇文章是基于饿了么的message提示框做的一个修改,请悉知
效果:
话不多说,上代码
toast.vue
<!--
@description toast提示框
@author: hruomei
@update
@date: 2020-12-03 17:41:04
-->
<template>
<transition name="ilink-toast-fade" @after-leave="handleAfterLeave">
<div
:class="[
'ilink-toast',
type && !iconClass ? `ilink-toast--${type}` : '',
customClass,
]"
:style="positionStyle"
v-show="visible"
@mouseenter="clearTimer"
@mouseleave="startTimer"
role="toast"
>
<i :class="iconClass" v-if="iconClass"></i>
<i :class="typeClass" v-else></i>
<slot>
<p v-if="!dangerouslyUseHTMLString" class="ilink-toast__content">
{{ message }}
</p>
<p v-else v-html="message" class="ilink-toast__content"></p>
</slot>
<i v-if="showClose" class="ilink-toast__closeBtn el-icon-close" @click="close"></i>
</div>
</transition>
</template>
<script type="text/babel">
const typeMap = {
success: "success",
info: "info",
warning: "warning",
error: "error",
};
export default {
data() {
return {
visible: false,
message: "",
duration: 3000,
type: "info",
iconClass: "",
customClass: "",
onClose: null,
showClose: false,
closed: false,
verticalOffset: 20,
timer: null,
dangerouslyUseHTMLString: false
};
},
computed: {
typeClass() {
return this.type && !this.iconClass
? `ilink-toast__icon el-icon-${typeMap[this.type]}`
: "";
},
positionStyle() {
return {
top: `${this.verticalOffset}px`,
};
},
},
watch: {
closed(newVal) {
if (newVal) {
this.visible = false;
}
},
},
methods: {
handleAfterLeave() {
this.$destroy(true);
this.$el.parentNode.removeChild(this.$el);
},
close() {
this.closed = true;
if (typeof this.onClose === "function") {
this.onClose(this);
}
},
clearTimer() {
clearTimeout(this.timer);
},
startTimer() {
if (this.duration > 0) {
this.timer = setTimeout(() => {
if (!this.closed) {
this.close();
}
}, this.duration);
}
},
keydown(e) {
if (e.keyCode === 27) { // esc关闭消息
if (!this.closed) {
this.close();
}
}
},
},
mounted() {
this.startTimer();
document.addEventListener("keydown", this.keydown);
},
beforeDestroy() {
document.removeEventListener("keydown", this.keydown);
},
};
</script>
<style lang="scss">
.ilink-toast {
$color-info: #0D76FF;
$color-waring: #E6A23C;
$color-error: #F56C6C;
$color-success: #67C23A;
max-width: 380px;
box-sizing: border-box;
position: fixed;
left: 50%;
top: 20px;
transform: translateX(-50%);
background: #E5F1FF;
opacity: 0.9;
border-radius: 4px;
transition: opacity .1s, transform .2s, top .2s;
transition-timing-function: cubic-bezier(.45,.48,.49,.98);
overflow: hidden;
padding: 12px 17px;
display: flex;
align-items: center;
p {
margin: 0;
}
&__icon {
margin-right: 8px;
font-size: 14px;
}
&__content {
padding: 0;
font-size: 12px;
line-height: 1;
color: #18191A;
}
&__closeBtn {
cursor: pointer;
color: #9dabc7;
font-size: 14px;
margin-left: 10px;
&:hover {
color: #909399;
}
}
.el-icon-success {
color: $color-success
}
.el-icon-error {
color: $color-error
}
.el-icon-info {
color: $color-info;
}
.el-icon-warning {
color: $color-waring;
}
}
.ilink-toast-fade-enter,
.ilink-toast-fade-leave-active {
opacity: 0;
transform: translate(-50%, -100%);
}
</style>
toast.js
/**
* @description toast 提示
* @author: hruomei
* @update
* @date: 2020-12-03 17:42:08
*/
import Vue from 'vue';
import ToastComponent from './index.vue';
let ToastConstructor = Vue.extend(ToastComponent);
function isVNode(node) {
return node !== null && typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'componentOptions');
};
let instance;
let instances = [];
let seed = 1;
const Toast = function (options) {
options = options || {};
if (typeof options === 'string') {
options = {
message: options
};
}
let userOnClose = options.onClose;
let id = 'toast_' + seed++;
options.onClose = function () {
Toast.close(id, userOnClose);
};
instance = new ToastConstructor({
data: options
});
instance.id = id;
if (isVNode(instance.message)) {
instance.$slots.default = [instance.message];
instance.message = null;
}
instance.$mount();
document.body.appendChild(instance.$el);
let verticalOffset = options.offset || 20;
instances.forEach(item => {
verticalOffset += item.$el.offsetHeight + 16;
});
instance.verticalOffset = verticalOffset;
instance.visible = true;
instance.$el.style.zIndex = 2000;
instances.push(instance);
return instance;
};
['success', 'warning', 'info', 'error'].forEach(type => {
Toast[type] = options => {
if (typeof options === 'string') {
options = {
message: options
};
}
options.type = type;
return Toast(options);
};
});
Toast.close = function (id, userOnClose) {
let len = instances.length;
let index = -1;
let removedHeight;
for (let i = 0; i < len; i++) {
if (id === instances[i].id) {
removedHeight = instances[i].$el.offsetHeight;
index = i;
if (typeof userOnClose === 'function') {
userOnClose(instances[i]);
}
instances.splice(i, 1);
break;
}
}
if (len <= 1 || index === -1 || index > instances.length - 1) return;
for (let i = index; i < len - 1; i++) {
let dom = instances[i].$el;
dom.style['top'] = parseInt(dom.style['top'], 10) - removedHeight - 16 + 'px';
}
};
Toast.closeAll = function () {
for (let i = instances.length - 1; i >= 0; i--) {
instances[i].close();
}
};
export default Toast;
挂载
import Vue from 'vue'
import Toast from '@/components/Toast/toast.js'
Vue.prototype.$toast = Toast;
.vue组件内使用
this.$toast('添加参会人异常');
// 或
this.$toast({
type: 'error',
message: '添加参会人异常'
});
.js文件内使用
import Toast from '@/components/Toast/toast.js';
Toast('提示消息');
// 或
Toast({
type: 'info',
message: '提示消息',
onClose: () => { // do... }
});
// 或
Toast.success('提示信息');
// 或
Toast.success({
message: '提示消息',
onClose: () => { // do... }
});
上一篇: Leetcode:344.反转字符串