一个基于vue3的通过函数式调用的弹框组件

本文介绍如何在不使用第三方UI库的情况下,利用Vue自制一个简单的消息提示组件,该组件能够显示不同类型的消息并自动关闭。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

因项目需求需要一个弹框提示,之前是使用elemne-plus,但是因为项目调整不使用第三方ui库了,所以简单实现下element-plus的elMessage(ps:只做了单弹框提示,并没有做多个。动画也没有~,感兴趣的小伙伴可以自己添加哦

处理svg的vite插件,并且全局注册使用

message.vue

<!-- 目前的 message 只支持全局只有一个,暂时不修改为多个 -->
<template>
    <div v-show="visible" class="redcat-message left" @mouseover="clear" @mouseout="startTimer">
        <svgIcon :name="style.icon" :color="style.style.color" size="6px" @click="close"></svgIcon>
        <span class="message-text">{{ message }}</span>
    </div>
</template>

<script lang="ts" setup>
import svgIcon from '@/globComponents/svg-icon.vue'
import { getStyle, MessageProps, MessageType } from '@/utils/Message/useMessage'
import { computed, Ref, ref } from 'vue'

const message = ref('')
const type: Ref<MessageType> = ref('success')
const visible = ref(false)
const closeTime: Ref<Nullable<NodeJS.Timeout>> = ref(null)

const close = (): void => {
    visible.value = false
    closeTime.value && clearTimeout(closeTime.value)
    closeTime.value = null
}
// 暂时定为三秒后消失
const startTimer = (): void => {
    closeTime.value = setTimeout(() => {
        close()
    }, 3000)
}

const open = (props: MessageProps): void => {
    if (closeTime.value) {
        clearTimeout(closeTime.value)
    }

    message.value = props.message
    type.value = props.type
    visible.value = true

    startTimer()
}

const zIndex = ref(new Date().getFullYear())

const style = computed(() => getStyle(type.value))

const clear = () => {
    closeTime.value && clearTimeout(closeTime.value)
}

defineExpose({
    open,
    close
})

</script>

<style lang="scss" scoped>
.redcat-message {
    min-width: 100%;
    padding: 15px 15px 15px 20px;
    border-radius: 5px;
    background-color: v-bind('style.style.background');
    border: v-bind('style.style.border');
    color: v-bind('style.style.color');
    pointer-events: all;
    line-height: 1;

    .svg-icon {
        margin-right: 10px;
        cursor: pointer;
    }
}
</style>

useMessage.ts

import MessageVue from '@/utils/Message/index.vue'
import { h, render, VNode } from 'vue'

export type MessageProps = {
    message: string
    type: MessageType
}

type MessageFnc = (message: string) => void

type MessageProp = ((message: string | MessageProps) => void) & {
    success: MessageFnc
    warning: MessageFnc
    info: MessageFnc
    error: MessageFnc
}

export type MessageType = 'success' | 'warning' | 'info' | 'error'

export const getStyle = (type: MessageType): {
    style: {
        background: string
        border: string
        color: string
    },
    icon: string
} => {
    // TODO 目前只有一个图标
    const icon = 'delete-report'

    // success
    const style = {
        background: '#f0f9eb',
        borderColor: '#e1f3d8',
        color: '#67C23B'
    }

    if (type === 'error') {
        style.background = '#fef0f0'
        style.borderColor = '#fde2e2'
        style.color = '#f56c6c'
    }

    if (type === 'info') {
        style.background = '#f4f4f5'
        style.borderColor = '#e9e9eb'
        style.color = '#909399'
    }

    if (type === 'warning') {
        style.background = '#fdf6ec'
        style.borderColor = '#faecd8'
        style.color = '#e6a23c'
    }

    return {
        style: {
            background: style.background,
            border: `1px solid ${ style.borderColor }`,
            color: style.color
        },
        icon
    }
}

const messageBoxId = 'MESSAGE-ID'

const renderMessage = (): VNode => {
    const props: MessageProps = {
        message: '',
        type: 'success'
    }
    const container = document.createElement('div')
    container.id = messageBoxId
    Object.assign(container.style, {
        minWidth: '380px',
        top: '36px',
        position: 'fixed',
        left: 'calc(50% - 190px)',
        zIndex: new Date().getFullYear(),
        boxSizing: 'border-box',
        fontSize: '14px',
        pointerEvents: 'none'
    })
    // 创建 虚拟dom
    const messageVNode = h(MessageVue, props)
    // 将虚拟dom渲染到 container dom 上
    render(messageVNode, container)
    // 最后将 container 追加到 body 上
    document.body.appendChild(container)

    return messageVNode
}

let messageInstance: UndefinedAble<VNode>

const sendMessage = (message: string, type: MessageType): void => {
    if (messageInstance) {
        const messageVue = messageInstance.component
        messageVue?.exposed?.open?.({ message, type })
    } else {
        messageInstance = renderMessage()
        sendMessage(message, type)
    }
}

const _Message = (message: string | MessageProps): void => {
    if (typeof message === 'string') {
        sendMessage(message, 'success')
    } else {
        sendMessage(message.message, message.type)
    }
}
_Message.success = (message: string) => sendMessage(message, 'success')
_Message.warning = (message: string) => sendMessage(message, 'warning')
_Message.info = (message: string) => sendMessage(message, 'info')
_Message.error = (message: string) => sendMessage(message, 'error')

const Message: MessageProp = _Message

export default Message

使用

<template>
    <div></div>
</template>
<script lang="ts" setup>
import useMessage from '@/utils/Message/useMessage'
useMessage('成功提示')
</script>

在这里插入图片描述

错误提示:
useMessage.error('错误提示')
// 也支持
useMessage({
	message: '错误消息',
	type: 'error'
})

在这里插入图片描述

还有其他提示我就不一一使用啦~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值