From 6aec0ddf88b98b8a3a67ae75c88f9bb17cd02f3b Mon Sep 17 00:00:00 2001 From: jxxghp Date: Wed, 10 Sep 2025 08:51:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=85=8D=E7=BD=AE=E5=90=91?= =?UTF-8?q?=E5=AF=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/en-US.ts | 48 +++---- src/locales/zh-CN.ts | 48 +++---- src/locales/zh-TW.ts | 48 +++---- src/pages/login.vue | 16 ++- src/pages/setup.vue | 308 ++++++++++++++++++++++++++++++++++++++++--- src/stores/types.ts | 2 + src/stores/user.ts | 7 + 7 files changed, 383 insertions(+), 94 deletions(-) diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index bf9fa650..7283d2ed 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -2892,31 +2892,21 @@ export default { completed: 'Setup Wizard completed!', failed: 'Setup Wizard failed, please try again', complete: 'Complete Configuration', - step1: { - title: 'Basic', - description: 'Set access domain, background wallpaper and recognition data source', - }, - step2: { - title: 'Storage Directory', - description: 'Configure download directory and media library directory', - }, - step3: { - title: 'Downloader', - description: 'Configure downloader (optional)', - }, - step4: { - title: 'Media Server', - description: 'Configure media server (optional)', - }, - step5: { - title: 'Notification', - description: 'Configure notification channels (optional)', - }, - step6: { - title: 'Resource Preferences', - description: 'Set resource download preferences', - }, + testing: 'Testing', + connectivityTestSuccess: 'Connectivity test passed', + connectivityTestFailed: 'Connectivity test failed', + testingStorage: 'Testing storage', + checkingStorage: 'Checking storage connectivity', + testingDownloader: 'Testing downloader', + checkingDownloader: 'Checking downloader connectivity', + testingMediaServer: 'Testing media server', + checkingMediaServer: 'Checking media server connectivity', + testingNotification: 'Testing notification', + checkingNotification: 'Checking notification connectivity', + testFailedHint: 'Please check if the configuration is correct, you can retest after modification', basic: { + title: 'Basic Settings', + description: 'Set access domain, username/password and network configuration', appDomain: 'App Domain', appDomainHint: 'Used to add quick jump links when sending notifications', wallpaper: 'Background Wallpaper', @@ -2927,6 +2917,8 @@ export default { apiTokenHint: 'System automatically generated API access token', }, storage: { + title: 'Storage Configuration', + description: 'Configure download directory and media library directory', info: 'Storage Configuration', infoDesc: 'Configure local storage directories for download and media library management', downloadPath: 'Download Directory', @@ -2935,6 +2927,8 @@ export default { libraryPathHint: 'Set the storage path for media files', }, downloader: { + title: 'Downloader Configuration', + description: 'Configure downloader (optional)', info: 'Downloader Configuration', infoDesc: 'Configure downloader for automatic resource download (optional)', type: 'Downloader Type', @@ -2949,6 +2943,8 @@ export default { downloadPath: 'Download Path', }, mediaServer: { + title: 'Media Server', + description: 'Configure media server (optional)', info: 'Media Server Configuration', infoDesc: 'Configure media server for media library management (optional)', type: 'Media Server Type', @@ -2963,6 +2959,8 @@ export default { token: 'Access Token', }, notification: { + title: 'Notification Settings', + description: 'Configure notification channels (optional)', info: 'Notification Configuration', infoDesc: 'Configure notification channels for receiving system messages (optional)', type: 'Notification Type', @@ -2980,6 +2978,8 @@ export default { receiverEmail: 'Receiver Email', }, preferences: { + title: 'Resource Preferences', + description: 'Set resource download preferences', info: 'Resource Preferences', infoDesc: 'Set resource download preferences, the system will automatically select the best resources based on these preferences', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 6e8894b4..f6f2dac0 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -2859,31 +2859,21 @@ export default { completed: '配置向导完成!', failed: '配置向导失败,请重试', complete: '完成配置', - step1: { - title: '基础', - description: '设置访问域名、背景壁纸和识别数据源', - }, - step2: { - title: '存储目录', - description: '配置下载目录和媒体库目录', - }, - step3: { - title: '下载器', - description: '配置下载器(可选)', - }, - step4: { - title: '媒体服务器', - description: '配置媒体服务器(可选)', - }, - step5: { - title: '通知', - description: '配置通知渠道(可选)', - }, - step6: { - title: '资源偏好', - description: '设置资源下载偏好', - }, + testing: '正在测试', + connectivityTestSuccess: '连通性测试通过', + connectivityTestFailed: '连通性测试失败', + testingStorage: '正在测试存储目录', + checkingStorage: '检查存储目录连通性', + testingDownloader: '正在测试下载器', + checkingDownloader: '检查下载器连通性', + testingMediaServer: '正在测试媒体服务器', + checkingMediaServer: '检查媒体服务器连通性', + testingNotification: '正在测试消息通知', + checkingNotification: '检查消息通知连通性', + testFailedHint: '请检查配置是否正确,修改后可以重新测试', basic: { + title: '基础设置', + description: '设置访问域名、用户名密码和网络配置', appDomain: '访问域名', appDomainHint: '用于发送通知时,添加快捷跳转地址', wallpaper: '背景壁纸', @@ -2894,6 +2884,8 @@ export default { apiTokenHint: '系统自动生成的 API 访问令牌', }, storage: { + title: '存储配置', + description: '配置下载目录和媒体库目录', info: '存储配置说明', infoDesc: '配置本地存储目录,用于下载和媒体库管理', downloadPath: '下载目录', @@ -2902,6 +2894,8 @@ export default { libraryPathHint: '设置媒体文件的存储路径', }, downloader: { + title: '下载器配置', + description: '配置下载器(可选)', info: '下载器配置说明', infoDesc: '配置下载器用于自动下载资源(可选)', type: '下载器类型', @@ -2916,6 +2910,8 @@ export default { downloadPath: '下载路径', }, mediaServer: { + title: '媒体服务器', + description: '配置媒体服务器(可选)', info: '媒体服务器配置说明', infoDesc: '配置媒体服务器用于媒体库管理(可选)', type: '媒体服务器类型', @@ -2930,6 +2926,8 @@ export default { token: '访问令牌', }, notification: { + title: '通知设置', + description: '配置通知渠道(可选)', info: '通知配置说明', infoDesc: '配置通知渠道用于接收系统消息(可选)', type: '通知类型', @@ -2947,6 +2945,8 @@ export default { receiverEmail: '接收邮箱', }, preferences: { + title: '资源偏好', + description: '设置资源下载偏好', info: '资源偏好说明', infoDesc: '设置资源下载的偏好,系统将根据这些偏好自动选择最佳资源', quality: '质量偏好', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 67dcc454..e542c318 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -2858,31 +2858,21 @@ export default { completed: '設定精靈完成!', failed: '設定精靈失敗,請重試', complete: '完成設定', - step1: { - title: '基礎', - description: '設定存取網域、背景桌布和識別資料來源', - }, - step2: { - title: '儲存目錄', - description: '設定下載目錄和媒體庫目錄', - }, - step3: { - title: '下載器', - description: '設定下載器(可選)', - }, - step4: { - title: '媒體伺服器', - description: '設定媒體伺服器(可選)', - }, - step5: { - title: '通知', - description: '設定通知管道(可選)', - }, - step6: { - title: '資源偏好', - description: '設定資源下載偏好', - }, + testing: '正在測試', + connectivityTestSuccess: '連通性測試通過', + connectivityTestFailed: '連通性測試失敗', + testingStorage: '正在測試存儲目錄', + checkingStorage: '檢查存儲目錄連通性', + testingDownloader: '正在測試下載器', + checkingDownloader: '檢查下載器連通性', + testingMediaServer: '正在測試媒體服務器', + checkingMediaServer: '檢查媒體服務器連通性', + testingNotification: '正在測試消息通知', + checkingNotification: '檢查消息通知連通性', + testFailedHint: '請檢查配置是否正確,修改後可以重新測試', basic: { + title: '基礎設定', + description: '設定存取網域、用戶名密碼和網路配置', appDomain: '存取網域', appDomainHint: '用於發送通知時,新增快速跳轉位址', wallpaper: '背景桌布', @@ -2893,6 +2883,8 @@ export default { apiTokenHint: '系統自動產生的 API 存取權杖', }, storage: { + title: '儲存配置', + description: '設定下載目錄和媒體庫目錄', info: '儲存設定說明', infoDesc: '設定本機儲存目錄,用於下載和媒體庫管理', downloadPath: '下載目錄', @@ -2901,6 +2893,8 @@ export default { libraryPathHint: '設定媒體檔案的儲存路徑', }, downloader: { + title: '下載器配置', + description: '設定下載器(可選)', info: '下載器設定說明', infoDesc: '設定下載器用於自動下載資源(可選)', type: '下載器類型', @@ -2915,6 +2909,8 @@ export default { downloadPath: '下載路徑', }, mediaServer: { + title: '媒體伺服器', + description: '設定媒體伺服器(可選)', info: '媒體伺服器設定說明', infoDesc: '設定媒體伺服器用於媒體庫管理(可選)', type: '媒體伺服器類型', @@ -2929,6 +2925,8 @@ export default { token: '存取權杖', }, notification: { + title: '通知設定', + description: '設定通知管道(可選)', info: '通知設定說明', infoDesc: '設定通知管道用於接收系統訊息(可選)', type: '通知類型', @@ -2946,6 +2944,8 @@ export default { receiverEmail: '接收信箱', }, preferences: { + title: '資源偏好', + description: '設定資源下載偏好', info: '資源偏好說明', infoDesc: '設定資源下載的偏好,系統將根據這些偏好自動選擇最佳資源', quality: '品質偏好', diff --git a/src/pages/login.vue b/src/pages/login.vue index 5e2d2872..903d99b6 100644 --- a/src/pages/login.vue +++ b/src/pages/login.vue @@ -117,12 +117,17 @@ async function subscribeForPushNotifications() { // 登录后处理 async function afterLogin(superuser: boolean, userPayload: userState, filteredMenus: any[]) { - // 如果有原始路径,优先跳转到原始路径 - if (authStore.originalPath && authStore.originalPath !== '/') { - router.push(authStore.originalPath) + // 如果需要显示设置向导,跳转到设置向导页面 + if (userPayload.wizard) { + router.push('/setup-wizard') } else { - // 跳转到第一个有权限的菜单 - router.push(filteredMenus[0].to) + // 如果有原始路径,优先跳转到原始路径 + if (authStore.originalPath && authStore.originalPath !== '/') { + router.push(authStore.originalPath) + } else { + // 跳转到第一个有权限的菜单 + router.push(filteredMenus[0].to) + } } // 订阅推送通知 @@ -165,6 +170,7 @@ function login() { avatar: response.avatar, level: response.level, permissions: response.permissions, + wizard: response.wizard, } // 在保存用户信息之前检查权限 diff --git a/src/pages/setup.vue b/src/pages/setup.vue index 16de31eb..e951b738 100644 --- a/src/pages/setup.vue +++ b/src/pages/setup.vue @@ -66,28 +66,37 @@ const wizardData = ref({ // 步骤标题 const stepTitles = [ - t('setupWizard.step1.title'), - t('setupWizard.step2.title'), - t('setupWizard.step3.title'), - t('setupWizard.step4.title'), - t('setupWizard.step5.title'), - t('setupWizard.step6.title'), + t('setupWizard.basic.title'), + t('setupWizard.storage.title'), + t('setupWizard.downloader.title'), + t('setupWizard.mediaServer.title'), + t('setupWizard.notification.title'), + t('setupWizard.preferences.title'), ] // 步骤描述 const stepDescriptions = [ - t('setupWizard.step1.description'), - t('setupWizard.step2.description'), - t('setupWizard.step3.description'), - t('setupWizard.step4.description'), - t('setupWizard.step5.description'), - t('setupWizard.step6.description'), + t('setupWizard.basic.description'), + t('setupWizard.storage.description'), + t('setupWizard.downloader.description'), + t('setupWizard.mediaServer.description'), + t('setupWizard.notification.description'), + t('setupWizard.preferences.description'), ] // 密码可见性控制 const isPasswordVisible = ref(false) const isConfirmPasswordVisible = ref(false) +// 连通性测试状态 +const connectivityTest = ref({ + isTesting: false, + testMessage: '', + testProgress: 0, + testResult: null as 'success' | 'error' | null, + showResult: false, +}) + // 整理方式选项 const transferTypeItems = [ { title: '硬链接', value: 'link' }, @@ -192,12 +201,201 @@ function selectPreset(preset: string) { } } +// 连通性测试函数 +async function testConnectivity(step: number) { + connectivityTest.value.isTesting = true + connectivityTest.value.testMessage = '' + connectivityTest.value.testProgress = 0 + connectivityTest.value.testResult = null + connectivityTest.value.showResult = false + + try { + let testResult: { success: boolean; message: string | null } = { success: false, message: null } + + switch (step) { + case 2: // 存储目录测试 + testResult = await testStorageConnectivity() + break + case 3: // 下载器测试 + testResult = await testDownloaderConnectivity() + break + case 4: // 媒体服务器测试 + testResult = await testMediaServerConnectivity() + break + case 5: // 消息通知测试 + testResult = await testNotificationConnectivity() + break + } + + // 设置测试结果 + connectivityTest.value.isTesting = false + connectivityTest.value.testResult = testResult.success ? 'success' : 'error' + connectivityTest.value.showResult = true + + // 根据结果显示不同的消息 + if (testResult.success) { + connectivityTest.value.testMessage = t('setupWizard.connectivityTestSuccess') + } else { + // 显示API返回的具体错误原因 + connectivityTest.value.testMessage = testResult.message || t('setupWizard.connectivityTestFailed') + } + + // 成功时2秒后隐藏结果,失败时保持显示直到用户操作 + if (testResult.success) { + setTimeout(() => { + connectivityTest.value.showResult = false + connectivityTest.value.testResult = null + }, 2000) + } + + return testResult.success + } catch (error) { + console.error('Connectivity test failed:', error) + connectivityTest.value.isTesting = false + connectivityTest.value.testResult = 'error' + connectivityTest.value.showResult = true + connectivityTest.value.testMessage = (error as Error).message || t('setupWizard.connectivityTestFailed') + return false + } +} + +// 存储目录连通性测试 +async function testStorageConnectivity() { + try { + connectivityTest.value.testProgress = 30 + connectivityTest.value.testMessage = t('setupWizard.testingStorage') + + // 等待设置生效 + await new Promise(resolve => setTimeout(resolve, 2000)) + + connectivityTest.value.testProgress = 60 + connectivityTest.value.testMessage = t('setupWizard.checkingStorage') + + // 调用存储测试API + const result = await api.get('system/storagetest') + connectivityTest.value.testProgress = 100 + + if (result.data?.success) { + return { success: true, message: null } + } else { + return { success: false, message: result.data?.message || t('setupWizard.storageTestFailed') } + } + } catch (error) { + console.error('Storage test failed:', error) + return { success: false, message: (error as Error).message || t('setupWizard.storageTestFailed') } + } +} + +// 下载器连通性测试 +async function testDownloaderConnectivity() { + try { + connectivityTest.value.testProgress = 30 + connectivityTest.value.testMessage = t('setupWizard.testingDownloader') + + // 等待设置生效 + await new Promise(resolve => setTimeout(resolve, 2000)) + + connectivityTest.value.testProgress = 60 + connectivityTest.value.testMessage = t('setupWizard.checkingDownloader') + + // 调用下载器测试API + const moduleid = wizardData.value.downloader.type + if (!moduleid) { + return { success: false, message: t('setupWizard.downloaderNotSelected') } + } + + const result: { [key: string]: any } = await api.get(`system/moduletest/${moduleid}`) + connectivityTest.value.testProgress = 100 + + if (result.data?.success) { + return { success: true, message: null } + } else { + return { success: false, message: result.data?.message || t('setupWizard.downloaderTestFailed') } + } + } catch (error) { + console.error('Downloader test failed:', error) + return { success: false, message: (error as Error).message || t('setupWizard.downloaderTestFailed') } + } +} + +// 媒体服务器连通性测试 +async function testMediaServerConnectivity() { + try { + connectivityTest.value.testProgress = 30 + connectivityTest.value.testMessage = t('setupWizard.testingMediaServer') + + // 等待设置生效 + await new Promise(resolve => setTimeout(resolve, 2000)) + + connectivityTest.value.testProgress = 60 + connectivityTest.value.testMessage = t('setupWizard.checkingMediaServer') + + // 调用媒体服务器测试API + const moduleid = wizardData.value.mediaServer.type + if (!moduleid) { + return { success: false, message: t('setupWizard.mediaServerNotSelected') } + } + + const result: { [key: string]: any } = await api.get(`system/moduletest/${moduleid}`) + connectivityTest.value.testProgress = 100 + + if (result.data?.success) { + return { success: true, message: null } + } else { + return { success: false, message: result.data?.message || t('setupWizard.mediaServerTestFailed') } + } + } catch (error) { + console.error('Media server test failed:', error) + return { success: false, message: (error as Error).message || t('setupWizard.mediaServerTestFailed') } + } +} + +// 消息通知连通性测试 +async function testNotificationConnectivity() { + try { + connectivityTest.value.testProgress = 30 + connectivityTest.value.testMessage = t('setupWizard.testingNotification') + + // 等待设置生效 + await new Promise(resolve => setTimeout(resolve, 2000)) + + connectivityTest.value.testProgress = 60 + connectivityTest.value.testMessage = t('setupWizard.checkingNotification') + + // 调用通知测试API + const moduleid = wizardData.value.notification.type + if (!moduleid) { + return { success: false, message: t('setupWizard.notificationNotSelected') } + } + + const result: { [key: string]: any } = await api.get(`system/moduletest/${moduleid}`) + connectivityTest.value.testProgress = 100 + + if (result.data?.success) { + return { success: true, message: null } + } else { + return { success: false, message: result.data?.message || t('setupWizard.notificationTestFailed') } + } + } catch (error) { + console.error('Notification test failed:', error) + return { success: false, message: (error as Error).message || t('setupWizard.notificationTestFailed') } + } +} + // 下一步 async function nextStep() { if (currentStep.value < totalSteps) { // 保存当前步骤的设置 await saveCurrentStepSettings() + // 对于需要测试的步骤,进行连通性测试 + if ([2, 3, 4, 5].includes(currentStep.value)) { + const testResult = await testConnectivity(currentStep.value) + if (!testResult) { + return + } + } + currentStep.value++ } } @@ -1907,22 +2105,83 @@ onMounted(async () => { + + + + +
+ +
{{ connectivityTest.testMessage }}
+ +
{{ Math.round(connectivityTest.testProgress) }}%
+
+ + +
+ +
+ {{ connectivityTest.testMessage }} +
+
+ {{ t('setupWizard.testFailedHint') }} +
+
+
+
+
- + {{ t('common.previous') }} - + {{ t('common.skip') }}
- - {{ t('common.next') }} + + {{ connectivityTest.isTesting ? t('setupWizard.testing') : t('common.next') }} - + {{ t('setupWizard.complete') }}
@@ -1986,4 +2245,19 @@ onMounted(async () => { .v-card--variant-tonal.v-theme--dark { background-color: rgb(var(--v-theme-primary), 0.2); } + +/* 旋转动画 */ +.rotating { + animation: rotate 2s linear infinite; +} + +@keyframes rotate { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } +} diff --git a/src/stores/types.ts b/src/stores/types.ts index 5cccbe34..d3a8ebbd 100644 --- a/src/stores/types.ts +++ b/src/stores/types.ts @@ -20,6 +20,8 @@ export interface userState { level: number // 权限 permissions: { [key: string]: any } + // 是否需要显示设置向导 + wizard: boolean } export interface globalSettingsState { diff --git a/src/stores/user.ts b/src/stores/user.ts index 0f77ea98..7f748079 100644 --- a/src/stores/user.ts +++ b/src/stores/user.ts @@ -10,6 +10,7 @@ export const useUserStore = defineStore('user', { avatar: '', level: 1, permissions: DEFAULT_PERMISSIONS, + wizard: false, }), // 全局持久化 @@ -34,6 +35,9 @@ export const useUserStore = defineStore('user', { setPermissions(permissions: object) { this.permissions = { ...DEFAULT_PERMISSIONS, ...permissions } }, + setWizard(wizard: boolean) { + this.wizard = wizard + }, loginUser(payload: userState) { this.setSuperUser(payload.superUser) this.setUserID(payload.userID) @@ -41,6 +45,7 @@ export const useUserStore = defineStore('user', { this.setAvatar(payload.avatar) this.setLevel(payload.level) this.setPermissions(payload.permissions) + this.setWizard(payload.wizard) }, reset() { this.setSuperUser(false) @@ -49,6 +54,7 @@ export const useUserStore = defineStore('user', { this.setAvatar('') this.setLevel(1) this.setPermissions(DEFAULT_PERMISSIONS) + this.setWizard(false) }, }, @@ -59,5 +65,6 @@ export const useUserStore = defineStore('user', { getAvatar: state => state.avatar, getLevel: state => state.level, getPermissions: state => state.permissions, + getWizard: state => state.wizard, }, })