You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

97 lines
1.6 KiB
Vue

1 month ago
<template>
<Teleport to="body">
<Transition name="toast">
<div v-if="isVisible" class="toast" :class="`toast-${type}`" role="alert">
<div class="toast-content">
{{ message }}
</div>
</div>
</Transition>
</Teleport>
</template>
<script setup>
import { ref, onMounted, onUnmounted, watch } from "vue";
const props = defineProps({
message: String,
type: {
type: String,
default: "info",
validator: (value) => ["info", "success", "warning", "error"].includes(value),
},
duration: {
type: Number,
default: 3000,
},
});
const emit = defineEmits(["close"]);
const isVisible = ref(false);
let timer = null;
onMounted(() => {
isVisible.value = true;
timer = setTimeout(() => {
isVisible.value = false;
}, props.duration);
});
watch(isVisible, (newValue) => {
if (!newValue) {
setTimeout(() => {
emit("close");
}, 300); // 确保动画完成
}
});
onUnmounted(() => {
if (timer) {
clearTimeout(timer);
}
});
</script>
<style scoped>
.toast {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 20px;
border-radius: 8px;
color: white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 9999;
max-width: 300px;
transform: translateY(0);
opacity: 1;
transition: all 0.3s ease;
}
.toast-content {
display: flex;
align-items: center;
gap: 10px;
}
.toast-enter-from,
.toast-leave-to {
opacity: 0;
transform: translateY(-50px);
}
.toast-info {
background-color: #3b82f6;
}
.toast-success {
background-color: #10b981;
}
.toast-warning {
background-color: #f59e0b;
}
.toast-error {
background-color: #ef4444;
}
</style>