vue 自定义类似 antd-vue message 功能

98 阅读1分钟

更新

的确不对,应该写个插件,然后使用inject 和 provide进行调用

新建组件 Message.vue

<template>
    <div class="messages ">
        <div class="message "  v-for="item in messages">
            <div class="content">
                <div :class="item.style"></div>
                {{ item.value }}</div>
        </div>
    </div>
</template>

<script setup lang="ts">

import { onMounted, reactive } from 'vue';
const props = defineProps({
  myProvide: {
    type: Function,
    required: true
  },
})
onMounted(()=>{
    props.myProvide('$message', showMessage)
})


const messages:MessageCustomInterface[] = reactive([])
const clearFirstMessage = ()=>{
    setTimeout(()=>{
        if(messages.length>0) messages.splice(0,1) 
    },3000)
}

const success =(content:string)=>{
    updateMessage(content,'success')
}

const warning =(content:string)=>{
    updateMessage(content,'warning')
}

const error =(content:string)=>{
    updateMessage(content,'error')
}

const showMessage = {
    success:success,
    warning:warning,
    error:error,
}

const updateMessage = (content:string,type:string)=>{
    messages.push({
        value:content,
        style:type
    })
    clearFirstMessage()
}

defineExpose({
    success,
    warning,
    error
})


</script>

<style lang="less" scoped>
    .messages{
        margin: 20px auto;
        line-height: 30px;
        height: 30px;
        text-align: center;

        .message{
            margin-top: 30px;
            .content{
                display: inline-block;
                padding-left: 10px;
                padding-right: 20px;
                font-size: 14px;
                background-position: left 10px top 5px;
                background-size: 20px 20px;
                background-color: #1A3D96;
                // border: 1px solid #4573d679;
                border-radius: 6px;
                color: #fff;
                background-repeat: no-repeat;
                // box-shadow:  0 0 0 20px inset #285EE5;
                box-shadow: 0px 0px 10px 0px rgba(40, 94, 229, 0.7);

                div{
                    width: 20px;
                    height: 20px;
                    display: inline-block;
                    vertical-align: middle;
                    background-position: center;
                    background-repeat: no-repeat;
                    background-size: 100% 100%;
                    margin-right: 10px;
                }
            }
        }
    }

    .success{
        background-image: url(@/assets/custom/info.png);
        color: #4DE4FF;
    }

    .warning{
        color: orange;
        background-image: url(@/assets/custom/warning.png);
    }

    .error{
        background-image: url(@/assets/custom/error.png);
        color: #ff0000;
    }
</style>

新建插件

import Message from '@/components/custom/Message';
import { createApp } from 'vue'


export default {
    install: (app, options) => {
        const myProvide = app.provide;
        const messageConstructor = createApp(MessageCustom,{myProvide});
        const panel = document.createElement('div')
        panel.className = 'message_overall';
        panel.id =  'message_overall';
        document.body.appendChild(panel);
        const message_overall = document.getElementById('message_overall')
        const messageOverall = messageConstructor.mount(message_overall);
        app.config.globalProperties.$messageOverall = messageOverall;
    }
  }

组件引用

import { inject } from 'vue';
const $message:any = inject("$message");
$message.success("成功!!!")

不知道能不能用hooks实现

以下是原文


我不知道我这个对不对,映像里好像不建议直接操作dom,但我一下想不到别的方式 如果我思路不对或者有什么可以改进的地方希望大佬提示下

定义


class MessageBox {
    constructor(){
        const panel = document.createElement('div')
        panel.className = 'message_overall'
        panel.id =  'message_overall'
        document.body.appendChild(panel)
        this.message_overall = document.getElementById('message_overall')

    }

    getDom(class_name,content ){
        const div = document.createElement('div')
        div.className = class_name;
        div.innerHTML = `
            <div class="content">${content}</div>
        `
        return div
    }
    success(content){
        const dom = this.getDom('message success',content)
        this.message_overall.insertBefore(dom,this.message_overall.childNodes[0])
        this.clearDom()
    }

    warning(content){
        const dom = this.getDom('message warning',content)
        this.message_overall.insertBefore(dom,this.message_overall.childNodes[0])
        this.clearDom()
    }

    error(content){
        const dom = this.getDom('message error',content)
        this.message_overall.insertBefore(dom,this.message_overall.childNodes[0])
        this.clearDom()
    }

    clearDom(){
        setTimeout(()=>{
            this.message_overall.removeChild(this.message_overall.lastElementChild)
        },3000)
    }
}

export const messageOverall = new MessageBox();

main.js 里面导入

import { createApp } from 'vue'
import App from './App.vue'
import {messageOverall} from '@/utils/model'
import router from './router'

const app = createApp(AsyncApp);
window.$messageOverall = messageOverall
// app.config.globalProperties.$messageOverall = messageOverall; // 不知道为啥无效
app.use(router).mount('#app')

样式我随便写的

.message_overall{
    position: fixed;
    left: 0;
    top: 0;
    height: 100%;
    width: 100%;
    pointer-events: none;    

    .message{
        margin: 20px auto;
        
        line-height: 30px;
        height: 30px;
        text-align: center;
        .content{
            display: inline-block;
            background-color: black;
            padding-left: 10px;
            padding-right: 40px;
        }
    }

    .success{
        color: #00ff00;
    }

    .warning{
        color: orange;
    }

    .error{
        color: #ff0000;
    }
}

使用

<template>
    <button @click="messageTip('success')">全局提示-成功</button>
    <button @click="messageTip('warning')">全局提示-失败</button>
    <button @click="messageTip('error')">全局提示-报警</button>
</template>
    
<script setup>
const messageTip = (type)=>{
  $messageOverall[type]('体魄提示'+type)
}
</script>