涵盖移动端和PC端的消息提示框(Toast和Message)功能,包括动态调整显示位置、显示逻辑以及动画效果。
目标
-
移动端:
-
同一时间只显示一条消息。
-
支持动态调整显示位置(屏幕底部或中间)。
-
提供简洁的动画效果。
-
消息可点击关闭。
-
-
PC端:
-
最多同时显示5条消息。
-
消息按顺序显示,超出的消息自动移除。
-
提供关闭按钮。
-
1. 消息组件(Message.vue)
消息组件支持动态位置调整,并根据设备类型(移动端或PC端)调整样式。
<template>
<div
v-if="visible"
class="message"
:class="[
isMobile ? 'message-mobile' : 'message-pc',
positionClass,
{ 'message-fade-in': fadeIn, 'message-fade-out': fadeOut }
]"
:style="{ backgroundColor: background, color: textColor }"
@click="closeIfMobile"
>
<div class="message-content">
<span>{
{ message }}</span>
<button v-if="!isMobile" class="message-close" @click="close">x</button>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, onUnmounted } from 'vue';
const props = defineProps({
message: {
type: String,
required: true,
},
duration: {
type: Number,
default: 2000,
},
background: {
type: String,
default: '#333',
},
textColor: {
type: String,
default: '#fff',
},
position: {
type: String,
default: 'bottom', // 默认显示在底部
},
});
const visible = ref(false);
const fadeIn = ref(false);
const fadeOut = ref(false);
const isMobile = ref(window.innerWidth <= 768);
const positionClass = computed(() => {
return `message-${props.position}`;
});
onMounted(() => {
visible.value = true;
setTimeout(() => {
fadeIn.value = true;
}, 10);
const timer = setTimeout(() => {
fadeOut.value = true;
setTimeout(() => {
visible.value = false;
}, 300); // 等待动画完成
}, props.duration);
onUnmounted(() => {
clearTimeout(timer);
});
});
const close = () => {
fadeOut.value = true;
setTimeout(() => {
visible.value = false;
}, 300); // 等待动画完成
};
const closeIfMobile = () => {
if (isMobile.value) {
close();
}
};
</script>
<style scoped>
.message {
position: fixed;
z-index: 1000;
padding: 12px 16px;
border-radius: 8px;
font-size: 14px;
text-align: center;
transition: opacity 0.3s ease, transform 0.3s ease;
opacity: 0;
transform: translateY(20px); /* 初始状态隐藏 */
}
.message-mobile {
width: 90%;
max-width: 300px;
left: 50%;
transform: translateX(-50%);
}
.message-bottom {
bottom: 20px;
}
.message-center {
top: 50%;
transform: translate(-50%, -50%);
}
.message-pc {
top: 20px;
right: 20px;
margin-bottom: 10px;
}
.message-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.message-close {
background: none;
border: none;
color: inherit;
font-size: 16px;
cursor: pointer;
}
.message-fade-in {
opacity: 1;
transform: translateY(0);
}
.message-fade-out {
opacity: 0;
transform: translateY(20px);
}
</style>
2. 消息服务(message.ts)
消息服务支持移动端和PC端的逻辑,动态调整显示位置和数量。
// src/utils/message.ts
import { createVNode, render } from 'vue';
import Message from '@/components/Message.vue';
const container = document.createElement('div');
document.body.appendChild(container);
const messageQueue: { content: string; options: { duration?: number; background?: string; textColor?: string; position?: string } }[] = [];
let currentMessage: ReturnType<typeof setTimeout> | null = null;
const maxPCMessages = 5; // PC 端最多显示 5 条消息
const isMobile = window.innerWidth <= 768; // 是否为移动端
export function useMessage() {
return (content: string, options: { duration?: number; background?: string; textColor?: string; position?: string } = {}) => {
messageQueue.push({ content, options });
displayMessages();
};
}
function displayMessages() {
if (isMobile) {
// 移动端逻辑:同一时间只显示一条消息
if (currentMessage !== null) return;
const { content, options } = messageQueue.shift()!;
const vnode = createVNode(Message, {
message: content,
duration: options.duration || 2000,
background: options.background || '#333',
textColor: options.textColor || '#fff',
position: options.position || 'bottom', // 默认显示在底部
});
render(vnode, container);
currentMessage = setTimeout(() => {
render(null, container);
currentMessage = null;
displayMessages(); // 显示下一条消息
}, vnode.props?.duration || 2000);
} else {
// PC 端逻辑:最多同时显示 5 条消息
if (messageQueue.length > maxPCMessages) {
messageQueue.shift(); // 如果超过 5 条,移除最早的消息
}
const { content, options } = messageQueue[messageQueue.length - 1]!;
const vnode = createVNode(Message, {
message: content,
duration: options.duration || 2000,
background: options.background || '#333',
textColor: options.textColor || '#fff',
position: options.position || 'bottom', // 默认显示在底部
});
render(vnode, container);
}
}
3. 全局挂载
在 main.ts
中挂载消息服务,确保可以在任何组件中调用。
// src/main.ts
import { createApp } from 'vue';
import App from './App.vue';
import { useMessage } from './utils/message';
const app = createApp(App);
const message = useMessage();
app.config.globalProperties.$message = message;
app.mount('#app');
4. 在组件中使用
在任何组件中调用 $message
方法显示消息,并可以指定显示位置。
<template>
<div>
<button @click="showBottomMessage">显示底部消息</button>
<button @click="showCenterMessage">显示中间消息</button>
</div>
</template>
<script lang="ts" setup>
const showBottomMessage = () => {
$message('这是一条底部消息', {
duration: 2000,
background: '#ff5722',
textColor: '#fff',
position: 'bottom', // 显示在底部
});
};
const showCenterMessage = () => {
$message('这是一条中间消息', {
duration: 2000,
background: '#ff5722',
textColor: '#fff',
position: 'center', // 显示在中间
});
};
</script>
总结
通过上述实现,我们确保了:
-
移动端:
-
同一时间只显示一条消息。
-
支持动态调整显示位置(底部或中间)。
-
提供简洁的动画效果。
-
消息可点击关闭。
-
-
PC端:
-
最多同时显示5条消息。
-
消息按顺序显示,超出的消息自动移除。
-
提供关闭按钮。
-