diff --git a/frontend/bindings/voidraft/internal/services/configservice.ts b/frontend/bindings/voidraft/internal/services/configservice.ts index c5d038e..ddf3cba 100644 --- a/frontend/bindings/voidraft/internal/services/configservice.ts +++ b/frontend/bindings/voidraft/internal/services/configservice.ts @@ -10,6 +10,9 @@ // @ts-ignore: Unused imports import {Call as $Call, Create as $Create} from "@wailsio/runtime"; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js"; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: Unused imports import * as models$0 from "../models/models.js"; @@ -38,22 +41,6 @@ export function GetConfig(): Promise & { cancel(): vo return $typingPromise; } -/** - * GetConfigDir 获取配置目录 - */ -export function GetConfigDir(): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(2275626561) as any; - return $resultPromise; -} - -/** - * GetSettingsPath 获取设置文件路径 - */ -export function GetSettingsPath(): Promise & { cancel(): void } { - let $resultPromise = $Call.ByID(2175583370) as any; - return $resultPromise; -} - /** * MigrateConfig 执行配置迁移 */ @@ -78,6 +65,14 @@ export function ServiceShutdown(): Promise & { cancel(): void } { return $resultPromise; } +/** + * ServiceStartup initializes the service when the application starts + */ +export function ServiceStartup(options: application$0.ServiceOptions): Promise & { cancel(): void } { + let $resultPromise = $Call.ByID(3311949428, options) as any; + return $resultPromise; +} + /** * Set 设置配置项 */ diff --git a/frontend/bindings/voidraft/internal/services/models.ts b/frontend/bindings/voidraft/internal/services/models.ts index e7aee79..ba5c171 100644 --- a/frontend/bindings/voidraft/internal/services/models.ts +++ b/frontend/bindings/voidraft/internal/services/models.ts @@ -268,9 +268,6 @@ export class OSInfo { /** * ObserverCallback 观察者回调函数 - * 参数: - * - oldValue: 配置变更前的值 - * - newValue: 配置变更后的值 */ export type ObserverCallback = any; diff --git a/frontend/src/stores/backupStore.ts b/frontend/src/stores/backupStore.ts index ecc4041..530a877 100644 --- a/frontend/src/stores/backupStore.ts +++ b/frontend/src/stores/backupStore.ts @@ -1,175 +1,49 @@ import { defineStore } from 'pinia'; -import { computed, readonly, ref, shallowRef, watchEffect, onScopeDispose } from 'vue'; -import type { GitBackupConfig } from '@/../bindings/voidraft/internal/models'; +import { ref, onScopeDispose } from 'vue'; import { BackupService } from '@/../bindings/voidraft/internal/services'; import { useConfigStore } from '@/stores/configStore'; import { createTimerManager } from '@/common/utils/timerUtils'; -// 备份状态枚举 -export enum BackupStatus { - IDLE = 'idle', - PUSHING = 'pushing', - SUCCESS = 'success', - ERROR = 'error' -} - -// 备份操作结果类型 -export interface BackupResult { - status: BackupStatus; - message?: string; - timestamp?: number; -} - -// 类型守卫函数 -const isBackupError = (error: unknown): error is Error => { - return error instanceof Error; -}; - -// 工具类型:提取错误消息 -type ErrorMessage = T extends Error ? string : string; - - export const useBackupStore = defineStore('backup', () => { - // === 核心状态 === - const config = shallowRef(null); - - // 统一的备份结果状态 - const backupResult = ref({ - status: BackupStatus.IDLE - }); - - // === 定时器管理 === - const statusTimer = createTimerManager(); + const isPushing = ref(false); + const message = ref(null); + const isError = ref(false); - // 组件卸载时清理定时器 - onScopeDispose(() => { - statusTimer.clear(); - }); - - // === 外部依赖 === + const timer = createTimerManager(); const configStore = useConfigStore(); - // === 计算属性 === - const isEnabled = computed(() => configStore.config.backup.enabled); - const isConfigured = computed(() => Boolean(configStore.config.backup.repo_url?.trim())); - - // 派生状态计算属性 - const isPushing = computed(() => backupResult.value.status === BackupStatus.PUSHING); - const isSuccess = computed(() => backupResult.value.status === BackupStatus.SUCCESS); - const isError = computed(() => backupResult.value.status === BackupStatus.ERROR); - const errorMessage = computed(() => - backupResult.value.status === BackupStatus.ERROR ? backupResult.value.message : null - ); + onScopeDispose(() => timer.clear()); - // === 状态管理方法 === - - /** - * 设置备份状态 - * @param status 备份状态 - * @param message 可选消息 - * @param autoHide 是否自动隐藏(毫秒) - */ - const setBackupStatus = ( - status: T, - message?: T extends BackupStatus.ERROR ? string : string, - autoHide?: number - ): void => { - statusTimer.clear(); + const pushToRemote = async () => { + const isConfigured = Boolean(configStore.config.backup.repo_url?.trim()); - backupResult.value = { - status, - message, - timestamp: Date.now() - }; - - // 自动隐藏逻辑 - if (autoHide && (status === BackupStatus.SUCCESS || status === BackupStatus.ERROR)) { - statusTimer.set(() => { - if (backupResult.value.status === status) { - backupResult.value = { status: BackupStatus.IDLE }; - } - }, autoHide); - } - }; - - /** - * 清除当前状态 - */ - const clearStatus = (): void => { - statusTimer.clear(); - backupResult.value = { status: BackupStatus.IDLE }; - }; - - /** - * 处理错误的通用方法 - */ - const handleError = (error: unknown): void => { - const message: ErrorMessage = isBackupError(error) - ? error.message - : 'Backup operation failed'; - - setBackupStatus(BackupStatus.ERROR, message, 5000); - }; - - // === 业务逻辑方法 === - - /** - * 推送到远程仓库 - * 使用现代 async/await 和错误处理 - */ - const pushToRemote = async (): Promise => { - // 前置条件检查 - if (isPushing.value || !isConfigured.value) { + if (isPushing.value || !isConfigured) { return; } try { - setBackupStatus(BackupStatus.PUSHING); + isPushing.value = true; + message.value = null; + timer.clear(); await BackupService.PushToRemote(); - setBackupStatus(BackupStatus.SUCCESS, 'Backup completed successfully', 3000); + isError.value = false; + message.value = 'push successful'; + timer.set(() => { message.value = null; }, 3000); } catch (error) { - handleError(error); + isError.value = true; + message.value = error instanceof Error ? error.message : 'backup operation failed'; + timer.set(() => { message.value = null; }, 5000); + } finally { + isPushing.value = false; } }; - /** - * 重试备份操作 - */ - const retryBackup = async (): Promise => { - if (isError.value) { - await pushToRemote(); - } - }; - - // === 响应式副作用 === - - // 监听配置变化,自动清除错误状态 - watchEffect(() => { - if (isEnabled.value && isConfigured.value && isError.value) { - // 配置修复后清除错误状态 - clearStatus(); - } - }); - - // === 返回的 API === return { - // 只读状态 - config: readonly(config), - backupResult: readonly(backupResult), - - // 计算属性 - isEnabled, - isConfigured, isPushing, - isSuccess, + message, isError, - errorMessage, - - // 方法 - pushToRemote, - retryBackup, - clearStatus - } as const; + pushToRemote + }; }); \ No newline at end of file diff --git a/frontend/src/views/settings/components/SettingItem.vue b/frontend/src/views/settings/components/SettingItem.vue index f3c791b..67065b9 100644 --- a/frontend/src/views/settings/components/SettingItem.vue +++ b/frontend/src/views/settings/components/SettingItem.vue @@ -2,6 +2,7 @@ defineProps<{ title: string; description?: string; + descriptionType?: 'default' | 'success' | 'error'; }>(); @@ -9,7 +10,16 @@ defineProps<{
{{ title }}
-
{{ description }}
+
+ {{ description }} +
@@ -48,6 +58,14 @@ defineProps<{ font-size: 11px; color: var(--settings-text-secondary); line-height: 1.4; + + &.description-success { + color: #4caf50; + } + + &.description-error { + color: #f44336; + } } } diff --git a/frontend/src/views/settings/pages/BackupPage.vue b/frontend/src/views/settings/pages/BackupPage.vue index a698b80..8a26bb3 100644 --- a/frontend/src/views/settings/pages/BackupPage.vue +++ b/frontend/src/views/settings/pages/BackupPage.vue @@ -2,7 +2,7 @@ import {useConfigStore} from '@/stores/configStore'; import {useBackupStore} from '@/stores/backupStore'; import {useI18n} from 'vue-i18n'; -import {computed, onUnmounted} from 'vue'; +import {computed} from 'vue'; import SettingSection from '../components/SettingSection.vue'; import SettingItem from '../components/SettingItem.vue'; import ToggleSwitch from '../components/ToggleSwitch.vue'; @@ -13,18 +13,12 @@ const {t} = useI18n(); const configStore = useConfigStore(); const backupStore = useBackupStore(); -onUnmounted(() => { - backupStore.clearStatus(); -}); - -// 认证方式选项 const authMethodOptions = computed(() => [ {value: AuthMethod.Token, label: t('settings.backup.authMethods.token')}, {value: AuthMethod.SSHKey, label: t('settings.backup.authMethods.sshKey')}, {value: AuthMethod.UserPass, label: t('settings.backup.authMethods.userPass')} ]); -// 备份间隔选项(分钟) const backupIntervalOptions = computed(() => [ {value: 5, label: t('settings.backup.intervals.5min')}, {value: 10, label: t('settings.backup.intervals.10min')}, @@ -33,124 +27,11 @@ const backupIntervalOptions = computed(() => [ {value: 60, label: t('settings.backup.intervals.1hour')} ]); -// 计算属性 - 启用备份 -const enableBackup = computed({ - get: () => configStore.config.backup.enabled, - set: (value: boolean) => configStore.setEnableBackup(value) -}); - -// 计算属性 - 自动备份 -const autoBackup = computed({ - get: () => configStore.config.backup.auto_backup, - set: (value: boolean) => configStore.setAutoBackup(value) -}); - -// 仓库URL -const repoUrl = computed({ - get: () => configStore.config.backup.repo_url, - set: (value: string) => configStore.setRepoUrl(value) -}); - - -// 认证方式 -const authMethod = computed({ - get: () => configStore.config.backup.auth_method, - set: (value: AuthMethod) => configStore.setAuthMethod(value) -}); - -// 备份间隔 -const backupInterval = computed({ - get: () => configStore.config.backup.backup_interval, - set: (value: number) => configStore.setBackupInterval(value) -}); - -// 用户名 -const username = computed({ - get: () => configStore.config.backup.username, - set: (value: string) => configStore.setUsername(value) -}); - -// 密码 -const password = computed({ - get: () => configStore.config.backup.password, - set: (value: string) => configStore.setPassword(value) -}); - -// 访问令牌 -const token = computed({ - get: () => configStore.config.backup.token, - set: (value: string) => configStore.setToken(value) -}); - -// SSH密钥路径 -const sshKeyPath = computed({ - get: () => configStore.config.backup.ssh_key_path, - set: (value: string) => configStore.setSshKeyPath(value) -}); - -// SSH密钥密码 -const sshKeyPassphrase = computed({ - get: () => configStore.config.backup.ssh_key_passphrase, - set: (value: string) => configStore.setSshKeyPassphrase(value) -}); - -// 处理输入变化 -const handleRepoUrlChange = (event: Event) => { - const target = event.target as HTMLInputElement; - repoUrl.value = target.value; -}; - - -const handleUsernameChange = (event: Event) => { - const target = event.target as HTMLInputElement; - username.value = target.value; -}; - -const handlePasswordChange = (event: Event) => { - const target = event.target as HTMLInputElement; - password.value = target.value; -}; - -const handleTokenChange = (event: Event) => { - const target = event.target as HTMLInputElement; - token.value = target.value; -}; - -const handleSshKeyPassphraseChange = (event: Event) => { - const target = event.target as HTMLInputElement; - sshKeyPassphrase.value = target.value; -}; - -const handleAuthMethodChange = (event: Event) => { - const target = event.target as HTMLSelectElement; - authMethod.value = target.value as AuthMethod; -}; - -const handleBackupIntervalChange = (event: Event) => { - const target = event.target as HTMLSelectElement; - backupInterval.value = parseInt(target.value); -}; - -// 推送到远程 -const pushToRemote = async () => { - await backupStore.pushToRemote(); -}; - -// 重试备份 -const retryBackup = async () => { - await backupStore.retryBackup(); -}; - -// 选择SSH密钥文件 const selectSshKeyFile = async () => { - // 使用DialogService选择文件 const selectedPath = await DialogService.SelectFile(); - // 检查用户是否取消了选择或路径为空 - if (!selectedPath.trim()) { - return; + if (selectedPath.trim()) { + configStore.setSshKeyPath(selectedPath.trim()); } - // 更新SSH密钥路径 - sshKeyPath.value = selectedPath.trim(); }; @@ -158,34 +39,35 @@ const selectSshKeyFile = async () => {
- - + + - + @@ -194,110 +76,94 @@ const selectSshKeyFile = async () => { - + - - - + -