缩短dashboard名称 调整通知样式
This commit is contained in:
@@ -1,50 +1,115 @@
|
|||||||
<!-- src/components/common/Toast.vue:daisyUI toast 容器堆叠展示全部活动提示 -->
|
|
||||||
<template>
|
<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">
|
<TransitionGroup name="toast">
|
||||||
<div
|
<div
|
||||||
v-for="t in toasts"
|
v-for="t in toasts"
|
||||||
:key="t.id"
|
:key="t.id"
|
||||||
role="status"
|
role="status"
|
||||||
class="alert shadow-lg"
|
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="{
|
:class="typeClasses(t.type)"
|
||||||
'alert-error': t.type === 'error',
|
|
||||||
'alert-success': t.type === 'success',
|
|
||||||
'alert-info': t.type === 'info',
|
|
||||||
}"
|
|
||||||
>
|
>
|
||||||
<span class="min-w-0 flex-1 break-words">{{ t.message }}</span>
|
<component :is="iconForType(t.type)" class="mt-0.5 h-4 w-4 shrink-0" />
|
||||||
<button type="button" class="btn btn-ghost btn-xs" aria-label="关闭提示" @click="dismiss(t.id)">✕</button>
|
<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>
|
</div>
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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 { 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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 只动 transform/opacity;系统开启减弱动态效果时由全局样式禁用 */
|
.toast-item {
|
||||||
.toast-enter-active,
|
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 {
|
.toast-leave-active {
|
||||||
transition: opacity 0.25s ease, transform 0.25s ease;
|
transition: all 0.2s ease-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast-enter-from {
|
.toast-enter-from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-8px);
|
transform: translateX(100%) scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast-leave-to {
|
.toast-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(16px);
|
transform: translateX(100%) scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast-leave-active {
|
.toast-move {
|
||||||
position: absolute;
|
transition: transform 0.3s ease;
|
||||||
right: 0;
|
}
|
||||||
|
|
||||||
|
/* 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>
|
</style>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<RouterLink to="/dashboard" class="btn btn-primary btn-sm h-9 min-h-9 rounded-full px-3 sm:px-4 whitespace-nowrap">
|
<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>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user