Toast.vue组件文件
<script setup lang="ts">
import { computed, onMounted, ref } from "vue";
import type { ToastProps } from './index';
const props = withDefaults(defineProps<ToastProps>(), {
text: '',
position: 'top-center',
duration: 'normal',
});
const myself = ref<HTMLElement>();
const durationTimeMs = computed(() => {
switch (props.duration) {
case 'long':
return 1500;
case 'normal':
return 1000;
case 'short':
return 500;
default:
return props.duration;
}
})
onMounted(() => {
setTimeout(() => {
// 一定时间后移除
myself.value?.remove()
}, durationTimeMs.value);
})
const toastClass = computed(() => {
let className: string = props.position;
if (className === 'top') className = 'top-center';
if (className === 'bottom') className = 'bottom-center';
return ['toast', className];
});
</script>
<template>
<Teleport to="body">
<div :class="toastClass" ref="myself">
<div class="toast-wrapper">
/* 同时支持text和slot */
<span v-if="text" class="toast-text">{{ props.text }}</span>
<slot v-if="$slots.default"></slot>
</div>
</div>
</Teleport>
</template>
<style scoped lang="scss">
@import "@/assets/variables";
.toast {
position: fixed;
&-wrapper {
background-color: $color-white;
border-radius: .5rem;
box-shadow: $box-shadow;
padding: .25rem .5rem;
display: flex;
flex-direction: row;
align-items: center;
gap: .5rem;
}
.top-center {
top: 0;
left: 50%;
transform: translateX(-50%);
}
.top-left {
top: 0;
left: 0;
}
.top-right {
top: 0;
right: 0;
}
.bottom-center {
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
.bottom-left {
bottom: 0;
left: 0;
}
.bottom-right {
bottom: 0;
right: 0;
}
}
</style>
toast.ts函数文件
import type { ToastProps } from "@/components/toast/index";
import { createApp, h } from "vue";
import Toast from "@/components/toast/Toast.vue";
let toastDiv: HTMLDivElement;
function showToast(props: ToastProps) {
if (!toastDiv) {
toastDiv = document.createElement('div');
}
const element = createApp({
render() {
return h(Toast, props);
}
})
// 添加div到根组件上
document.querySelector('#app')?.appendChild(toastDiv);
// Toast组件挂载到div内部
element.mount(toastDiv);
}
export default showToast;
这里将toastDiv挂载在#app的内部,是由于我这里编写的框架结构导致的,正常情况下用
document.body.appendChild(toastDiv)
就行。
index.ts定义文件
export interface ToastProps {
text?: string;
position?: 'top' | 'top-center' | 'top-left' | 'top-right' | 'bottom' | 'bottom-center' | 'bottom-left' | 'bottom-right';
duration?: 'normal' | 'long' | 'short' | number;
}
使用方式
showToast({text: '1024程序员节快乐'});