fucai-claim/src/components/Loading.vue

349 lines
7.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div
v-if="visible"
class="loading-container"
:class="{
'loading-container-fullscreen': fullscreen,
'loading-container-inline': !fullscreen
}"
:style="containerStyle"
>
<div class="loading-spinner-wrapper" :style="spinnerWrapperStyle">
<!-- 旋转圆环样式 -->
<div v-if="type === 'spinner'" class="loading-spinner" :style="spinnerStyle">
<svg class="loading-svg" viewBox="0 0 50 50" :style="svgStyle">
<circle
class="loading-path"
cx="25"
cy="25"
r="20"
fill="none"
stroke="currentColor"
:stroke-width="strokeWidth"
:stroke-linecap="strokeLinecap"
stroke-dasharray="94.2 94.2"
stroke-dashoffset="94.2"
></circle>
</svg>
</div>
<!-- 点状加载样式 -->
<div v-else-if="type === 'dots'" class="loading-dots" :style="dotsContainerStyle">
<div
v-for="n in 3"
:key="n"
class="loading-dot"
:style="[
dotStyle,
{
animationDelay: `${n * 0.2}s`
}
]"
></div>
</div>
<!-- 脉冲样式 -->
<div v-else-if="type === 'pulse'" class="loading-pulse" :style="pulseStyle"></div>
<!-- 环形样式 -->
<div v-else-if="type === 'ring'" class="loading-ring" :style="ringStyle">
<div class="loading-ring-circle" :style="ringCircleStyle"></div>
</div>
<!-- 默认样式(旋转圆环) -->
<div v-else class="loading-spinner" :style="spinnerStyle">
<svg class="loading-svg" viewBox="0 0 50 50" :style="svgStyle">
<circle
class="loading-path"
cx="25"
cy="25"
r="20"
fill="none"
stroke="currentColor"
:stroke-width="strokeWidth"
:stroke-linecap="strokeLinecap"
stroke-dasharray="94.2 94.2"
stroke-dashoffset="94.2"
></circle>
</svg>
</div>
<!-- 加载文本 -->
<div v-if="text" class="loading-text" :style="textStyle">
{{ text }}
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
// 定义加载动画类型
type LoadingType = 'spinner' | 'dots' | 'pulse' | 'ring'
// Props 定义
const props = defineProps<{
// 是否显示加载组件
visible: boolean
// 加载动画类型
type?: LoadingType
// 加载动画大小
size?: number | string
// 加载动画颜色
color?: string
// 是否全屏显示
fullscreen?: boolean
// 加载文本
text?: string
// 遮罩层背景色
background?: string
// 遮罩层透明度
opacity?: number
// 线条宽度仅适用于spinner和ring类型
strokeWidth?: number
// 线条末端样式仅适用于spinner类型
strokeLinecap?: 'round' | 'butt' | 'square'
}>()
// 计算容器样式
const containerStyle = computed(() => {
const style: any = {}
if (props.fullscreen) {
style.backgroundColor = props.background || 'rgba(255, 255, 255, 0.9)'
style.opacity = props.opacity ?? 1
}
return style
})
// 计算spinner包装器样式
const spinnerWrapperStyle = computed(() => {
return {
color: props.color || '#1890ff'
}
})
// 计算spinner样式
const spinnerStyle = computed(() => {
return {
width: typeof props.size === 'number' ? `${props.size}px` : props.size || '40px',
height: typeof props.size === 'number' ? `${props.size}px` : props.size || '40px'
}
})
// 计算svg样式
const svgStyle = computed(() => {
return {
animationDuration: '1.4s',
transformOrigin: 'center center'
}
})
// 计算线条宽度
const strokeWidth = computed(() => {
return props.strokeWidth || 3
})
// 计算线条末端样式
const strokeLinecap = computed(() => {
return props.strokeLinecap || 'round'
})
// 计算点状加载容器样式
const dotsContainerStyle = computed(() => {
return {
gap: typeof props.size === 'number' ? `${props.size / 6}px` : '6px'
}
})
// 计算点状加载样式
const dotStyle = computed(() => {
const dotSize = typeof props.size === 'number' ? props.size / 5 : 8
return {
width: `${dotSize}px`,
height: `${dotSize}px`,
backgroundColor: props.color || '#1890ff'
}
})
// 计算脉冲样式
const pulseStyle = computed(() => {
const pulseSize = typeof props.size === 'number' ? props.size : 40
return {
width: `${pulseSize}px`,
height: `${pulseSize}px`,
backgroundColor: props.color || '#1890ff'
}
})
// 计算环形样式
const ringStyle = computed(() => {
const ringSize = typeof props.size === 'number' ? props.size : 40
return {
width: `${ringSize}px`,
height: `${ringSize}px`
}
})
// 计算环形内部圆样式
const ringCircleStyle = computed(() => {
const circleBorderWidth = props.strokeWidth || 3
return {
borderWidth: `${circleBorderWidth}px`,
borderColor: `${props.color || '#1890ff'} transparent ${props.color || '#1890ff'} transparent`
}
})
// 计算文本样式
const textStyle = computed(() => {
return {
marginTop: typeof props.size === 'number' ? `${props.size / 4}px` : '10px',
color: props.color || '#666'
}
})
</script>
<style scoped>
/* 全屏容器样式 */
.loading-container-fullscreen {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
/* 内联容器样式 */
.loading-container-inline {
display: inline-flex;
justify-content: center;
align-items: center;
}
/* 旋转圆环样式 */
.loading-spinner {
display: flex;
justify-content: center;
align-items: center;
}
.loading-svg {
animation: rotate 1.4s linear infinite;
transform-origin: center center;
}
/* 确保SVG中的circle元素也正确居中 */
.loading-svg circle {
transform-origin: 25px 25px; /* 匹配circle的cx="25" cy="25" */
}
.loading-path {
animation: dash 1.4s ease-in-out infinite;
}
/* 点状加载样式 */
.loading-dots {
display: flex;
align-items: center;
justify-content: center;
}
.loading-dot {
border-radius: 50%;
animation: bounce 1.4s ease-in-out infinite;
}
/* 脉冲样式 */
.loading-pulse {
border-radius: 50%;
animation: pulse 1.4s ease-in-out infinite;
}
/* 环形样式 */
.loading-ring {
display: flex;
justify-content: center;
align-items: center;
}
.loading-ring-circle {
width: 100%;
height: 100%;
border-style: solid;
border-radius: 50%;
animation: spin 1.4s linear infinite;
transform-origin: center center;
}
/* 加载文本样式 */
.loading-text {
font-size: 14px;
text-align: center;
white-space: nowrap;
}
/* 动画定义 */
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
@keyframes dash {
0% {
stroke-dashoffset: 94.2;
}
50% {
stroke-dashoffset: 23.55;
transform: rotate(45deg);
}
100% {
stroke-dashoffset: 94.2;
transform: rotate(360deg);
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes bounce {
0%, 80%, 100% {
transform: scale(0);
opacity: 0.5;
}
40% {
transform: scale(1);
opacity: 1;
}
}
@keyframes pulse {
0% {
transform: scale(0.3);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 0;
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>