缩短dashboard名称 调整通知样式

This commit is contained in:
Sakurasan
2026-08-30 15:53:11 +08:00
parent 6cc76c6767
commit 8f6acb981d
2 changed files with 85 additions and 20 deletions
+84 -19
View File
@@ -1,50 +1,115 @@
<!-- src/components/common/Toast.vue:daisyUI toast 容器堆叠展示全部活动提示 -->
<template>
<div aria-live="polite" class="toast toast-top toast-end z-50 mt-16 gap-2">
<div aria-live="polite" class="fixed right-4 top-4 z-50 flex flex-col gap-2 pt-14 sm:pt-4">
<TransitionGroup name="toast">
<div
v-for="t in toasts"
:key="t.id"
role="status"
class="alert shadow-lg"
:class="{
'alert-error': t.type === 'error',
'alert-success': t.type === 'success',
'alert-info': t.type === 'info',
}"
class="toast-item group flex items-start gap-2.5 rounded-lg border px-3.5 py-2.5 shadow-lg backdrop-blur-sm transition-colors"
:class="typeClasses(t.type)"
>
<span class="min-w-0 flex-1 break-words">{{ t.message }}</span>
<button type="button" class="btn btn-ghost btn-xs" aria-label="关闭提示" @click="dismiss(t.id)">✕</button>
<component :is="iconForType(t.type)" class="mt-0.5 h-4 w-4 shrink-0" />
<span class="min-w-0 flex-1 break-words text-sm leading-snug">{{ t.message }}</span>
<button
type="button"
class="mt-0.5 -mr-0.5 shrink-0 rounded p-0.5 opacity-40 transition-opacity hover:opacity-100"
aria-label="关闭"
@click="dismiss(t.id)"
>
<XIcon class="h-3.5 w-3.5" />
</button>
<!-- progress bar -->
<div
v-if="t.duration > 0"
class="absolute bottom-0 left-0 h-0.5 rounded-b-lg transition-all"
:class="progressClass(t.type)"
:style="{ width: '100%', animation: `shrink ${t.duration}ms linear forwards` }"
/>
</div>
</TransitionGroup>
</div>
</template>
<script setup lang="ts">
import { useToasts } from '@/composables/toast';
import { useToasts, type ToastType } from '@/composables/toast';
import { CheckCircleIcon, XCircleIcon, InfoIcon, XIcon } from '@lucide/vue';
const { toasts, dismiss } = useToasts();
const iconForType = (type: ToastType) => {
switch (type) {
case 'success': return CheckCircleIcon;
case 'error': return XCircleIcon;
default: return InfoIcon;
}
};
const typeClasses = (type: ToastType) => {
switch (type) {
case 'success':
return 'border-success/20 bg-success/10 text-success-content dark:border-success/30 dark:bg-success/15';
case 'error':
return 'border-error/20 bg-error/10 text-error-content dark:border-error/30 dark:bg-error/15';
default:
return 'border-base-300 bg-base-100 text-base-content';
}
};
const progressClass = (type: ToastType) => {
switch (type) {
case 'success': return 'bg-success/60';
case 'error': return 'bg-error/60';
default: return 'bg-base-content/30';
}
};
</script>
<style scoped>
/* 只动 transform/opacity;系统开启减弱动态效果时由全局样式禁用 */
.toast-enter-active,
.toast-item {
position: relative;
overflow: hidden;
max-width: 22rem;
}
/* Animations */
.toast-enter-active {
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.toast-leave-active {
transition: opacity 0.25s ease, transform 0.25s ease;
transition: all 0.2s ease-in;
}
.toast-enter-from {
opacity: 0;
transform: translateY(-8px);
transform: translateX(100%) scale(0.95);
}
.toast-leave-to {
opacity: 0;
transform: translateX(16px);
transform: translateX(100%) scale(0.95);
}
.toast-leave-active {
position: absolute;
right: 0;
.toast-move {
transition: transform 0.3s ease;
}
/* Progress bar shrink animation */
@keyframes shrink {
from { width: 100%; }
to { width: 0%; }
}
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.toast-enter-active,
.toast-leave-active,
.toast-move {
transition: opacity 0.15s ease !important;
}
.toast-enter-from,
.toast-leave-to {
transform: none !important;
}
}
</style>
+1 -1
View File
@@ -48,7 +48,7 @@
</span>
</a>
<RouterLink to="/dashboard" class="btn btn-primary btn-sm h-9 min-h-9 rounded-full px-3 sm:px-4 whitespace-nowrap">
Open Dashboard
Dashboard
</RouterLink>
</div>
</div>