mirror of
https://github.com/OpenListTeam/OpenList-Desktop.git
synced 2025-11-25 19:27:33 +08:00
feat: add loading indicators and processing state for core and rclone actions close #66
This commit is contained in:
@@ -4,21 +4,34 @@
|
|||||||
<div class="action-section">
|
<div class="action-section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h4>{{ t('dashboard.quickActions.openlistService') }}</h4>
|
<h4>{{ t('dashboard.quickActions.openlistService') }}</h4>
|
||||||
|
<div v-if="isCoreLoading" class="section-loading-indicator">
|
||||||
|
<Loader :size="12" class="loading-icon" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<button @click="toggleCore" :class="['action-btn', 'service-btn', { running: isCoreRunning }]">
|
<button
|
||||||
<component :is="serviceButtonIcon" :size="20" />
|
@click="toggleCore"
|
||||||
<span>{{ serviceButtonText }}</span>
|
:disabled="isCoreLoading"
|
||||||
|
:class="['action-btn', 'service-btn', { running: isCoreRunning, loading: isCoreLoading }]"
|
||||||
|
>
|
||||||
|
<component v-if="!isCoreLoading" :is="serviceButtonIcon" :size="20" />
|
||||||
|
<Loader v-else :size="20" class="loading-icon" />
|
||||||
|
<span>{{ isCoreLoading ? t('dashboard.quickActions.processing') : serviceButtonText }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button @click="restartCore" :disabled="!isCoreRunning" class="action-btn restart-btn">
|
<button
|
||||||
<RotateCcw :size="18" />
|
@click="restartCore"
|
||||||
|
:disabled="!isCoreRunning || isCoreLoading"
|
||||||
|
:class="['action-btn', 'restart-btn', { loading: isCoreLoading }]"
|
||||||
|
>
|
||||||
|
<RotateCcw v-if="!isCoreLoading" :size="18" />
|
||||||
|
<Loader v-else :size="18" class="loading-icon" />
|
||||||
<span>{{ t('dashboard.quickActions.restart') }}</span>
|
<span>{{ t('dashboard.quickActions.restart') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@click="openWebUI"
|
@click="openWebUI"
|
||||||
:disabled="!isCoreRunning"
|
:disabled="!isCoreRunning || isCoreLoading"
|
||||||
class="action-btn web-btn"
|
class="action-btn web-btn"
|
||||||
:title="appStore.openListCoreUrl"
|
:title="appStore.openListCoreUrl"
|
||||||
>
|
>
|
||||||
@@ -47,15 +60,26 @@
|
|||||||
<div class="action-section">
|
<div class="action-section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h4>{{ t('dashboard.quickActions.rclone') }}</h4>
|
<h4>{{ t('dashboard.quickActions.rclone') }}</h4>
|
||||||
|
<div v-if="isRcloneLoading" class="section-loading-indicator">
|
||||||
|
<Loader :size="12" class="loading-icon" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<button
|
<button
|
||||||
@click="rcloneStore.serviceRunning ? stopBackend() : startBackend()"
|
@click="rcloneStore.serviceRunning ? stopBackend() : startBackend()"
|
||||||
:class="['action-btn', 'service-indicator-btn', { active: rcloneStore.serviceRunning }]"
|
:disabled="isRcloneLoading"
|
||||||
|
:class="[
|
||||||
|
'action-btn',
|
||||||
|
'service-indicator-btn',
|
||||||
|
{ active: rcloneStore.serviceRunning, loading: isRcloneLoading }
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<component :is="rcloneStore.serviceRunning ? Square : Play" :size="18" />
|
<component v-if="!isRcloneLoading" :is="rcloneStore.serviceRunning ? Square : Play" :size="18" />
|
||||||
|
<Loader v-else :size="18" class="loading-icon" />
|
||||||
<span>{{
|
<span>{{
|
||||||
rcloneStore.serviceRunning
|
isRcloneLoading
|
||||||
|
? t('dashboard.quickActions.processing')
|
||||||
|
: rcloneStore.serviceRunning
|
||||||
? t('dashboard.quickActions.stopRclone')
|
? t('dashboard.quickActions.stopRclone')
|
||||||
: t('dashboard.quickActions.startRclone')
|
: t('dashboard.quickActions.startRclone')
|
||||||
}}</span>
|
}}</span>
|
||||||
@@ -118,7 +142,7 @@ import { useAppStore } from '../../stores/app'
|
|||||||
import { useRcloneStore } from '../../stores/rclone'
|
import { useRcloneStore } from '../../stores/rclone'
|
||||||
import { useTranslation } from '../../composables/useI18n'
|
import { useTranslation } from '../../composables/useI18n'
|
||||||
import Card from '../ui/Card.vue'
|
import Card from '../ui/Card.vue'
|
||||||
import { Play, Square, RotateCcw, ExternalLink, Settings, HardDrive, Key, Shield } from 'lucide-vue-next'
|
import { Play, Square, RotateCcw, ExternalLink, Settings, HardDrive, Key, Shield, Loader } from 'lucide-vue-next'
|
||||||
import { TauriAPI } from '@/api/tauri'
|
import { TauriAPI } from '@/api/tauri'
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -127,6 +151,8 @@ const appStore = useAppStore()
|
|||||||
const rcloneStore = useRcloneStore()
|
const rcloneStore = useRcloneStore()
|
||||||
|
|
||||||
const isCoreRunning = computed(() => appStore.isCoreRunning)
|
const isCoreRunning = computed(() => appStore.isCoreRunning)
|
||||||
|
const isCoreLoading = computed(() => appStore.loading)
|
||||||
|
const isRcloneLoading = computed(() => rcloneStore.loading)
|
||||||
const settings = computed(() => appStore.settings)
|
const settings = computed(() => appStore.settings)
|
||||||
let statusCheckInterval: number | null = null
|
let statusCheckInterval: number | null = null
|
||||||
|
|
||||||
@@ -452,6 +478,16 @@ onUnmounted(() => {
|
|||||||
letter-spacing: -0.025em;
|
letter-spacing: -0.025em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section-loading-indicator {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-icon {
|
||||||
|
opacity: 0.7;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
.icon-only-btn {
|
.icon-only-btn {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
min-width: auto;
|
min-width: auto;
|
||||||
@@ -509,6 +545,15 @@ onUnmounted(() => {
|
|||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
.action-btn.loading {
|
||||||
|
opacity: 0.8;
|
||||||
|
cursor: wait !important;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn.loading .loading-icon {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.service-btn.running {
|
.service-btn.running {
|
||||||
background: rgb(239, 68, 68);
|
background: rgb(239, 68, 68);
|
||||||
|
|||||||
@@ -23,10 +23,11 @@
|
|||||||
"openlistService": "OpenList Core",
|
"openlistService": "OpenList Core",
|
||||||
"rclone": "RClone",
|
"rclone": "RClone",
|
||||||
"quickSettings": "Quick Settings",
|
"quickSettings": "Quick Settings",
|
||||||
"startOpenListCore": "Start Core",
|
"startOpenListCore": "Start",
|
||||||
"stopOpenListCore": "Stop Core",
|
"stopOpenListCore": "Stop",
|
||||||
|
"processing": "Processing...",
|
||||||
"restart": "Restart",
|
"restart": "Restart",
|
||||||
"openWeb": "Web UI",
|
"openWeb": "Web",
|
||||||
"configRclone": "Configure RClone",
|
"configRclone": "Configure RClone",
|
||||||
"startRclone": "Start RClone",
|
"startRclone": "Start RClone",
|
||||||
"stopRclone": "Stop RClone",
|
"stopRclone": "Stop RClone",
|
||||||
|
|||||||
@@ -23,10 +23,11 @@
|
|||||||
"openlistService": "OpenList 核心",
|
"openlistService": "OpenList 核心",
|
||||||
"rclone": "RClone",
|
"rclone": "RClone",
|
||||||
"quickSettings": "快速设置",
|
"quickSettings": "快速设置",
|
||||||
"startOpenListCore": "启动核心",
|
"startOpenListCore": "启动",
|
||||||
"stopOpenListCore": "停止核心",
|
"stopOpenListCore": "停止",
|
||||||
|
"processing": "处理中...",
|
||||||
"restart": "重启",
|
"restart": "重启",
|
||||||
"openWeb": "网页界面",
|
"openWeb": "网页",
|
||||||
"configRclone": "配置 RClone",
|
"configRclone": "配置 RClone",
|
||||||
"startRclone": "启动 RClone",
|
"startRclone": "启动 RClone",
|
||||||
"stopRclone": "停止 RClone",
|
"stopRclone": "停止 RClone",
|
||||||
|
|||||||
Reference in New Issue
Block a user