利用 Vue.extend写一个全局toast提示

此人很懒,就不写文字描述了,这篇文章是基于饿了么的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... }
});

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值