优化配置向导

This commit is contained in:
jxxghp
2025-09-10 16:56:06 +08:00
parent c8a65ecbe4
commit 4a2073a038
8 changed files with 69 additions and 69 deletions
-1
View File
@@ -10,7 +10,6 @@ const props = defineProps({
root: { root: {
type: String, type: String,
default: '/', default: '/',
required: true,
}, },
storage: { storage: {
type: String, type: String,
+33 -21
View File
@@ -232,12 +232,13 @@ export function useSetupWizard() {
if (wizardData.value.downloader.type === type) { if (wizardData.value.downloader.type === type) {
// 重复点击已选中的类型,取消选择 // 重复点击已选中的类型,取消选择
wizardData.value.downloader.type = '' wizardData.value.downloader.type = ''
wizardData.value.downloader.name = ''
wizardData.value.downloader.config = {}
} else { } else {
wizardData.value.downloader.type = type wizardData.value.downloader.type = type
wizardData.value.downloader.name = `${type} 下载器` // 如果名称为空或为默认名称,则设置默认名称
wizardData.value.downloader.config = {} if (!wizardData.value.downloader.name || wizardData.value.downloader.name.includes('下载器')) {
wizardData.value.downloader.name = `${type} 下载器`
}
// 不清空config,保留用户已输入的值
} }
} }
@@ -246,14 +247,13 @@ export function useSetupWizard() {
if (wizardData.value.mediaServer.type === type) { if (wizardData.value.mediaServer.type === type) {
// 重复点击已选中的类型,取消选择 // 重复点击已选中的类型,取消选择
wizardData.value.mediaServer.type = '' wizardData.value.mediaServer.type = ''
wizardData.value.mediaServer.name = ''
wizardData.value.mediaServer.config = {}
wizardData.value.mediaServer.sync_libraries = []
} else { } else {
wizardData.value.mediaServer.type = type wizardData.value.mediaServer.type = type
wizardData.value.mediaServer.name = `${type} 服务器` // 如果名称为空或为默认名称,则设置默认名称
wizardData.value.mediaServer.config = {} if (!wizardData.value.mediaServer.name || wizardData.value.mediaServer.name.includes('服务器')) {
wizardData.value.mediaServer.sync_libraries = [] wizardData.value.mediaServer.name = `${type} 服务器`
}
// 不清空config和sync_libraries,保留用户已输入的值
} }
} }
@@ -262,16 +262,14 @@ export function useSetupWizard() {
if (wizardData.value.notification.type === type) { if (wizardData.value.notification.type === type) {
// 重复点击已选中的类型,取消选择 // 重复点击已选中的类型,取消选择
wizardData.value.notification.type = '' wizardData.value.notification.type = ''
wizardData.value.notification.name = ''
wizardData.value.notification.enabled = false
wizardData.value.notification.config = {}
wizardData.value.notification.switchs = []
} else { } else {
wizardData.value.notification.type = type wizardData.value.notification.type = type
wizardData.value.notification.name = `${type} 通知` // 如果名称为空或为默认名称,则设置默认名称
if (!wizardData.value.notification.name || wizardData.value.notification.name.includes('通知')) {
wizardData.value.notification.name = `${type} 通知`
}
wizardData.value.notification.enabled = true wizardData.value.notification.enabled = true
wizardData.value.notification.config = {} // 不清空config和switchs,保留用户已输入的值
wizardData.value.notification.switchs = []
} }
} }
@@ -924,6 +922,7 @@ export function useSetupWizard() {
const directory = { const directory = {
name: '默认目录', name: '默认目录',
storage: 'local', storage: 'local',
library_storage: 'local',
download_path: wizardData.value.storage.downloadPath, download_path: wizardData.value.storage.downloadPath,
library_path: wizardData.value.storage.libraryPath, library_path: wizardData.value.storage.libraryPath,
priority: 0, priority: 0,
@@ -955,12 +954,15 @@ export function useSetupWizard() {
async function saveDownloaderSettings() { async function saveDownloaderSettings() {
if (wizardData.value.downloader.type) { if (wizardData.value.downloader.type) {
try { try {
// 只保存当前选中类型的配置
const config = { ...wizardData.value.downloader.config }
const downloader = { const downloader = {
name: wizardData.value.downloader.name, name: wizardData.value.downloader.name,
type: wizardData.value.downloader.type, type: wizardData.value.downloader.type,
default: true, default: true,
enabled: true, enabled: true,
config: wizardData.value.downloader.config, config: config,
} }
const response: { [key: string]: any } = await api.post('system/setting/Downloaders', [downloader]) const response: { [key: string]: any } = await api.post('system/setting/Downloaders', [downloader])
@@ -981,11 +983,16 @@ export function useSetupWizard() {
async function saveMediaServerSettings() { async function saveMediaServerSettings() {
if (wizardData.value.mediaServer.type) { if (wizardData.value.mediaServer.type) {
try { try {
// 只保存当前选中类型的配置
const config = { ...wizardData.value.mediaServer.config }
const sync_libraries = [...(wizardData.value.mediaServer.sync_libraries || [])]
const mediaServer = { const mediaServer = {
name: wizardData.value.mediaServer.name, name: wizardData.value.mediaServer.name,
type: wizardData.value.mediaServer.type, type: wizardData.value.mediaServer.type,
enabled: true, enabled: true,
config: wizardData.value.mediaServer.config, config: config,
sync_libraries: sync_libraries,
} }
const response: { [key: string]: any } = await api.post('system/setting/MediaServers', [mediaServer]) const response: { [key: string]: any } = await api.post('system/setting/MediaServers', [mediaServer])
@@ -1006,11 +1013,16 @@ export function useSetupWizard() {
async function saveNotificationSettings() { async function saveNotificationSettings() {
if (wizardData.value.notification.type) { if (wizardData.value.notification.type) {
try { try {
// 只保存当前选中类型的配置
const config = { ...wizardData.value.notification.config }
const switchs = [...(wizardData.value.notification.switchs || [])]
const notification = { const notification = {
name: wizardData.value.notification.name, name: wizardData.value.notification.name,
type: wizardData.value.notification.type, type: wizardData.value.notification.type,
enabled: true, enabled: wizardData.value.notification.enabled,
config: wizardData.value.notification.config, config: config,
switchs: switchs,
} }
const response: { [key: string]: any } = await api.post('system/setting/Notifications', [notification]) const response: { [key: string]: any } = await api.post('system/setting/Notifications', [notification])
+1 -1
View File
@@ -526,7 +526,7 @@ onUnmounted(() => {
<template #prepend> <template #prepend>
<VIcon :icon="isAdvancedMode ? 'mdi-cog-outline' : 'mdi-wizard-hat'" /> <VIcon :icon="isAdvancedMode ? 'mdi-cog-outline' : 'mdi-wizard-hat'" />
</template> </template>
<VListItemTitle>{{ t('user.systemSettings') }}</VListItemTitle> <VListItemTitle>{{ isAdvancedMode ? t('user.systemSettings') : t('user.wizardSettings') }}</VListItemTitle>
</VListItem> </VListItem>
<!-- 👉 Site Auth --> <!-- 👉 Site Auth -->
+4 -3
View File
@@ -372,6 +372,7 @@ export default {
deleteFailed: 'Failed to delete user!', deleteFailed: 'Failed to delete user!',
profile: 'Profile', profile: 'Profile',
systemSettings: 'System Settings', systemSettings: 'System Settings',
wizardSettings: 'Setup Wizard',
siteAuth: 'User Authentication', siteAuth: 'User Authentication',
helpDocs: 'Help Documents', helpDocs: 'Help Documents',
restart: 'Restart', restart: 'Restart',
@@ -2948,7 +2949,7 @@ export default {
apiTokenRequired: 'API Token is required', apiTokenRequired: 'API Token is required',
}, },
storage: { storage: {
title: 'Storage Configuration', title: 'Storage',
description: 'Configure download directory and media library directory', description: 'Configure download directory and media library directory',
info: 'Storage Configuration', info: 'Storage Configuration',
infoDesc: 'Configure local storage directories for download and media library management', infoDesc: 'Configure local storage directories for download and media library management',
@@ -2960,7 +2961,7 @@ export default {
libraryPathRequired: 'Media library directory is required', libraryPathRequired: 'Media library directory is required',
}, },
downloader: { downloader: {
title: 'Downloader Configuration', title: 'Downloader',
description: 'Configure downloader', description: 'Configure downloader',
info: 'Downloader Configuration', info: 'Downloader Configuration',
infoDesc: 'Configure downloader for resource download, can choose qBittorrent or Transmission', infoDesc: 'Configure downloader for resource download, can choose qBittorrent or Transmission',
@@ -2992,7 +2993,7 @@ export default {
token: 'Access Token', token: 'Access Token',
}, },
notification: { notification: {
title: 'Notification Settings', title: 'Notification',
description: 'Configure notification channels', description: 'Configure notification channels',
info: 'Notification Configuration', info: 'Notification Configuration',
infoDesc: 'Configure notification channels for receiving system messages (optional)', infoDesc: 'Configure notification channels for receiving system messages (optional)',
+4 -7
View File
@@ -370,6 +370,7 @@ export default {
deleteFailed: '用户删除失败!', deleteFailed: '用户删除失败!',
profile: '个人信息', profile: '个人信息',
systemSettings: '系统设定', systemSettings: '系统设定',
wizardSettings: '设置向导',
siteAuth: '用户认证', siteAuth: '用户认证',
helpDocs: '帮助文档', helpDocs: '帮助文档',
restart: '重启', restart: '重启',
@@ -1791,11 +1792,7 @@ export default {
webPush: 'WebPush', webPush: 'WebPush',
creatingUser: '正在创建【{name}】用户,请稍后', creatingUser: '正在创建【{name}】用户,请稍后',
updatingUser: '正在更新【{name}】用户,请稍后', updatingUser: '正在更新【{name}】用户,请稍后',
usernameRequired: '用户名不能为空',
usernameExists: '用户名已存在', usernameExists: '用户名已存在',
passwordMinLength: '密码长度不能少于6位字符',
confirmPasswordRequired: '请确认密码',
passwordMismatch: '两次输入的密码不一致',
userCreated: '用户【{name}】创建成功', userCreated: '用户【{name}】创建成功',
userCreateFailed: '创建用户失败:{message}', userCreateFailed: '创建用户失败:{message}',
userUpdateSuccess: '用户【{name}】更新成功', userUpdateSuccess: '用户【{name}】更新成功',
@@ -2939,7 +2936,7 @@ export default {
apiTokenRequired: 'API Token不能为空', apiTokenRequired: 'API Token不能为空',
}, },
storage: { storage: {
title: '存储配置', title: '存储',
description: '配置下载目录和媒体库目录', description: '配置下载目录和媒体库目录',
info: '存储配置说明', info: '存储配置说明',
infoDesc: '配置本地存储目录,用于下载和媒体库管理', infoDesc: '配置本地存储目录,用于下载和媒体库管理',
@@ -2951,7 +2948,7 @@ export default {
libraryPathRequired: '媒体库目录不能为空', libraryPathRequired: '媒体库目录不能为空',
}, },
downloader: { downloader: {
title: '下载器配置', title: '下载器',
description: '配置下载器', description: '配置下载器',
info: '下载器配置说明', info: '下载器配置说明',
infoDesc: '配置下载器用于下载资源,可选择qBittorrent或Transmission', infoDesc: '配置下载器用于下载资源,可选择qBittorrent或Transmission',
@@ -2983,7 +2980,7 @@ export default {
token: '访问令牌', token: '访问令牌',
}, },
notification: { notification: {
title: '通知设置', title: '通知',
description: '配置通知渠道', description: '配置通知渠道',
info: '通知配置说明', info: '通知配置说明',
infoDesc: '配置通知渠道用于接收系统消息(可选)', infoDesc: '配置通知渠道用于接收系统消息(可选)',
+4 -3
View File
@@ -371,6 +371,7 @@ export default {
deleteFailed: '用戶刪除失敗!', deleteFailed: '用戶刪除失敗!',
profile: '個人信息', profile: '個人信息',
systemSettings: '系統設定', systemSettings: '系統設定',
wizardSettings: '設定向導',
siteAuth: '用戶認證', siteAuth: '用戶認證',
helpDocs: '幫助文檔', helpDocs: '幫助文檔',
restart: '重啟', restart: '重啟',
@@ -2903,7 +2904,7 @@ export default {
apiTokenRequired: 'API Token 不能為空', apiTokenRequired: 'API Token 不能為空',
}, },
storage: { storage: {
title: '儲存配置', title: '儲存',
description: '設定下載目錄和媒體庫目錄', description: '設定下載目錄和媒體庫目錄',
info: '儲存設定說明', info: '儲存設定說明',
infoDesc: '設定本機儲存目錄,用於下載和媒體庫管理', infoDesc: '設定本機儲存目錄,用於下載和媒體庫管理',
@@ -2915,7 +2916,7 @@ export default {
libraryPathRequired: '媒體庫目錄不能為空', libraryPathRequired: '媒體庫目錄不能為空',
}, },
downloader: { downloader: {
title: '下載器配置', title: '下載器',
description: '設定下載器', description: '設定下載器',
info: '下載器設定說明', info: '下載器設定說明',
infoDesc: '設定下載器用於下載資源,可選擇qBittorrent或Transmission', infoDesc: '設定下載器用於下載資源,可選擇qBittorrent或Transmission',
@@ -2947,7 +2948,7 @@ export default {
token: '存取權杖', token: '存取權杖',
}, },
notification: { notification: {
title: '通知設定', title: '通知',
description: '設定通知管道', description: '設定通知管道',
info: '通知設定說明', info: '通知設定說明',
infoDesc: '設定通知管道用於接收系統訊息(可選)', infoDesc: '設定通知管道用於接收系統訊息(可選)',
+19 -29
View File
@@ -10,10 +10,14 @@ import MediaServerSettingsStep from '@/views/setup/MediaServerSettingsStep.vue'
import NotificationSettingsStep from '@/views/setup/NotificationSettingsStep.vue' import NotificationSettingsStep from '@/views/setup/NotificationSettingsStep.vue'
import PreferencesSettingsStep from '@/views/setup/PreferencesSettingsStep.vue' import PreferencesSettingsStep from '@/views/setup/PreferencesSettingsStep.vue'
import ConnectivityTest from '@/views/setup/ConnectivityTest.vue' import ConnectivityTest from '@/views/setup/ConnectivityTest.vue'
import { useDisplay } from 'vuetify'
const { t } = useI18n() const { t } = useI18n()
const router = useRouter() const router = useRouter()
// 显示器宽度
const display = useDisplay()
const { currentStep, totalSteps, stepTitles, connectivityTest, nextStep, prevStep, completeWizard, initialize } = const { currentStep, totalSteps, stepTitles, connectivityTest, nextStep, prevStep, completeWizard, initialize } =
useSetupWizard() useSetupWizard()
@@ -28,20 +32,27 @@ onMounted(async () => {
<!-- 全屏头部 --> <!-- 全屏头部 -->
<div class="setup-wizard-header"> <div class="setup-wizard-header">
<div class="d-flex align-center justify-space-between"> <div class="d-flex align-center justify-space-between">
<div class="d-flex align-center text-center mx-auto"> <!-- 左侧占位 -->
<div style="inline-size: 40px"></div>
<!-- 中间标题 -->
<div class="d-flex align-center text-center">
<div> <div>
<h1 class="text-h3 font-weight-bold text-moviepilot mb-3">{{ t('setupWizard.title') }}</h1> <h1 class="text-h3 font-weight-bold text-moviepilot mb-3">{{ t('setupWizard.title') }}</h1>
<p class="text-body-1 text-medium-emphasis">{{ t('setupWizard.subtitle') }}</p> <p class="text-body-1 text-medium-emphasis">{{ t('setupWizard.subtitle') }}</p>
</div> </div>
</div> </div>
<!-- 右侧关闭按钮 -->
<VBtn variant="text" icon="mdi-close" @click="router.push('/')" size="small" />
</div> </div>
</div> </div>
<!-- 向导内容 --> <!-- 向导内容 -->
<div class="setup-wizard-content"> <VCard max-width="800px" class="mx-auto my-7">
<div class="setup-wizard"> <VCardText>
<!-- 使用 VStepper 组件 --> <!-- 使用 VStepper 组件 -->
<VStepper v-model="currentStep" class="elevation-0" flat> <VStepper v-model="currentStep" class="elevation-0" flat alt-labels :mobile="display.smAndDown.value">
<!-- 步骤标题 --> <!-- 步骤标题 -->
<VStepperHeader class="elevation-0"> <VStepperHeader class="elevation-0">
<template v-for="(step, index) in stepTitles" :key="index"> <template v-for="(step, index) in stepTitles" :key="index">
@@ -49,6 +60,7 @@ onMounted(async () => {
:value="index + 1" :value="index + 1"
:complete="currentStep > index + 1" :complete="currentStep > index + 1"
:color="currentStep >= index + 1 ? 'primary' : 'default'" :color="currentStep >= index + 1 ? 'primary' : 'default'"
complete-icon="mdi-check-circle"
> >
<template #title> <template #title>
<span class="text-caption">{{ step }}</span> <span class="text-caption">{{ step }}</span>
@@ -105,15 +117,6 @@ onMounted(async () => {
> >
{{ t('common.previous') }} {{ t('common.previous') }}
</VBtn> </VBtn>
<VBtn
v-else
color="primary"
prepend-icon="mdi-keyboard-return"
@click="router.push('/')"
:disabled="connectivityTest.isTesting"
>
{{ t('common.skip') }}
</VBtn>
</div> </div>
<div class="d-flex gap-2"> <div class="d-flex gap-2">
@@ -138,16 +141,15 @@ onMounted(async () => {
</div> </div>
</VCardActions> </VCardActions>
</VStepper> </VStepper>
</div> </VCardText>
</div> </VCard>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.setup-wizard-fullscreen { .setup-wizard-fullscreen {
position: fixed; position: fixed;
z-index: 9999; background-color: rgb(var(--v-theme-background));
background-color: rgb(var(--v-theme-surface));
inset: 0; inset: 0;
overflow-y: auto; overflow-y: auto;
} }
@@ -161,16 +163,4 @@ onMounted(async () => {
padding-block: 16px; padding-block: 16px;
padding-inline: 24px; padding-inline: 24px;
} }
.setup-wizard-content {
padding: 24px;
min-block-size: calc(100vh - 80px);
}
.setup-wizard {
padding: 20px;
margin-block: 0;
margin-inline: auto;
max-inline-size: 800px;
}
</style> </style>
+4 -4
View File
@@ -41,7 +41,7 @@ const overwriteModeItems = [
</VAlert> </VAlert>
</VCol> </VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VTextField <VPathField
v-model="wizardData.storage.downloadPath" v-model="wizardData.storage.downloadPath"
:label="t('setupWizard.storage.downloadPath')" :label="t('setupWizard.storage.downloadPath')"
:hint="t('setupWizard.storage.downloadPathHint')" :hint="t('setupWizard.storage.downloadPathHint')"
@@ -55,7 +55,7 @@ const overwriteModeItems = [
/> />
</VCol> </VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VTextField <VPathField
v-model="wizardData.storage.libraryPath" v-model="wizardData.storage.libraryPath"
:label="t('setupWizard.storage.libraryPath')" :label="t('setupWizard.storage.libraryPath')"
:hint="t('setupWizard.storage.libraryPathHint')" :hint="t('setupWizard.storage.libraryPathHint')"
@@ -69,7 +69,7 @@ const overwriteModeItems = [
/> />
</VCol> </VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VAutocomplete <VSelect
v-model="wizardData.storage.transferType" v-model="wizardData.storage.transferType"
:label="t('directory.transferType')" :label="t('directory.transferType')"
:hint="t('directory.transferTypeHint')" :hint="t('directory.transferTypeHint')"
@@ -79,7 +79,7 @@ const overwriteModeItems = [
/> />
</VCol> </VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VAutocomplete <VSelect
v-model="wizardData.storage.overwriteMode" v-model="wizardData.storage.overwriteMode"
:label="t('directory.overwriteMode')" :label="t('directory.overwriteMode')"
:hint="t('directory.overwriteModeHint')" :hint="t('directory.overwriteModeHint')"