mi-assessment/uniapp/components/Loading/index.vue
2026-02-09 14:45:06 +08:00

189 lines
3.7 KiB
Vue

<template>
<!-- 页面加载状态 -->
<view v-if="type === 'page' && loading" class="loading-page">
<view class="loading-spinner">
<view class="spinner"></view>
</view>
<text class="loading-text">{{ text || '加载中...' }}</text>
</view>
<!-- 下拉刷新状态 -->
<view v-else-if="type === 'refresh'" class="loading-refresh">
<view class="refresh-spinner" :class="{ 'is-loading': loading }">
<view class="spinner-icon"></view>
</view>
<text class="refresh-text">{{ loading ? (text || '刷新中...') : '下拉刷新' }}</text>
</view>
<!-- 加载更多状态 -->
<view v-else-if="type === 'more'" class="loading-more">
<template v-if="loading">
<view class="more-spinner"></view>
<text class="more-text">{{ text || '加载中...' }}</text>
</template>
<template v-else-if="noMore">
<text class="more-text no-more">{{ noMoreText || '没有更多了' }}</text>
</template>
<template v-else>
<text class="more-text">上拉加载更多</text>
</template>
</view>
<!-- 内联加载状态 -->
<view v-else-if="type === 'inline' && loading" class="loading-inline">
<view class="inline-spinner"></view>
<text v-if="text" class="inline-text">{{ text }}</text>
</view>
</template>
<script>
export default {
name: 'Loading',
props: {
type: {
type: String,
default: 'page',
validator: (value) => ['page', 'refresh', 'more', 'inline'].includes(value)
},
loading: {
type: Boolean,
default: false
},
text: {
type: String,
default: ''
},
noMore: {
type: Boolean,
default: false
},
noMoreText: {
type: String,
default: '没有更多了'
}
}
}
</script>
<style lang="scss" scoped>
// 通用spinner动画
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
// 页面加载
.loading-page {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.9);
z-index: 998;
.loading-spinner {
margin-bottom: 20rpx;
.spinner {
width: 60rpx;
height: 60rpx;
border: 4rpx solid #f0f0f0;
border-top-color: #ff6b6b;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
}
.loading-text {
font-size: 28rpx;
color: #666;
}
}
// 下拉刷新
.loading-refresh {
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 0;
.refresh-spinner {
margin-bottom: 10rpx;
.spinner-icon {
width: 40rpx;
height: 40rpx;
border: 3rpx solid #f0f0f0;
border-top-color: #ff6b6b;
border-radius: 50%;
}
&.is-loading .spinner-icon {
animation: spin 0.8s linear infinite;
}
}
.refresh-text {
font-size: 24rpx;
color: #999;
}
}
// 加载更多
.loading-more {
display: flex;
align-items: center;
justify-content: center;
padding: 30rpx 0;
.more-spinner {
width: 32rpx;
height: 32rpx;
border: 3rpx solid #f0f0f0;
border-top-color: #ff6b6b;
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin-right: 12rpx;
}
.more-text {
font-size: 26rpx;
color: #999;
&.no-more {
color: #ccc;
}
}
}
// 内联加载
.loading-inline {
display: inline-flex;
align-items: center;
.inline-spinner {
width: 28rpx;
height: 28rpx;
border: 2rpx solid #f0f0f0;
border-top-color: #ff6b6b;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
.inline-text {
font-size: 24rpx;
color: #999;
margin-left: 8rpx;
}
}
</style>