vue 实现自定义message 全局提示

message组件

html 复制代码
<template>
	<transition name="fade">
		<div v-if="visible" class="m-message" :class="`m-message-${type}`">
			<svg v-if="type == 'success'" fill="none" viewBox="0 0 24 24" width="1em" height="1em" class="t-icon t-icon-error-circle-filled" style="fill: none">
				<path
					fill="currentColor"
					d="M12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12C1 5.92487 5.92487 1 12 1ZM11.0001 14H13.0001V6.49998H11.0001V14ZM13.004 15.5H11.0001V17.5039H13.004V15.5Z"></path>
			</svg>
			<svg
				v-else-if="type == 'error'"
				fill="none"
				viewBox="0 0 24 24"
				width="1em"
				height="1em"
				class="t-icon t-icon-info-circle-filled"
				style="fill: none">
				<path
					fill="currentColor"
					d="M12 23C18.0751 23 23 18.0751 23 12C23 5.92487 18.0751 1 12 1C5.92487 1 1 5.92487 1 12C1 18.0751 5.92487 23 12 23ZM10.996 8.50002V6.49611H12.9999V8.50002H10.996ZM12.9999 10L12.9999 17.5H10.9999V10L12.9999 10Z"></path>
			</svg>
			<svg
				v-else-if="type == 'warning'"
				fill="none"
				viewBox="0 0 24 24"
				width="1em"
				height="1em"
				class="t-icon t-icon-error-circle-filled"
				style="fill: none">
				<path
					fill="currentColor"
					d="M12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12C1 5.92487 5.92487 1 12 1ZM11.0001 14H13.0001V6.49998H11.0001V14ZM13.004 15.5H11.0001V17.5039H13.004V15.5Z"></path>
			</svg>
			<svg
				v-else-if="type == 'info'"
				fill="none"
				viewBox="0 0 24 24"
				width="1em"
				height="1em"
				class="t-icon t-icon-info-circle-filled"
				style="fill: none">
				<path
					fill="currentColor"
					d="M12 23C18.0751 23 23 18.0751 23 12C23 5.92487 18.0751 1 12 1C5.92487 1 1 5.92487 1 12C1 18.0751 5.92487 23 12 23ZM10.996 8.50002V6.49611H12.9999V8.50002H10.996ZM12.9999 10L12.9999 17.5H10.9999V10L12.9999 10Z"></path>
			</svg>

			{{ text }}
		</div>
	</transition>
</template>

<script setup lang="ts">
import { ref, onMounted } from "vue";

const props = defineProps({
	text: String,
	type: {
		type: String,
		default: "info", // success | error | warning | info
	},
	duration: {
		type: Number,
		default: 3000,
	},
});

const visible = ref(false);

onMounted(() => {
	visible.value = true;
	setTimeout(() => {
		visible.value = false;
	}, props.duration);
});
</script>

<style lang="scss" scoped>
@media screen and (min-width: 768px) {
	.m-message {
		display: flex;
		align-items: center;
		gap: 8px;
		padding: 12px 16px;
		width: fit-content;
		height: fit-content;
		background-color: #fff;
		border-radius: 6px;
		color: #041e39;
		font-size: 14px;
		box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
		opacity: 0.95;
		white-space: nowrap;
		& > svg {
			width: 16px;
			height: 16px;
		}
	}
}

@media screen and (max-width: 768px) {
	.m-message {
		display: flex;
		align-items: center;
		gap: 0.8rem;
		padding: 1.2rem 1.6rem;
		width: fit-content;
		height: fit-content;
		background-color: #fff;
		border-radius: 0.6rem;
		color: #041e39;
		font-size: 1.4rem;
		box-shadow: 0 0.2rem 0.8rem rgba(0, 0, 0, 0.15);
		opacity: 0.95;
		white-space: nowrap;
		& > svg {
			width: 1.6rem;
			height: 1.6rem;
		}
	}
}

/* 不同类型的颜色 */
.m-message-success {
	color: #67c23a;
}
.m-message-error {
	color: #f56c6c;
}
.m-message-warning {
	color: #e6a23c;
}
.m-message-info {
	color: #909399;
}

/* 动画 */
.fade-enter-active,
.fade-leave-active {
	transition: all 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
	opacity: 0;
	transform: translateY(-10px);
}
</style>

注册成方法

javascript 复制代码
import { createVNode, render } from "vue";
import MessageComponent from "@/components/M/Message/index.vue";

type MessageType = "success" | "error" | "warning" | "info";

// 维护当前已显示的消息节点容器
const messageInstances: HTMLElement[] = [];

function showMessage(type: MessageType, text: string, duration = 2000) {
	const container = document.createElement("div");
	container.className = "custom-message-container";
	document.body.appendChild(container);

	const vnode = createVNode(MessageComponent, { text, type, duration });
	render(vnode, container);

	// 记录实例
	messageInstances.push(container);

	// 调整每个消息的位置(根据索引累积 20px)
	updateMessagePositions();

	// 定时销毁节点
	setTimeout(() => {
		render(null, container);
		document.body.removeChild(container);
		messageInstances.splice(messageInstances.indexOf(container), 1);
		updateMessagePositions();
	}, duration + 500);
}

// 更新所有 message 的位置
function updateMessagePositions() {
	messageInstances.forEach((container, index) => {
		const offset = index == 0 ? 12 : index * 62; // 每条间隔12px
		container.style.position = "fixed";
		container.style.top = `${offset}px`;
		container.style.left = "50%";
		container.style.width = "fit-content";
		container.style.transform = "translateX(-50%)";
		container.style.zIndex = "9999";
		container.style.transition = "all 0.3s";
	});
}

export const Message = {
	success: (text: string, duration?: number) => showMessage("success", text, duration),
	error: (text: string, duration?: number) => showMessage("error", text, duration),
	warning: (text: string, duration?: number) => showMessage("warning", text, duration),
	info: (text: string, duration?: number) => showMessage("info", text, duration),
};

export default Message;

使用:

javascript 复制代码
import message from "@/utils/message.ts";
	message.error("6666");
相关推荐
华玥作者15 小时前
[特殊字符] VitePress 对接 Algolia AI 问答(DocSearch + AI Search)完整实战(下)
前端·人工智能·ai
Mr Xu_15 小时前
告别冗长 switch-case:Vue 项目中基于映射表的优雅路由数据匹配方案
前端·javascript·vue.js
前端摸鱼匠15 小时前
Vue 3 的toRefs保持响应性:讲解toRefs在解构响应式对象时的作用
前端·javascript·vue.js·前端框架·ecmascript
sleeppingfrog15 小时前
zebra通过zpl语言实现中文打印(二)
javascript
lang2015092816 小时前
JSR-340 :高性能Web开发新标准
java·前端·servlet
好家伙VCC16 小时前
### WebRTC技术:实时通信的革新与实现####webRTC(Web Real-TimeComm
java·前端·python·webrtc
未来之窗软件服务17 小时前
未来之窗昭和仙君(六十五)Vue与跨地区多部门开发—东方仙盟练气
前端·javascript·vue.js·仙盟创梦ide·东方仙盟·昭和仙君
baidu_2474386117 小时前
Android ViewModel定时任务
android·开发语言·javascript
嘿起屁儿整17 小时前
面试点(网络层面)
前端·网络
VT.馒头17 小时前
【力扣】2721. 并行执行异步函数
前端·javascript·算法·leetcode·typescript