2025-12-10 20:52:44 +08:00
|
|
|
<template>
|
|
|
|
|
<div :class="containerClasses">
|
|
|
|
|
<div class="flex flex-col items-center gap-4">
|
2025-12-12 16:15:16 +08:00
|
|
|
<Skeleton
|
|
|
|
|
v-if="variant === 'skeleton'"
|
|
|
|
|
:class="skeletonClasses"
|
|
|
|
|
/>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
2025-12-12 16:15:16 +08:00
|
|
|
<div
|
|
|
|
|
v-else-if="variant === 'spinner'"
|
|
|
|
|
class="relative"
|
|
|
|
|
>
|
|
|
|
|
<div class="h-12 w-12 animate-spin rounded-full border-4 border-muted border-t-primary" />
|
2025-12-10 20:52:44 +08:00
|
|
|
</div>
|
|
|
|
|
|
2025-12-12 16:15:16 +08:00
|
|
|
<div
|
|
|
|
|
v-else-if="variant === 'pulse'"
|
|
|
|
|
class="flex gap-2"
|
|
|
|
|
>
|
2025-12-10 20:52:44 +08:00
|
|
|
<div
|
|
|
|
|
v-for="i in 3"
|
|
|
|
|
:key="i"
|
|
|
|
|
class="h-3 w-3 animate-pulse rounded-full bg-primary"
|
|
|
|
|
:style="{ animationDelay: `${i * 150}ms` }"
|
2025-12-12 16:15:16 +08:00
|
|
|
/>
|
2025-12-10 20:52:44 +08:00
|
|
|
</div>
|
|
|
|
|
|
2025-12-12 16:15:16 +08:00
|
|
|
<div
|
|
|
|
|
v-if="message"
|
|
|
|
|
class="text-sm text-muted-foreground"
|
|
|
|
|
>
|
2025-12-10 20:52:44 +08:00
|
|
|
{{ message }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { computed } from 'vue'
|
|
|
|
|
import Skeleton from '@/components/ui/skeleton.vue'
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
variant?: 'skeleton' | 'spinner' | 'pulse'
|
|
|
|
|
message?: string
|
|
|
|
|
size?: 'sm' | 'md' | 'lg'
|
|
|
|
|
fullHeight?: boolean
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
|
|
|
variant: 'spinner',
|
|
|
|
|
size: 'md',
|
|
|
|
|
fullHeight: false,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const containerClasses = computed(() => {
|
|
|
|
|
const classes = ['flex items-center justify-center']
|
|
|
|
|
|
|
|
|
|
if (props.fullHeight) {
|
|
|
|
|
classes.push('min-h-[400px]')
|
|
|
|
|
} else {
|
|
|
|
|
const sizeMap = {
|
|
|
|
|
sm: 'py-8',
|
|
|
|
|
md: 'py-12',
|
|
|
|
|
lg: 'py-16',
|
|
|
|
|
}
|
|
|
|
|
classes.push(sizeMap[props.size])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return classes.join(' ')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const skeletonClasses = computed(() => {
|
|
|
|
|
const sizeMap = {
|
|
|
|
|
sm: 'h-24 w-full',
|
|
|
|
|
md: 'h-48 w-full',
|
|
|
|
|
lg: 'h-64 w-full',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sizeMap[props.size]
|
|
|
|
|
})
|
|
|
|
|
</script>
|