vue3.x全局toast、message、loading组件

vue3.x全局toast、message、loading组件

    • Toast组件
    • loading

Toast组件

  • 在 src/components下创建toast文件夹,并依此创建index.vue和index.js

1、index.vue
一般toast会有如下功能:背景色、字体颜色、文本、停留时间

<template>
<div class="toast-box" >
    <p class="toast-value" :style="{background: background, color: color}">
        {{ value }}
    </p> 
</div>
</template>    
<script>
    import { defineComponent } from 'vue'
    export default defineComponent({
        name: 'Toast',
        props: {
            value: {
                type: String,
                default: ''
            },
            duration: {
                type: Number,
                default: 3000
            },
            background: {
                type: String,
                default: '#000'
            },
            color: {
                type: String,
                default: '#fff'
            }
        }
    })
</script>

<style>
.toast-box  {
    position: fixed;
    width: 100vw;
    height: 100vh;
    top: 0;
    left: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
}
    .toast-value {
        max-width: 100px;
        background: rgb(8, 8, 8);
        padding: 8px 10px;
        border-radius: 4px;
        text-align: center;
        display: inline-block;
        animation: anim 0.5s;
    }
    @keyframes anim { 
            0% {opacity: 0;}
            100%{opacity:1;
        }
    }
    .toast-value.remove{
        animation: remove 0.5s;
    }
     @keyframes remove { 
            0% {opacity: 1;}
            100%{opacity:0;
        }
    }
</style>

2、index.js 导出Toast方法

  • 创建
    首先使用createVNode方法创建一个vNode独享
    使用render方法转换成真实dom
    添加到body

  • 销毁
    首先添加一个淡入淡出效果
    使用render将真实设置为null
    移除创建的dom

以下代码为TS写法,不支持部分去掉代码即可

import { createVNode, render } from 'vue'
import toastTemplate from './index.vue'
export interface IProps {
    value?: string;
    duration?: number;
    background?: string;
    color?: string;
}
const defaultOpt = { // 创建默认参数
    duration: 3000
}

export interface ResultParams {
    destory?: () => void;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
const Toast = (options: IProps):ResultParams => {
    const container = document.createElement('div')
    const opt = {...defaultOpt,...options}
    const vm = createVNode(toastTemplate, opt) // 创建vNode
    render(vm, container)
    document.body.appendChild(container)       // 添加到body上
    const destory =  ()=> {
        const dom = vm.el as HTMLDivElement
        if(dom.querySelector('.toast-value')) {
            dom.querySelector('.toast-value')?.classList.add('remove') // 销毁时添加淡入淡出效果
            const t = setTimeout(() => {             // 淡入淡出效果之后删除dom节点
                render(null, container)
                document.body.removeChild(container)
                clearTimeout(t)
            },500);
        } 
    }
    if(opt.duration) {                            // 如果传入的值为0可以持续保留在页面,需要手动销毁
        const timer = setTimeout(()=> {
            destory()
            clearTimeout(timer)
        }, opt.duration)
    }
    return {
        destory
    }
}
export default Toast

3、main.js插件全局引入

import Toast from '@/components/Toast/index'

app.config.globalProperties.$toast = Toast;
// app.use(Toast) 用use来全局载入会导致我们不能使用this的地方不太好调用。

4、使用

this.$toast({
	value: 'toast',
    duration: 0, // 如果大于0则不必使用destory方法
    background: '#000',
    color: '#fff'
})
setTimeout(() => {
  this.$toast.destory && this.$toast.destory()
}, 2000)

setup内使用

import { getCurrentInstance} from 'vue'

setup() {
  const { proxy } = getCurrentInstance();
  const showToastEvent = () => {
  	proxy.$toast({
		value: 'toast',
	    duration: 1000,
	    background: '#000',
	    color: '#fff'
	})
  }
  return {
  	showToastEvent,
  }
}

loading

<template>
  <div class="loading">
    加载中...
  </div>
</template>

<script>
  export default {
    name: "loading",
  }
</script>

<style scoped>
  .loading {
    position: fixed;
    left: 50%;
    top: 50%;
    background-color: rgba(0, 0, 0, 0.2);
    color: white;
    transform: translate(-50%, -50%);
    border-radius: 4px;
    padding: 8px 10px;
    z-index: 1000;
  }
</style>
import { createApp } from "vue"

import Loading from './loading.vue'

export default {
  instance: null,
  parent: null,
  times: 0, 
  // 为了保证多个同时loading的时候,只显示一个,并且需要全部close之后才消失
  open() {
    if (this.times == 0) {
      this.instance = createApp(Loading)
      this.parent = document.createElement("div")
      let appDom = document.getElementById('app')
      appDom.appendChild(this.parent)
      this.instance.mount(this.parent)
    }
    this.times ++
  },
  close() {
    this.times --
    if (this.times <= 0) {
      this.times = 0
      let appDom = document.getElementById('app')
      this.instance.unmount(this.parent)
      appDom.removeChild(this.parent)
    }
  }
};
import loading from '@/components/loading/index'
app.config.globalProperties.$loading = loading;

你可能感兴趣的:(vue,javascript,vue.js,typescript)