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
97 lines
1.6 KiB
Vue
<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>
|