Spaces:
Paused
Paused
File size: 1,876 Bytes
395e9dc 1f4cfcb 395e9dc 1f4cfcb 395e9dc 1f4cfcb 395e9dc 1f4cfcb 395e9dc 1f4cfcb 395e9dc | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <!--
ToastHost — toast 容器,挂在 App 顶层
-->
<template>
<div class="fixed top-3 right-3 z-[100] space-y-2 max-w-sm pointer-events-none">
<transition-group tag="div" class="space-y-2">
<div v-for="t in state.items" :key="t.id"
class="pointer-events-auto rounded-lg border bg-surface shadow-card px-4 py-3 flex items-start gap-3"
:class="[toneClass(t.tone), t.leaving ? 'animate-toast-out' : 'animate-toast-in']"
@click="dismiss(t.id)">
<span class="shrink-0 mt-0.5">
<Check v-if="t.tone === 'success'" class="w-4 h-4 text-emerald-700" :stroke-width="2" />
<X v-else-if="t.tone === 'error'" class="w-4 h-4 text-rose-700" :stroke-width="2" />
<TriangleAlert v-else-if="t.tone === 'warning'" class="w-4 h-4 text-amber-700" :stroke-width="2" />
<Info v-else class="w-4 h-4 text-sky-700" :stroke-width="2" />
</span>
<div class="flex-1 min-w-0">
<div class="text-sm font-semibold leading-tight" :class="titleColor(t.tone)">{{ t.text }}</div>
<div v-if="t.detail" class="text-xs mt-1 opacity-70 break-all" :class="titleColor(t.tone)">{{ t.detail }}</div>
</div>
</div>
</transition-group>
</div>
</template>
<script setup>
import { useToast } from '../composables/useToast.js'
import { Check, Info, TriangleAlert, X } from 'lucide-vue-next'
const { state, dismiss } = useToast()
function toneClass(tone) {
return {
success: 'bg-emerald-50 border-emerald-200',
error: 'bg-rose-50 border-rose-200',
warning: 'bg-amber-50 border-amber-200',
info: 'bg-sky-50 border-sky-200',
}[tone] || 'bg-sky-50 border-sky-200'
}
function titleColor(tone) {
return {
success: 'text-emerald-900',
error: 'text-rose-900',
warning: 'text-amber-900',
info: 'text-sky-900',
}[tone] || 'text-sky-900'
}
</script>
|