本文主要分享了如何使用vue3和TS来封装一个自定义的的message消息提示组件,通常消息提示框位于页面顶部居中,为可单击手动关闭和定时关闭以及设置消息提示框的类型。在本文中使用success、warning、info、error四种类型作为案例演示。
1️⃣ message.vue文件
使用vue3和ts实现的一个消息提示组件,类型为success、warning、info、error四种。 图标是使用的iconfont,iconfont是我在已经在项目中引入的资源。
✏️template部分
- transition-group给提示组件增加自然淡入淡出的效果,可能同时出现几个提示组件,因此使用的是transition-group。
- closeHandle为消息提示框关闭事件,当duration传参为0时,不显示关闭按钮且无法自动关闭。
- getInnerClass设置消息提示框的样式
- getIconName获取不同类型提示框的图标
<template>
<transition-group tag="div" name="slide-fade">
<div v-for="message in messageQueue" :key="message.id" :class="getInnerClass(message.type)">
<div v-if="message.duration === 0" class="close-message" @click="closeHandle(message)">
<i class="iconfont icon-cuowuguanbiquxiao"></i>
</div>
<div class="contents">
<i :class="['iconfont', `${getIconName(message.type)}`]"></i>
<span class="cloud-toast-content" v-html="message.content"></span>
</div>
</div>
</transition-group>
</template>
✏️script部分
<script lang="ts" setup>
import { PropType } from "vue"
import type{ ToastOption, MessageType } from "./message" //从message.ts文件中导入定义好的interface
import Message from "./message"
defineProps({
messageQueue: {
type: Array as PropType<ToastOption[]>,
default: () => []
}
})
const getInnerClass = (type: MessageType) => { // 控制不同类型的提示组件的样式
const MESSAGE_CLASS = "cloud-toast"
return {
[MESSAGE_CLASS]: true,
[`${MESSAGE_CLASS}-${type}`]: true
}
}
const getIconName = (type: MessageType) => { //获取四种不同提示状态的图标名称,此处使用的是iconfont图标
return `icon-${type}`
}
const closeHandle = (message: ToastOption) => Message.remove(message)
</script>
✏️样式部分
- scss变量可以定义在公共样式文件中
<style lang="scss">
$success-font: #67c23a;
$info-font: #909399;
$warning-font: #e6a23c;
$error-font: #f56c6c;
.slide-fade-enter-active {
transition: all 0.3s ease-out;
}
.slide-fade-leave-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter-from,
.slide-fade-leave-to {
transform: translateX(20px);
opacity: 0;
}
.message-wrapper {
position: fixed;
top: 10px;
z-index: 999;
left: 50%;
transform: translateX(-50%);
}
.cloud-toast {
display: block;
padding: 15px 15px 15px 20px;
margin-top: 16px;
min-width: 300px;
background-color: $primary-color;
border-radius: 8px;
transition: all 0.3s;
transition: opacity 0.3s, transform 0.4s, top 0.4s;
box-shadow: 2px 1px 5px 2px rgba(0, 0, 0, 0.1);
position: relative;
overflow: hidden;
display: flex;
align-items: center;
&-contents {
padding-right: 16px;
}
.close-message {
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
i {
font-size: 12px;
}
}
&-success {
background-color: #f0f9eb;
border-color: #e1f3d8;
.cc-message-content,
i {
color: $success-font;
}
}
&-error {
background-color: #fef0f0;
border-color: #fde2e2;
.cc-message-content,
i {
color: $error-font;
}
}
&-info {
background-color: #edf2fc;
border-color: #ebeef5;
.cc-message-content,
i {
color: $info-font;
}
}
&-warning {
background-color: #fdf6ec;
border-color: #faecd8;
.cc-message-content,
i {
color: $warning-font;
}
}
}
</style>
2️⃣ message.ts文件
- 从vue中导入api和类型
- MessageOption为提示框传参的基本信息;MessageType为不同的消息类型;IMessage为Message类中必须实现的属性;defaultMessageOptions为默认提示框传参
- 创建一个uuid()的方法,为每一个创建的message组件的唯一id,以便匹配删除组件的功能
- 创建一个Message类
-
定义一个wrapper实例和messageQueue消息队列
-
定义一个createWrapper()方法实现组件的添加和删除操作,在render函数中创建节点并使用teleport传送门将创建的组件放置于根节点下。将createWrapper创建的实例赋值给wrapper
-
appendMessage()方法调用wrapper下的append()方法创建消息提示组件,并判断传参中的duration值是否为0,不为0则正常实现定时删除的方法。
-
createMessage合并默认参数和形参
-
success方法合并参数,调用createWrapper()初始化wrapper,再使用appendMessage()添加组件到DOM节点中。其他的info、warning、error方法一样的操作,只是type不同。
-
import { h, Teleport, createApp } from "vue"
import type {ComponentPublicInstance, App} from 'vue'
import Message from "./index.vue"
export interface MessageOption {
type: MessageType
content: string
duration: number
closeVisible: boolean
id: string
}
//定义消息类型的枚举常量
export enum MessageType {
SUCCESS = "success",
ERROR = "error",
INFO = "info",
WARNING = "warning"
}
type IHandleMessageFn = (message: string, option?: MessageOption) => void
// 定义Message类中的属性、方法的数据类型
export interface IMessage {
messageQueue: MessageOption[]
success: IHandleMessageFn
info: IHandleMessageFn
error: IHandleMessageFn
warning: IHandleMessageFn
}
const defaultMessageOptions = {
type: MessageType.INFO,
duration: 3000,
closeVisible: false
}
// 创建一个唯一标识符
function uuid(): string {
const tempUrl = URL.createObjectURL(new Blob())
const uuid = tempUrl.toString()
URL.revokeObjectURL(uuid)
return uuid.substr(uuid.lastIndexOf("/") + 1)
}
class Message implements IMessage {
private wrapper: ComponentPublicInstance<any>
get messageQueue() {
return this.wrapper.messageQueue
}
private createWrapper(): App {
if (this.wrapper) {
return this.wrapper
}
this.wrapper = createApp({
data() {
return {
messageQueue: []
}
},
methods: {
remove(toastInfo: ToastOption) {
this.messageQueue = this.messageQueue.filter((item: ToastOption) => item.id !== toastInfo.id)
},
append(toastInfo: ToastOption) {
this.messageQueue.push(toastInfo)
}
},
render() {
return h(Teleport, { to: "body" }, [
h("div", { class: "message-wrapper" }, [h(Message, { messageQueue: this.messageQueue })])
])
}
}).mount(document.createElement("div"))
return this.wrapper
}
private appendMessage(message: MessageOption) {
this.wrapper.append(message)
if (message.duration === 0) return
setTimeout(() => {
this.remove(message)
}, message.duration)
}
remove(message: MessageOption) {
this.wrapper.remove(message)
}
public createMessage(content: string, options: Partial<MessageOption>): MessageOption {
const message = Object.assign(
{},
defaultMessageOptions,
{
content,
id: uuid()
},
options
)
return message
}
success(content: string, option?: Partial<MessageOption>) {
const message = this.createMessage(content, {
type: MessageType.SUCCESS,
...option
})
this.createWrapper()
this.appendMessage(message)
}
info(content: string, option?: Partial<MessageOption>) {
const message = this.createMessage(content, {
type: MessageType.INFO,
...option
})
this.createWrapper()
this.appendMessage(message)
}
error(content: string, option?: Partial<MessageOption>) {
const message = this.createMessage(content, {
type: MessageType.ERROR,
...option
})
this.createWrapper()
this.appendMessage(message)
}
warning(content: string, option?: Partial<MessageOption>) {
const message = this.createMessage(content, {
type: MessageType.WARNING,
...option
})
this.createWrapper()
this.appendMessage(message)
}
}
export default new Message()
3️⃣ 在其他vue文件中使用
Message.success("吴彦祖看我文章了!", { type: "success", duration: 0 })
注意:要将Message挂载到全局才能在任意的文件中使用。