mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 23:56:42 +08:00
feat: add site auth step to setup wizard
This commit is contained in:
@@ -18,6 +18,11 @@ export interface WizardData {
|
|||||||
proxyHost: string
|
proxyHost: string
|
||||||
githubToken: string
|
githubToken: string
|
||||||
}
|
}
|
||||||
|
siteAuth: {
|
||||||
|
auxiliaryAuthEnable: boolean
|
||||||
|
site: string
|
||||||
|
params: Record<string, string | number>
|
||||||
|
}
|
||||||
storage: {
|
storage: {
|
||||||
downloadPath: string
|
downloadPath: string
|
||||||
libraryPath: string
|
libraryPath: string
|
||||||
@@ -85,6 +90,10 @@ export interface ConnectivityTestState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ValidationErrorState {
|
export interface ValidationErrorState {
|
||||||
|
siteAuth: {
|
||||||
|
site: boolean
|
||||||
|
[key: string]: boolean
|
||||||
|
}
|
||||||
downloader: {
|
downloader: {
|
||||||
name: boolean
|
name: boolean
|
||||||
host: boolean
|
host: boolean
|
||||||
@@ -114,7 +123,7 @@ export interface ValidationErrorState {
|
|||||||
|
|
||||||
// 全局状态,所有组件共享
|
// 全局状态,所有组件共享
|
||||||
const currentStep = ref(1)
|
const currentStep = ref(1)
|
||||||
const totalSteps = 7
|
const totalSteps = 8
|
||||||
|
|
||||||
// 加载状态
|
// 加载状态
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
@@ -122,6 +131,22 @@ const isLoading = ref(false)
|
|||||||
// 选中的预设规则
|
// 选中的预设规则
|
||||||
const selectedPreset = ref('')
|
const selectedPreset = ref('')
|
||||||
|
|
||||||
|
// 可认证站点列表
|
||||||
|
const authSites = ref<{
|
||||||
|
[key: string]: {
|
||||||
|
name: string
|
||||||
|
icon: string
|
||||||
|
params: {
|
||||||
|
[key: string]: {
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
placeholder?: string
|
||||||
|
tooltip?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}>({})
|
||||||
|
|
||||||
// 向导数据
|
// 向导数据
|
||||||
const wizardData = ref<WizardData>({
|
const wizardData = ref<WizardData>({
|
||||||
basic: {
|
basic: {
|
||||||
@@ -135,6 +160,11 @@ const wizardData = ref<WizardData>({
|
|||||||
proxyHost: '',
|
proxyHost: '',
|
||||||
githubToken: '',
|
githubToken: '',
|
||||||
},
|
},
|
||||||
|
siteAuth: {
|
||||||
|
auxiliaryAuthEnable: false,
|
||||||
|
site: '',
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
storage: {
|
storage: {
|
||||||
downloadPath: '',
|
downloadPath: '',
|
||||||
libraryPath: '',
|
libraryPath: '',
|
||||||
@@ -194,6 +224,9 @@ const connectivityTest = ref<ConnectivityTestState>({
|
|||||||
|
|
||||||
// 验证错误状态
|
// 验证错误状态
|
||||||
const validationErrors = ref<ValidationErrorState>({
|
const validationErrors = ref<ValidationErrorState>({
|
||||||
|
siteAuth: {
|
||||||
|
site: false,
|
||||||
|
},
|
||||||
downloader: {
|
downloader: {
|
||||||
name: false,
|
name: false,
|
||||||
host: false,
|
host: false,
|
||||||
@@ -256,6 +289,7 @@ export function useSetupWizard() {
|
|||||||
// 步骤标题
|
// 步骤标题
|
||||||
const stepTitles = computed(() => [
|
const stepTitles = computed(() => [
|
||||||
t('setupWizard.basic.title'),
|
t('setupWizard.basic.title'),
|
||||||
|
t('setupWizard.siteAuth.title'),
|
||||||
t('setupWizard.storage.title'),
|
t('setupWizard.storage.title'),
|
||||||
t('setupWizard.downloader.title'),
|
t('setupWizard.downloader.title'),
|
||||||
t('setupWizard.mediaServer.title'),
|
t('setupWizard.mediaServer.title'),
|
||||||
@@ -267,6 +301,7 @@ export function useSetupWizard() {
|
|||||||
// 步骤描述
|
// 步骤描述
|
||||||
const stepDescriptions = computed(() => [
|
const stepDescriptions = computed(() => [
|
||||||
t('setupWizard.basic.description'),
|
t('setupWizard.basic.description'),
|
||||||
|
t('setupWizard.siteAuth.description'),
|
||||||
t('setupWizard.storage.description'),
|
t('setupWizard.storage.description'),
|
||||||
t('setupWizard.downloader.description'),
|
t('setupWizard.downloader.description'),
|
||||||
t('setupWizard.mediaServer.description'),
|
t('setupWizard.mediaServer.description'),
|
||||||
@@ -378,6 +413,9 @@ export function useSetupWizard() {
|
|||||||
|
|
||||||
// 清除验证错误状态
|
// 清除验证错误状态
|
||||||
function clearValidationErrors() {
|
function clearValidationErrors() {
|
||||||
|
validationErrors.value.siteAuth = {
|
||||||
|
site: false,
|
||||||
|
}
|
||||||
validationErrors.value.downloader = {
|
validationErrors.value.downloader = {
|
||||||
name: false,
|
name: false,
|
||||||
host: false,
|
host: false,
|
||||||
@@ -404,6 +442,47 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证用户站点认证字段
|
||||||
|
function validateSiteAuthFields(): { isValid: boolean; errors: string[] } {
|
||||||
|
const errors: string[] = []
|
||||||
|
clearValidationErrors()
|
||||||
|
|
||||||
|
if (!wizardData.value.siteAuth.site) {
|
||||||
|
return {
|
||||||
|
isValid: true,
|
||||||
|
errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedSite = authSites.value[wizardData.value.siteAuth.site]
|
||||||
|
if (!selectedSite) {
|
||||||
|
errors.push(t('setupWizard.siteAuth.siteConfigNotExist'))
|
||||||
|
validationErrors.value.siteAuth.site = true
|
||||||
|
return {
|
||||||
|
isValid: false,
|
||||||
|
errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields = Object.keys(selectedSite.params || {}).filter(key => {
|
||||||
|
return selectedSite.params[key]?.name && selectedSite.params[key]?.type
|
||||||
|
})
|
||||||
|
|
||||||
|
fields.forEach(key => {
|
||||||
|
const fieldKey = `${wizardData.value.siteAuth.site.toUpperCase()}_${key.toUpperCase()}`
|
||||||
|
const value = wizardData.value.siteAuth.params[fieldKey]
|
||||||
|
if (value === undefined || value === null || value === '') {
|
||||||
|
errors.push(t('setupWizard.siteAuth.fieldRequired', { name: selectedSite.params[key].name }))
|
||||||
|
validationErrors.value.siteAuth[fieldKey] = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
isValid: errors.length === 0,
|
||||||
|
errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 验证下载器字段
|
// 验证下载器字段
|
||||||
function validateDownloaderFields(): { isValid: boolean; errors: string[] } {
|
function validateDownloaderFields(): { isValid: boolean; errors: string[] } {
|
||||||
const errors: string[] = []
|
const errors: string[] = []
|
||||||
@@ -645,6 +724,13 @@ export function useSetupWizard() {
|
|||||||
break
|
break
|
||||||
|
|
||||||
case 2: // 存储设置
|
case 2: // 存储设置
|
||||||
|
if (wizardData.value.siteAuth.site) {
|
||||||
|
const validation = validateSiteAuthFields()
|
||||||
|
errors.push(...validation.errors)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 3: // 存储设置
|
||||||
if (!wizardData.value.storage.downloadPath) {
|
if (!wizardData.value.storage.downloadPath) {
|
||||||
errors.push(t('setupWizard.storage.downloadPathRequired'))
|
errors.push(t('setupWizard.storage.downloadPathRequired'))
|
||||||
}
|
}
|
||||||
@@ -653,7 +739,7 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case 3: // 下载器设置
|
case 4: // 下载器设置
|
||||||
if (wizardData.value.downloader.type) {
|
if (wizardData.value.downloader.type) {
|
||||||
// 如果选择了下载器,则验证必输项
|
// 如果选择了下载器,则验证必输项
|
||||||
const validation = validateDownloaderFields()
|
const validation = validateDownloaderFields()
|
||||||
@@ -661,7 +747,7 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case 4: // 媒体服务器设置
|
case 5: // 媒体服务器设置
|
||||||
if (wizardData.value.mediaServer.type) {
|
if (wizardData.value.mediaServer.type) {
|
||||||
// 如果选择了媒体服务器,则验证必输项
|
// 如果选择了媒体服务器,则验证必输项
|
||||||
const validation = validateMediaServerFields()
|
const validation = validateMediaServerFields()
|
||||||
@@ -669,7 +755,7 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case 5: // 通知设置
|
case 6: // 通知设置
|
||||||
if (wizardData.value.notification.type) {
|
if (wizardData.value.notification.type) {
|
||||||
// 如果选择了通知,则验证必输项
|
// 如果选择了通知,则验证必输项
|
||||||
const validation = validateNotificationFields()
|
const validation = validateNotificationFields()
|
||||||
@@ -677,14 +763,14 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case 6: // 智能助手设置
|
case 7: // 智能助手设置
|
||||||
if (wizardData.value.agent.enabled) {
|
if (wizardData.value.agent.enabled) {
|
||||||
const validation = validateAgentFields()
|
const validation = validateAgentFields()
|
||||||
errors.push(...validation.errors)
|
errors.push(...validation.errors)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case 7: // 偏好设置
|
case 8: // 偏好设置
|
||||||
// 偏好设置有默认值,不需要验证
|
// 偏好设置有默认值,不需要验证
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -699,12 +785,14 @@ export function useSetupWizard() {
|
|||||||
function shouldPerformTest(step: number): boolean {
|
function shouldPerformTest(step: number): boolean {
|
||||||
switch (step) {
|
switch (step) {
|
||||||
case 2: // 存储目录测试 - 总是需要测试
|
case 2: // 存储目录测试 - 总是需要测试
|
||||||
|
return false
|
||||||
|
case 3: // 存储目录测试 - 总是需要测试
|
||||||
return true
|
return true
|
||||||
case 3: // 下载器测试 - 只有选择了下载器才测试
|
case 4: // 下载器测试 - 只有选择了下载器才测试
|
||||||
return !!wizardData.value.downloader.type
|
return !!wizardData.value.downloader.type
|
||||||
case 4: // 媒体服务器测试 - 只有选择了媒体服务器才测试
|
case 5: // 媒体服务器测试 - 只有选择了媒体服务器才测试
|
||||||
return !!wizardData.value.mediaServer.type
|
return !!wizardData.value.mediaServer.type
|
||||||
case 5: // 消息通知测试 - 只有选择了通知才测试
|
case 6: // 消息通知测试 - 只有选择了通知才测试
|
||||||
return !!wizardData.value.notification.type
|
return !!wizardData.value.notification.type
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
@@ -724,15 +812,17 @@ export function useSetupWizard() {
|
|||||||
|
|
||||||
switch (step) {
|
switch (step) {
|
||||||
case 2: // 存储目录测试
|
case 2: // 存储目录测试
|
||||||
|
break
|
||||||
|
case 3: // 存储目录测试
|
||||||
testResult = await testStorageConnectivity()
|
testResult = await testStorageConnectivity()
|
||||||
break
|
break
|
||||||
case 3: // 下载器测试
|
case 4: // 下载器测试
|
||||||
testResult = await testDownloaderConnectivity()
|
testResult = await testDownloaderConnectivity()
|
||||||
break
|
break
|
||||||
case 4: // 媒体服务器测试
|
case 5: // 媒体服务器测试
|
||||||
testResult = await testMediaServerConnectivity()
|
testResult = await testMediaServerConnectivity()
|
||||||
break
|
break
|
||||||
case 5: // 消息通知测试
|
case 6: // 消息通知测试
|
||||||
testResult = await testNotificationConnectivity()
|
testResult = await testNotificationConnectivity()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -957,16 +1047,18 @@ export function useSetupWizard() {
|
|||||||
case 1:
|
case 1:
|
||||||
return await saveBasicSettings()
|
return await saveBasicSettings()
|
||||||
case 2:
|
case 2:
|
||||||
return await saveStorageSettings()
|
return await saveSiteAuthSettings()
|
||||||
case 3:
|
case 3:
|
||||||
return await saveDownloaderSettings()
|
return await saveStorageSettings()
|
||||||
case 4:
|
case 4:
|
||||||
return await saveMediaServerSettings()
|
return await saveDownloaderSettings()
|
||||||
case 5:
|
case 5:
|
||||||
return await saveNotificationSettings()
|
return await saveMediaServerSettings()
|
||||||
case 6:
|
case 6:
|
||||||
return await saveAgentSettings()
|
return await saveNotificationSettings()
|
||||||
case 7:
|
case 7:
|
||||||
|
return await saveAgentSettings()
|
||||||
|
case 8:
|
||||||
return await savePreferenceSettings()
|
return await savePreferenceSettings()
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -1109,6 +1201,39 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存用户站点认证设置
|
||||||
|
async function saveSiteAuthSettings() {
|
||||||
|
try {
|
||||||
|
const envResponse: { [key: string]: any } = await api.post('system/env', {
|
||||||
|
AUXILIARY_AUTH_ENABLE: wizardData.value.siteAuth.auxiliaryAuthEnable,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!envResponse.success) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wizardData.value.siteAuth.site) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const response: { [key: string]: any } = await api.post('site/auth', {
|
||||||
|
site: wizardData.value.siteAuth.site,
|
||||||
|
params: wizardData.value.siteAuth.params,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
$toast.error(t('setupWizard.saveSiteAuthSettingsFailed', { message: response.message }))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Save site auth settings failed:', error)
|
||||||
|
$toast.error(t('setupWizard.saveSiteAuthSettingsFailed', { message: (error as Error).message || '' }))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 保存下载器配置
|
// 保存下载器配置
|
||||||
async function saveDownloaderSettings() {
|
async function saveDownloaderSettings() {
|
||||||
if (wizardData.value.downloader.type) {
|
if (wizardData.value.downloader.type) {
|
||||||
@@ -1298,6 +1423,7 @@ export function useSetupWizard() {
|
|||||||
if (result.data.GITHUB_TOKEN) {
|
if (result.data.GITHUB_TOKEN) {
|
||||||
wizardData.value.basic.githubToken = result.data.GITHUB_TOKEN
|
wizardData.value.basic.githubToken = result.data.GITHUB_TOKEN
|
||||||
}
|
}
|
||||||
|
wizardData.value.siteAuth.auxiliaryAuthEnable = Boolean(result.data.AUXILIARY_AUTH_ENABLE)
|
||||||
if (result.data.SUPERUSER) {
|
if (result.data.SUPERUSER) {
|
||||||
wizardData.value.basic.username = result.data.SUPERUSER
|
wizardData.value.basic.username = result.data.SUPERUSER
|
||||||
}
|
}
|
||||||
@@ -1326,6 +1452,28 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载用户站点认证列表
|
||||||
|
async function loadAuthSites() {
|
||||||
|
try {
|
||||||
|
authSites.value = (await api.get('site/auth')) || {}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Load auth sites failed:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载用户站点认证设置
|
||||||
|
async function loadSiteAuthSettings() {
|
||||||
|
try {
|
||||||
|
const result: { [key: string]: any } = await api.get('system/setting/UserSiteAuthParams')
|
||||||
|
if (result.success && result.data?.value) {
|
||||||
|
wizardData.value.siteAuth.site = result.data.value.site || ''
|
||||||
|
wizardData.value.siteAuth.params = result.data.value.params || {}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Load site auth settings failed:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 加载存储设置
|
// 加载存储设置
|
||||||
async function loadStorageSettings() {
|
async function loadStorageSettings() {
|
||||||
try {
|
try {
|
||||||
@@ -1395,6 +1543,8 @@ export function useSetupWizard() {
|
|||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
try {
|
try {
|
||||||
await loadSystemSettings()
|
await loadSystemSettings()
|
||||||
|
await loadAuthSites()
|
||||||
|
await loadSiteAuthSettings()
|
||||||
await loadStorageSettings()
|
await loadStorageSettings()
|
||||||
await loadDownloaderSettings()
|
await loadDownloaderSettings()
|
||||||
await loadMediaServerSettings()
|
await loadMediaServerSettings()
|
||||||
@@ -1411,6 +1561,7 @@ export function useSetupWizard() {
|
|||||||
stepTitles,
|
stepTitles,
|
||||||
stepDescriptions,
|
stepDescriptions,
|
||||||
wizardData,
|
wizardData,
|
||||||
|
authSites,
|
||||||
selectedPreset,
|
selectedPreset,
|
||||||
connectivityTest,
|
connectivityTest,
|
||||||
validationErrors,
|
validationErrors,
|
||||||
@@ -1425,6 +1576,7 @@ export function useSetupWizard() {
|
|||||||
selectPreset,
|
selectPreset,
|
||||||
updatePreferences,
|
updatePreferences,
|
||||||
validateCurrentStep,
|
validateCurrentStep,
|
||||||
|
validateSiteAuthFields,
|
||||||
validateDownloaderFields,
|
validateDownloaderFields,
|
||||||
validateMediaServerFields,
|
validateMediaServerFields,
|
||||||
validateNotificationFields,
|
validateNotificationFields,
|
||||||
|
|||||||
@@ -3183,6 +3183,7 @@ export default {
|
|||||||
saveMediaServerSettingsFailed: 'Failed to save media server settings',
|
saveMediaServerSettingsFailed: 'Failed to save media server settings',
|
||||||
notificationSettingsSaved: 'Notification settings saved successfully',
|
notificationSettingsSaved: 'Notification settings saved successfully',
|
||||||
saveNotificationSettingsFailed: 'Failed to save notification settings',
|
saveNotificationSettingsFailed: 'Failed to save notification settings',
|
||||||
|
saveSiteAuthSettingsFailed: 'Failed to save user site authentication settings: {message}',
|
||||||
saveAgentSettingsFailed: 'Failed to save AI assistant settings',
|
saveAgentSettingsFailed: 'Failed to save AI assistant settings',
|
||||||
preferenceSettingsSaved: 'Preference settings saved successfully',
|
preferenceSettingsSaved: 'Preference settings saved successfully',
|
||||||
savePreferenceSettingsFailed: 'Failed to save preference settings',
|
savePreferenceSettingsFailed: 'Failed to save preference settings',
|
||||||
@@ -3205,6 +3206,17 @@ export default {
|
|||||||
confirmPasswordHint: 'Confirm new password',
|
confirmPasswordHint: 'Confirm new password',
|
||||||
apiTokenRequired: 'API Token is required',
|
apiTokenRequired: 'API Token is required',
|
||||||
},
|
},
|
||||||
|
siteAuth: {
|
||||||
|
title: 'User Authentication',
|
||||||
|
description: 'Configure site authentication and auxiliary authentication',
|
||||||
|
info: 'User Site Authentication',
|
||||||
|
infoDesc:
|
||||||
|
'Completing site authentication unlocks site capabilities and some plugin permissions. This step is optional and can also be configured later from the user menu.',
|
||||||
|
selectSiteHint: 'Choose a supported auth site and fill in the required credentials for that site',
|
||||||
|
submitHint: 'When you click Next, the wizard will immediately validate against the selected auth site and save the current parameters on success.',
|
||||||
|
siteConfigNotExist: 'Authentication site configuration does not exist',
|
||||||
|
fieldRequired: 'Please enter {name}',
|
||||||
|
},
|
||||||
storage: {
|
storage: {
|
||||||
title: 'Storage',
|
title: 'Storage',
|
||||||
description: 'Configure download directory and media library directory',
|
description: 'Configure download directory and media library directory',
|
||||||
|
|||||||
@@ -3147,6 +3147,7 @@ export default {
|
|||||||
saveMediaServerSettingsFailed: '保存媒体服务器设置失败',
|
saveMediaServerSettingsFailed: '保存媒体服务器设置失败',
|
||||||
notificationSettingsSaved: '通知设置保存成功',
|
notificationSettingsSaved: '通知设置保存成功',
|
||||||
saveNotificationSettingsFailed: '保存通知设置失败',
|
saveNotificationSettingsFailed: '保存通知设置失败',
|
||||||
|
saveSiteAuthSettingsFailed: '保存用户站点认证设置失败:{message}',
|
||||||
saveAgentSettingsFailed: '保存智能助手设置失败',
|
saveAgentSettingsFailed: '保存智能助手设置失败',
|
||||||
preferenceSettingsSaved: '偏好设置保存成功',
|
preferenceSettingsSaved: '偏好设置保存成功',
|
||||||
savePreferenceSettingsFailed: '保存偏好设置失败',
|
savePreferenceSettingsFailed: '保存偏好设置失败',
|
||||||
@@ -3169,6 +3170,16 @@ export default {
|
|||||||
confirmPasswordHint: '确认新密码',
|
confirmPasswordHint: '确认新密码',
|
||||||
apiTokenRequired: 'API Token不能为空',
|
apiTokenRequired: 'API Token不能为空',
|
||||||
},
|
},
|
||||||
|
siteAuth: {
|
||||||
|
title: '用户认证',
|
||||||
|
description: '配置用户站点认证与辅助认证',
|
||||||
|
info: '用户站点认证说明',
|
||||||
|
infoDesc: '完成站点认证后可解锁站点能力与部分插件权限。此步骤可选,后续也可在个人菜单中继续配置。',
|
||||||
|
selectSiteHint: '选择一个支持认证的站点,并填写该站点要求的认证参数',
|
||||||
|
submitHint: '点击下一步时将立即向认证站点发起校验,认证成功后会保存当前参数。',
|
||||||
|
siteConfigNotExist: '认证站点配置不存在',
|
||||||
|
fieldRequired: '请输入{name}',
|
||||||
|
},
|
||||||
storage: {
|
storage: {
|
||||||
title: '存储',
|
title: '存储',
|
||||||
description: '配置下载目录和媒体库目录',
|
description: '配置下载目录和媒体库目录',
|
||||||
|
|||||||
@@ -3148,6 +3148,7 @@ export default {
|
|||||||
saveMediaServerSettingsFailed: '保存媒體服務器設置失敗',
|
saveMediaServerSettingsFailed: '保存媒體服務器設置失敗',
|
||||||
notificationSettingsSaved: '通知設置保存成功',
|
notificationSettingsSaved: '通知設置保存成功',
|
||||||
saveNotificationSettingsFailed: '保存通知設置失敗',
|
saveNotificationSettingsFailed: '保存通知設置失敗',
|
||||||
|
saveSiteAuthSettingsFailed: '保存用戶站點認證設置失敗:{message}',
|
||||||
saveAgentSettingsFailed: '保存智能助手設置失敗',
|
saveAgentSettingsFailed: '保存智能助手設置失敗',
|
||||||
preferenceSettingsSaved: '偏好設置保存成功',
|
preferenceSettingsSaved: '偏好設置保存成功',
|
||||||
savePreferenceSettingsFailed: '保存偏好設置失敗',
|
savePreferenceSettingsFailed: '保存偏好設置失敗',
|
||||||
@@ -3170,6 +3171,16 @@ export default {
|
|||||||
confirmPasswordHint: '確認新密碼',
|
confirmPasswordHint: '確認新密碼',
|
||||||
apiTokenRequired: 'API Token 不能為空',
|
apiTokenRequired: 'API Token 不能為空',
|
||||||
},
|
},
|
||||||
|
siteAuth: {
|
||||||
|
title: '用戶認證',
|
||||||
|
description: '配置用戶站點認證與輔助認證',
|
||||||
|
info: '用戶站點認證說明',
|
||||||
|
infoDesc: '完成站點認證後可解鎖站點能力與部分插件權限。此步驟可選,後續也可在個人選單中繼續配置。',
|
||||||
|
selectSiteHint: '選擇一個支援認證的站點,並填寫該站點要求的認證參數',
|
||||||
|
submitHint: '點擊下一步時將立即向認證站點發起校驗,認證成功後會保存當前參數。',
|
||||||
|
siteConfigNotExist: '認證站點配置不存在',
|
||||||
|
fieldRequired: '請輸入{name}',
|
||||||
|
},
|
||||||
storage: {
|
storage: {
|
||||||
title: '儲存',
|
title: '儲存',
|
||||||
description: '設定下載目錄和媒體庫目錄',
|
description: '設定下載目錄和媒體庫目錄',
|
||||||
|
|||||||
+17
-11
@@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useSetupWizard } from '@/composables/useSetupWizard'
|
import { useSetupWizard } from '@/composables/useSetupWizard'
|
||||||
import BasicSettingsStep from '@/views/setup/BasicSettingsStep.vue'
|
import BasicSettingsStep from '@/views/setup/BasicSettingsStep.vue'
|
||||||
|
import SiteAuthSettingsStep from '@/views/setup/SiteAuthSettingsStep.vue'
|
||||||
import StorageSettingsStep from '@/views/setup/StorageSettingsStep.vue'
|
import StorageSettingsStep from '@/views/setup/StorageSettingsStep.vue'
|
||||||
import DownloaderSettingsStep from '@/views/setup/DownloaderSettingsStep.vue'
|
import DownloaderSettingsStep from '@/views/setup/DownloaderSettingsStep.vue'
|
||||||
import MediaServerSettingsStep from '@/views/setup/MediaServerSettingsStep.vue'
|
import MediaServerSettingsStep from '@/views/setup/MediaServerSettingsStep.vue'
|
||||||
@@ -102,33 +103,38 @@ onMounted(async () => {
|
|||||||
<BasicSettingsStep />
|
<BasicSettingsStep />
|
||||||
</VStepperWindowItem>
|
</VStepperWindowItem>
|
||||||
|
|
||||||
<!-- 步骤2:存储目录 -->
|
<!-- 步骤2:用户认证 -->
|
||||||
<VStepperWindowItem :value="2">
|
<VStepperWindowItem :value="2">
|
||||||
|
<SiteAuthSettingsStep />
|
||||||
|
</VStepperWindowItem>
|
||||||
|
|
||||||
|
<!-- 步骤3:存储目录 -->
|
||||||
|
<VStepperWindowItem :value="3">
|
||||||
<StorageSettingsStep />
|
<StorageSettingsStep />
|
||||||
</VStepperWindowItem>
|
</VStepperWindowItem>
|
||||||
|
|
||||||
<!-- 步骤3:下载器 -->
|
<!-- 步骤4:下载器 -->
|
||||||
<VStepperWindowItem :value="3">
|
<VStepperWindowItem :value="4">
|
||||||
<DownloaderSettingsStep />
|
<DownloaderSettingsStep />
|
||||||
</VStepperWindowItem>
|
</VStepperWindowItem>
|
||||||
|
|
||||||
<!-- 步骤4:媒体服务器 -->
|
<!-- 步骤5:媒体服务器 -->
|
||||||
<VStepperWindowItem :value="4">
|
<VStepperWindowItem :value="5">
|
||||||
<MediaServerSettingsStep />
|
<MediaServerSettingsStep />
|
||||||
</VStepperWindowItem>
|
</VStepperWindowItem>
|
||||||
|
|
||||||
<!-- 步骤5:通知 -->
|
<!-- 步骤6:通知 -->
|
||||||
<VStepperWindowItem :value="5">
|
<VStepperWindowItem :value="6">
|
||||||
<NotificationSettingsStep />
|
<NotificationSettingsStep />
|
||||||
</VStepperWindowItem>
|
</VStepperWindowItem>
|
||||||
|
|
||||||
<!-- 步骤6:智能助手 -->
|
<!-- 步骤7:智能助手 -->
|
||||||
<VStepperWindowItem :value="6">
|
<VStepperWindowItem :value="7">
|
||||||
<AgentSettingsStep />
|
<AgentSettingsStep />
|
||||||
</VStepperWindowItem>
|
</VStepperWindowItem>
|
||||||
|
|
||||||
<!-- 步骤7:资源偏好 -->
|
<!-- 步骤8:资源偏好 -->
|
||||||
<VStepperWindowItem :value="7">
|
<VStepperWindowItem :value="8">
|
||||||
<PreferencesSettingsStep />
|
<PreferencesSettingsStep />
|
||||||
</VStepperWindowItem>
|
</VStepperWindowItem>
|
||||||
</VStepperWindow>
|
</VStepperWindow>
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useSetupWizard } from '@/composables/useSetupWizard'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const { wizardData, authSites, validationErrors } = useSetupWizard()
|
||||||
|
|
||||||
|
const siteItems = computed(() => {
|
||||||
|
return Object.keys(authSites.value).map(key => ({
|
||||||
|
key,
|
||||||
|
name: authSites.value[key].name,
|
||||||
|
prependAvatar: authSites.value[key].icon,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
const formFields = computed(() => {
|
||||||
|
const site = authSites.value[wizardData.value.siteAuth.site]
|
||||||
|
return Object.keys(site?.params || {})
|
||||||
|
.filter(key => site.params[key]?.name && site.params[key]?.type)
|
||||||
|
.map(key => ({
|
||||||
|
key,
|
||||||
|
site: wizardData.value.siteAuth.site,
|
||||||
|
name: site.params[key].name,
|
||||||
|
type: site.params[key].type,
|
||||||
|
placeholder: site.params[key].placeholder,
|
||||||
|
tooltip: site.params[key].tooltip,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VCard variant="outlined">
|
||||||
|
<VCardText>
|
||||||
|
<div class="text-center mb-6">
|
||||||
|
<h3 class="text-h4 mb-2">{{ t('setupWizard.siteAuth.title') }}</h3>
|
||||||
|
<p class="text-body-1 text-medium-emphasis">{{ t('setupWizard.siteAuth.description') }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12">
|
||||||
|
<VAlert type="info" variant="tonal" class="mb-4">
|
||||||
|
<VAlertTitle>{{ t('setupWizard.siteAuth.info') }}</VAlertTitle>
|
||||||
|
{{ t('setupWizard.siteAuth.infoDesc') }}
|
||||||
|
</VAlert>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12">
|
||||||
|
<VSwitch
|
||||||
|
v-model="wizardData.siteAuth.auxiliaryAuthEnable"
|
||||||
|
:label="t('setting.system.auxAuthEnable')"
|
||||||
|
:hint="t('setting.system.auxAuthEnableHint')"
|
||||||
|
persistent-hint
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12">
|
||||||
|
<VSelect
|
||||||
|
v-model="wizardData.siteAuth.site"
|
||||||
|
:items="siteItems"
|
||||||
|
item-value="key"
|
||||||
|
item-title="name"
|
||||||
|
item-props
|
||||||
|
:label="t('dialog.userAuth.selectSite')"
|
||||||
|
:hint="t('setupWizard.siteAuth.selectSiteHint')"
|
||||||
|
:error="validationErrors.siteAuth.site"
|
||||||
|
:error-messages="validationErrors.siteAuth.site ? [t('dialog.userAuth.selectSiteRequired')] : []"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-web"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<template v-if="wizardData.siteAuth.site">
|
||||||
|
<VCol cols="12">
|
||||||
|
<VAlert type="warning" variant="tonal">
|
||||||
|
{{ t('setupWizard.siteAuth.submitHint') }}
|
||||||
|
</VAlert>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol v-for="param in formFields" :key="param.key" cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="wizardData.siteAuth.params[param.site.toUpperCase() + '_' + param.key.toUpperCase()]"
|
||||||
|
:type="param.type"
|
||||||
|
:label="param.name"
|
||||||
|
:placeholder="param.placeholder"
|
||||||
|
:hint="param.tooltip"
|
||||||
|
:error="validationErrors.siteAuth[param.site.toUpperCase() + '_' + param.key.toUpperCase()]"
|
||||||
|
:error-messages="
|
||||||
|
validationErrors.siteAuth[param.site.toUpperCase() + '_' + param.key.toUpperCase()]
|
||||||
|
? [t('setupWizard.siteAuth.fieldRequired', { name: param.name })]
|
||||||
|
: []
|
||||||
|
"
|
||||||
|
clearable
|
||||||
|
persistent-hint
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</template>
|
||||||
|
</VRow>
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user