效果如下:

代码如下:
javascript
<template>
<div class="loading-wrapper">
<div class="loading-container">
<!-- 外层灰色滑轨 -->
<svg class="loading-svg" viewBox="0 0 100 100">
<circle
class="track"
cx="50"
cy="50"
r="47"
stroke-width="6"
fill="none"
/>
<!-- 蓝色旋转滑块 -->
<circle
class="progress"
cx="50"
cy="50"
r="47"
stroke-width="6"
stroke-linecap="round"
fill="none"
/>
</svg>
<!-- 中间固定图标 -->
<div class="center-icon">
<slot name="icon">
<!-- 默认图标 -->
<svg viewBox="0 0 24 24" width="32" height="32" fill="#2E83FF">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
</svg>
</slot>
</div>
</div>
<!-- 可选的文字提示 -->
<div v-if="text" class="loading-text">{{ text }}</div>
</div>
</template>
<script setup>
import { ref, defineProps } from 'vue';
const props = defineProps({
text: {
type: String,
default: ''
},
size: {
type: Number,
default: 80
},
color: {
type: String,
default: '#2E83FF' // 蓝色滑块
}
});
</script>
<style lang="scss" scoped>
.loading-wrapper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}
.loading-container {
position: relative;
width: v-bind(size + 'px');
height: v-bind(size + 'px');
}
.loading-svg {
width: 100%;
height: 100%;
transform: rotate(-90deg); /* 从顶部开始 */
}
/* 灰色滑轨 */
.track {
stroke: #e0e0e0;
}
/* 蓝色旋转滑块 */
.progress {
stroke: v-bind(color);
stroke-dasharray: 295; /* 2 * PI * 47 = 295.31 */
stroke-dashoffset: 70; /* 留出缺口 */
animation: rotate 1.5s linear infinite;
transform-origin: center;
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* 中间固定图标 */
.center-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
align-items: center;
justify-content: center;
color: v-bind(color);
/* 图标不旋转 */
}
.loading-text {
margin-top: 16px;
font-size: 14px;
color: #606266;
}
</style>