mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 07:28:37 +08:00
重构 LoadingBanner 组件
This commit is contained in:
@@ -1,15 +1,88 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// 定义输入参数
|
// 定义输入参数
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
progress: Number,
|
|
||||||
text: String,
|
text: String,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full text-center text-gray-500 text-sm flex flex-col items-center mb-5">
|
<div class="w-full text-center text-gray-500 text-sm flex flex-col items-center my-5">
|
||||||
<VProgressCircular v-if="!props.text || !props.progress" class="mb-3" size="64" indeterminate color="primary" />
|
<div class="initial-loading-container">
|
||||||
<VProgressCircular v-if="props.progress" class="mb-3" color="primary" :model-value="props.progress" size="64" />
|
<div class="initial-loading-content">
|
||||||
<span>{{ props.text }}</span>
|
<div class="wave-loader">
|
||||||
|
<div class="wave-dot"></div>
|
||||||
|
<div class="wave-dot"></div>
|
||||||
|
<div class="wave-dot"></div>
|
||||||
|
<div class="wave-dot"></div>
|
||||||
|
</div>
|
||||||
|
<div class="initial-loading-text" v-if="props.text">{{ props.text }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 初始的加载状态 */
|
||||||
|
.initial-loading-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-block-size: 20vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.initial-loading-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wave-loader {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
block-size: 40px;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wave-dot {
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: wave 1.5s ease-in-out infinite;
|
||||||
|
background-color: rgb(var(--v-theme-primary));
|
||||||
|
block-size: 8px;
|
||||||
|
inline-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wave-dot:nth-child(1) {
|
||||||
|
animation-delay: 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wave-dot:nth-child(2) {
|
||||||
|
animation-delay: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wave-dot:nth-child(3) {
|
||||||
|
animation-delay: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wave-dot:nth-child(4) {
|
||||||
|
animation-delay: 0.6s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes wave {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: translateY(-15px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.initial-loading-text {
|
||||||
|
color: rgb(var(--v-theme-primary));
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -599,9 +599,7 @@ onMounted(() => {
|
|||||||
</IconBtn>
|
</IconBtn>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<VCardText v-if="loading" class="text-center flex flex-col items-center">
|
<LoadingBanner v-if="loading" />
|
||||||
<VProgressCircular size="48" indeterminate color="primary" />
|
|
||||||
</VCardText>
|
|
||||||
<!-- 文件详情 -->
|
<!-- 文件详情 -->
|
||||||
<VCardText v-else-if="isFile && !isImage && items.length > 0" class="text-center break-all">
|
<VCardText v-else-if="isFile && !isImage && items.length > 0" class="text-center break-all">
|
||||||
<div v-if="items[0]?.thumbnail" class="flex justify-center">
|
<div v-if="items[0]?.thumbnail" class="flex justify-center">
|
||||||
|
|||||||
+7
-79
@@ -65,20 +65,22 @@ const errorTitle = ref(t('resource.noData'))
|
|||||||
const errorDescription = ref(t('resource.noResourceFound'))
|
const errorDescription = ref(t('resource.noResourceFound'))
|
||||||
|
|
||||||
// 添加安全超时,确保进度条不会永远卡住
|
// 添加安全超时,确保进度条不会永远卡住
|
||||||
const watchProgressValue = watch(progressValue,
|
const watchProgressValue = watch(
|
||||||
|
progressValue,
|
||||||
debounce(async () => {
|
debounce(async () => {
|
||||||
if (progressEventSource.value && progressValue.value < 100) {
|
if (progressEventSource.value && progressValue.value < 100) {
|
||||||
console.warn("卡进度超时 关闭进度条")
|
console.warn('卡进度超时 关闭进度条')
|
||||||
stopLoadingProgress()
|
stopLoadingProgress()
|
||||||
}
|
}
|
||||||
}, 60_000))
|
}, 60_000),
|
||||||
|
)
|
||||||
|
|
||||||
// 使用SSE监听加载进度
|
// 使用SSE监听加载进度
|
||||||
function startLoadingProgress() {
|
function startLoadingProgress() {
|
||||||
watchProgressValue.resume()
|
watchProgressValue.resume()
|
||||||
progressText.value = t('resource.searching')
|
progressText.value = t('resource.searching')
|
||||||
progressValue.value = 0
|
progressValue.value = 0
|
||||||
progressEnabled.value = true
|
progressEnabled.value = false
|
||||||
progressEventSource.value = new EventSource(`${import.meta.env.VITE_API_BASE_URL}system/progress/search`)
|
progressEventSource.value = new EventSource(`${import.meta.env.VITE_API_BASE_URL}system/progress/search`)
|
||||||
progressEventSource.value.onmessage = event => {
|
progressEventSource.value.onmessage = event => {
|
||||||
const progress = JSON.parse(event.data)
|
const progress = JSON.parse(event.data)
|
||||||
@@ -278,17 +280,7 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 初始加载状态 -->
|
<!-- 初始加载状态 -->
|
||||||
<div v-else-if="!isRefreshed && !progressValue" class="initial-loading-container">
|
<LoadingBanner v-else-if="!isRefreshed && !(progressEnabled || progressValue > 0)" />
|
||||||
<div class="initial-loading-content">
|
|
||||||
<div class="wave-loader">
|
|
||||||
<div class="wave-dot"></div>
|
|
||||||
<div class="wave-dot"></div>
|
|
||||||
<div class="wave-dot"></div>
|
|
||||||
<div class="wave-dot"></div>
|
|
||||||
</div>
|
|
||||||
<div class="initial-loading-text">{{ t('resource.searching') }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 滚动到顶部按钮 -->
|
<!-- 滚动到顶部按钮 -->
|
||||||
<VScrollToTopBtn />
|
<VScrollToTopBtn />
|
||||||
</div>
|
</div>
|
||||||
@@ -457,70 +449,6 @@ onUnmounted(() => {
|
|||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 初始的加载状态 */
|
|
||||||
.initial-loading-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
min-block-size: 50vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.initial-loading-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wave-loader {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
block-size: 40px;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wave-dot {
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: wave 1.5s ease-in-out infinite;
|
|
||||||
background-color: rgb(var(--v-theme-primary));
|
|
||||||
block-size: 8px;
|
|
||||||
inline-size: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wave-dot:nth-child(1) {
|
|
||||||
animation-delay: 0s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wave-dot:nth-child(2) {
|
|
||||||
animation-delay: 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wave-dot:nth-child(3) {
|
|
||||||
animation-delay: 0.4s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wave-dot:nth-child(4) {
|
|
||||||
animation-delay: 0.6s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes wave {
|
|
||||||
0%,
|
|
||||||
100% {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
50% {
|
|
||||||
transform: translateY(-15px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.initial-loading-text {
|
|
||||||
color: rgb(var(--v-theme-primary));
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 500;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-results-container {
|
.search-results-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-block-size: 50vh;
|
min-block-size: 50vh;
|
||||||
|
|||||||
Reference in New Issue
Block a user