mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 23:56:42 +08:00
feat: add AI agent configuration step and expand basic settings with OCR and recognition source options
This commit is contained in:
@@ -13,6 +13,8 @@ export interface WizardData {
|
|||||||
username: string
|
username: string
|
||||||
password: string
|
password: string
|
||||||
confirmPassword: string
|
confirmPassword: string
|
||||||
|
recognizeSource: string
|
||||||
|
ocrHost: string
|
||||||
proxyHost: string
|
proxyHost: string
|
||||||
githubToken: string
|
githubToken: string
|
||||||
}
|
}
|
||||||
@@ -41,6 +43,22 @@ export interface WizardData {
|
|||||||
config: any
|
config: any
|
||||||
switchs: any[]
|
switchs: any[]
|
||||||
}
|
}
|
||||||
|
agent: {
|
||||||
|
enabled: boolean
|
||||||
|
global: boolean
|
||||||
|
verbose: boolean
|
||||||
|
provider: string
|
||||||
|
model: string
|
||||||
|
supportImageInput: boolean
|
||||||
|
apiKey: string
|
||||||
|
baseUrl: string
|
||||||
|
maxContextTokens: number
|
||||||
|
jobInterval: number
|
||||||
|
retryTransfer: boolean
|
||||||
|
recommendEnabled: boolean
|
||||||
|
recommendUserPreference: string
|
||||||
|
recommendMaxItems: number
|
||||||
|
}
|
||||||
preferences: {
|
preferences: {
|
||||||
quality: string
|
quality: string
|
||||||
subtitle: string
|
subtitle: string
|
||||||
@@ -85,11 +103,18 @@ export interface ValidationErrorState {
|
|||||||
name: boolean
|
name: boolean
|
||||||
[key: string]: boolean
|
[key: string]: boolean
|
||||||
}
|
}
|
||||||
|
agent: {
|
||||||
|
provider: boolean
|
||||||
|
apiKey: boolean
|
||||||
|
model: boolean
|
||||||
|
maxContextTokens: boolean
|
||||||
|
recommendMaxItems: boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 全局状态,所有组件共享
|
// 全局状态,所有组件共享
|
||||||
const currentStep = ref(1)
|
const currentStep = ref(1)
|
||||||
const totalSteps = 6
|
const totalSteps = 7
|
||||||
|
|
||||||
// 加载状态
|
// 加载状态
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
@@ -105,6 +130,8 @@ const wizardData = ref<WizardData>({
|
|||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
confirmPassword: '',
|
confirmPassword: '',
|
||||||
|
recognizeSource: 'themoviedb',
|
||||||
|
ocrHost: '',
|
||||||
proxyHost: '',
|
proxyHost: '',
|
||||||
githubToken: '',
|
githubToken: '',
|
||||||
},
|
},
|
||||||
@@ -133,6 +160,22 @@ const wizardData = ref<WizardData>({
|
|||||||
config: {},
|
config: {},
|
||||||
switchs: [],
|
switchs: [],
|
||||||
},
|
},
|
||||||
|
agent: {
|
||||||
|
enabled: false,
|
||||||
|
global: false,
|
||||||
|
verbose: false,
|
||||||
|
provider: 'deepseek',
|
||||||
|
model: 'deepseek-chat',
|
||||||
|
supportImageInput: true,
|
||||||
|
apiKey: '',
|
||||||
|
baseUrl: 'https://api.deepseek.com',
|
||||||
|
maxContextTokens: 64,
|
||||||
|
jobInterval: 0,
|
||||||
|
retryTransfer: false,
|
||||||
|
recommendEnabled: false,
|
||||||
|
recommendUserPreference: '',
|
||||||
|
recommendMaxItems: 50,
|
||||||
|
},
|
||||||
preferences: {
|
preferences: {
|
||||||
quality: '4K',
|
quality: '4K',
|
||||||
subtitle: 'chinese',
|
subtitle: 'chinese',
|
||||||
@@ -168,6 +211,13 @@ const validationErrors = ref<ValidationErrorState>({
|
|||||||
notification: {
|
notification: {
|
||||||
name: false,
|
name: false,
|
||||||
},
|
},
|
||||||
|
agent: {
|
||||||
|
provider: false,
|
||||||
|
apiKey: false,
|
||||||
|
model: false,
|
||||||
|
maxContextTokens: false,
|
||||||
|
recommendMaxItems: false,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export function useSetupWizard() {
|
export function useSetupWizard() {
|
||||||
@@ -181,6 +231,7 @@ export function useSetupWizard() {
|
|||||||
downloader: {
|
downloader: {
|
||||||
'qbittorrent': 'QbittorrentModule',
|
'qbittorrent': 'QbittorrentModule',
|
||||||
'transmission': 'TransmissionModule',
|
'transmission': 'TransmissionModule',
|
||||||
|
'rtorrent': 'RtorrentModule',
|
||||||
},
|
},
|
||||||
// 媒体服务器映射
|
// 媒体服务器映射
|
||||||
mediaServer: {
|
mediaServer: {
|
||||||
@@ -196,6 +247,7 @@ export function useSetupWizard() {
|
|||||||
'wechat': 'WechatModule',
|
'wechat': 'WechatModule',
|
||||||
'slack': 'SlackModule',
|
'slack': 'SlackModule',
|
||||||
'synologychat': 'SynologyChatModule',
|
'synologychat': 'SynologyChatModule',
|
||||||
|
'qqbot': 'QQBotModule',
|
||||||
'vocechat': 'VoceChatModule',
|
'vocechat': 'VoceChatModule',
|
||||||
'webpush': 'WebPushModule',
|
'webpush': 'WebPushModule',
|
||||||
},
|
},
|
||||||
@@ -208,6 +260,7 @@ export function useSetupWizard() {
|
|||||||
t('setupWizard.downloader.title'),
|
t('setupWizard.downloader.title'),
|
||||||
t('setupWizard.mediaServer.title'),
|
t('setupWizard.mediaServer.title'),
|
||||||
t('setupWizard.notification.title'),
|
t('setupWizard.notification.title'),
|
||||||
|
t('setupWizard.agent.title'),
|
||||||
t('setupWizard.preferences.title'),
|
t('setupWizard.preferences.title'),
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -218,6 +271,7 @@ export function useSetupWizard() {
|
|||||||
t('setupWizard.downloader.description'),
|
t('setupWizard.downloader.description'),
|
||||||
t('setupWizard.mediaServer.description'),
|
t('setupWizard.mediaServer.description'),
|
||||||
t('setupWizard.notification.description'),
|
t('setupWizard.notification.description'),
|
||||||
|
t('setupWizard.agent.description'),
|
||||||
t('setupWizard.preferences.description'),
|
t('setupWizard.preferences.description'),
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -341,6 +395,13 @@ export function useSetupWizard() {
|
|||||||
validationErrors.value.notification = {
|
validationErrors.value.notification = {
|
||||||
name: false,
|
name: false,
|
||||||
}
|
}
|
||||||
|
validationErrors.value.agent = {
|
||||||
|
provider: false,
|
||||||
|
apiKey: false,
|
||||||
|
model: false,
|
||||||
|
maxContextTokens: false,
|
||||||
|
recommendMaxItems: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证下载器字段
|
// 验证下载器字段
|
||||||
@@ -361,7 +422,11 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 根据下载器类型验证其他必输项
|
// 根据下载器类型验证其他必输项
|
||||||
if (wizardData.value.downloader.type === 'qbittorrent' || wizardData.value.downloader.type === 'transmission') {
|
if (
|
||||||
|
wizardData.value.downloader.type === 'qbittorrent'
|
||||||
|
|| wizardData.value.downloader.type === 'transmission'
|
||||||
|
|| wizardData.value.downloader.type === 'rtorrent'
|
||||||
|
) {
|
||||||
if (!wizardData.value.downloader.config?.username?.trim()) {
|
if (!wizardData.value.downloader.config?.username?.trim()) {
|
||||||
errors.push(t('downloader.usernameRequired'))
|
errors.push(t('downloader.usernameRequired'))
|
||||||
validationErrors.value.downloader.username = true
|
validationErrors.value.downloader.username = true
|
||||||
@@ -487,6 +552,12 @@ export function useSetupWizard() {
|
|||||||
validationErrors.value.notification.VOCECHAT_API_KEY = true
|
validationErrors.value.notification.VOCECHAT_API_KEY = true
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
case 'webpush':
|
||||||
|
if (!config.WEBPUSH_USERNAME?.trim()) {
|
||||||
|
errors.push(t('notification.webpush.usernameRequired'))
|
||||||
|
validationErrors.value.notification.WEBPUSH_USERNAME = true
|
||||||
|
}
|
||||||
|
break
|
||||||
case 'qqbot':
|
case 'qqbot':
|
||||||
if (!config.QQ_APP_ID?.trim()) {
|
if (!config.QQ_APP_ID?.trim()) {
|
||||||
errors.push(t('notification.qqbot.appIdRequired'))
|
errors.push(t('notification.qqbot.appIdRequired'))
|
||||||
@@ -505,6 +576,49 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证智能助手字段
|
||||||
|
function validateAgentFields(): { isValid: boolean; errors: string[] } {
|
||||||
|
const errors: string[] = []
|
||||||
|
clearValidationErrors()
|
||||||
|
|
||||||
|
if (!wizardData.value.agent.enabled) {
|
||||||
|
return {
|
||||||
|
isValid: true,
|
||||||
|
errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wizardData.value.agent.provider?.trim()) {
|
||||||
|
errors.push(t('setupWizard.agent.providerRequired'))
|
||||||
|
validationErrors.value.agent.provider = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wizardData.value.agent.apiKey?.trim()) {
|
||||||
|
errors.push(t('setupWizard.agent.apiKeyRequired'))
|
||||||
|
validationErrors.value.agent.apiKey = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wizardData.value.agent.model?.trim()) {
|
||||||
|
errors.push(t('setupWizard.agent.modelRequired'))
|
||||||
|
validationErrors.value.agent.model = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wizardData.value.agent.maxContextTokens || wizardData.value.agent.maxContextTokens < 1) {
|
||||||
|
errors.push(t('setupWizard.agent.maxContextTokensRequired'))
|
||||||
|
validationErrors.value.agent.maxContextTokens = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wizardData.value.agent.recommendEnabled && (!wizardData.value.agent.recommendMaxItems || wizardData.value.agent.recommendMaxItems < 1)) {
|
||||||
|
errors.push(t('setupWizard.agent.recommendMaxItemsRequired'))
|
||||||
|
validationErrors.value.agent.recommendMaxItems = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isValid: errors.length === 0,
|
||||||
|
errors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 验证当前步骤的必输项
|
// 验证当前步骤的必输项
|
||||||
function validateCurrentStep(): { isValid: boolean; errors: string[] } {
|
function validateCurrentStep(): { isValid: boolean; errors: string[] } {
|
||||||
const errors: string[] = []
|
const errors: string[] = []
|
||||||
@@ -563,7 +677,14 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
case 6: // 偏好设置
|
case 6: // 智能助手设置
|
||||||
|
if (wizardData.value.agent.enabled) {
|
||||||
|
const validation = validateAgentFields()
|
||||||
|
errors.push(...validation.errors)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 7: // 偏好设置
|
||||||
// 偏好设置有默认值,不需要验证
|
// 偏好设置有默认值,不需要验证
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -794,18 +915,21 @@ export function useSetupWizard() {
|
|||||||
validation.errors.forEach(error => {
|
validation.errors.forEach(error => {
|
||||||
$toast.error(error)
|
$toast.error(error)
|
||||||
})
|
})
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存当前步骤的设置
|
// 保存当前步骤的设置
|
||||||
await saveCurrentStepSettings()
|
const saved = await saveCurrentStepSettings()
|
||||||
|
if (!saved) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// 检查是否需要进行测试
|
// 检查是否需要进行测试
|
||||||
const needsTest = shouldPerformTest(currentStep.value)
|
const needsTest = shouldPerformTest(currentStep.value)
|
||||||
if (needsTest) {
|
if (needsTest) {
|
||||||
const testResult = await testConnectivity(currentStep.value)
|
const testResult = await testConnectivity(currentStep.value)
|
||||||
if (!testResult) {
|
if (!testResult) {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,6 +938,8 @@ export function useSetupWizard() {
|
|||||||
currentStep.value++
|
currentStep.value++
|
||||||
connectivityTest.value.showResult = false
|
connectivityTest.value.showResult = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上一步
|
// 上一步
|
||||||
@@ -829,35 +955,36 @@ export function useSetupWizard() {
|
|||||||
try {
|
try {
|
||||||
switch (currentStep.value) {
|
switch (currentStep.value) {
|
||||||
case 1:
|
case 1:
|
||||||
await saveBasicSettings()
|
return await saveBasicSettings()
|
||||||
break
|
|
||||||
case 2:
|
case 2:
|
||||||
await saveStorageSettings()
|
return await saveStorageSettings()
|
||||||
break
|
|
||||||
case 3:
|
case 3:
|
||||||
await saveDownloaderSettings()
|
return await saveDownloaderSettings()
|
||||||
break
|
|
||||||
case 4:
|
case 4:
|
||||||
await saveMediaServerSettings()
|
return await saveMediaServerSettings()
|
||||||
break
|
|
||||||
case 5:
|
case 5:
|
||||||
await saveNotificationSettings()
|
return await saveNotificationSettings()
|
||||||
break
|
|
||||||
case 6:
|
case 6:
|
||||||
await savePreferenceSettings()
|
return await saveAgentSettings()
|
||||||
break
|
case 7:
|
||||||
|
return await savePreferenceSettings()
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Save current step settings failed:', error)
|
console.error('Save current step settings failed:', error)
|
||||||
$toast.error(t('setupWizard.saveStepFailed'))
|
$toast.error(t('setupWizard.saveStepFailed'))
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 完成向导
|
// 完成向导
|
||||||
async function completeWizard() {
|
async function completeWizard() {
|
||||||
try {
|
try {
|
||||||
// 先处理下一步(保存当前步骤设置)
|
// 先处理下一步(保存当前步骤设置)
|
||||||
await nextStep()
|
const saved = await nextStep()
|
||||||
|
if (!saved) {
|
||||||
|
return
|
||||||
|
}
|
||||||
// 保存设置向导完成状态
|
// 保存设置向导完成状态
|
||||||
await saveSetupWizardState()
|
await saveSetupWizardState()
|
||||||
|
|
||||||
@@ -910,6 +1037,8 @@ export function useSetupWizard() {
|
|||||||
const basicSettings = {
|
const basicSettings = {
|
||||||
APP_DOMAIN: wizardData.value.basic.appDomain,
|
APP_DOMAIN: wizardData.value.basic.appDomain,
|
||||||
API_TOKEN: wizardData.value.basic.apiToken,
|
API_TOKEN: wizardData.value.basic.apiToken,
|
||||||
|
RECOGNIZE_SOURCE: wizardData.value.basic.recognizeSource,
|
||||||
|
OCR_HOST: wizardData.value.basic.ocrHost,
|
||||||
PROXY_HOST: wizardData.value.basic.proxyHost,
|
PROXY_HOST: wizardData.value.basic.proxyHost,
|
||||||
GITHUB_TOKEN: wizardData.value.basic.githubToken,
|
GITHUB_TOKEN: wizardData.value.basic.githubToken,
|
||||||
}
|
}
|
||||||
@@ -917,21 +1046,23 @@ export function useSetupWizard() {
|
|||||||
// 保存基础设置
|
// 保存基础设置
|
||||||
const response: { [key: string]: any } = await api.post('system/env', basicSettings)
|
const response: { [key: string]: any } = await api.post('system/env', basicSettings)
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果输入了密码,验证密码一致性
|
// 如果输入了密码,验证密码一致性
|
||||||
if (wizardData.value.basic.password) {
|
if (wizardData.value.basic.password) {
|
||||||
if (wizardData.value.basic.password !== wizardData.value.basic.confirmPassword) {
|
if (wizardData.value.basic.password !== wizardData.value.basic.confirmPassword) {
|
||||||
$toast.error(t('dialog.userAddEdit.passwordMismatch'))
|
$toast.error(t('dialog.userAddEdit.passwordMismatch'))
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
// 更新用户密码
|
// 更新用户密码
|
||||||
await updateUserPassword()
|
await updateUserPassword()
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Save basic settings failed:', error)
|
console.error('Save basic settings failed:', error)
|
||||||
$toast.error(t('setupWizard.saveBasicSettingsFailed'))
|
$toast.error(t('setupWizard.saveBasicSettingsFailed'))
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -970,9 +1101,11 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await api.post('system/setting/Directories', [directory])
|
await api.post('system/setting/Directories', [directory])
|
||||||
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Save storage settings failed:', error)
|
console.error('Save storage settings failed:', error)
|
||||||
$toast.error(t('setupWizard.saveStorageSettingsFailed'))
|
$toast.error(t('setupWizard.saveStorageSettingsFailed'))
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -992,13 +1125,16 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await api.post('system/setting/Downloaders', [downloader])
|
await api.post('system/setting/Downloaders', [downloader])
|
||||||
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Save downloader settings failed:', error)
|
console.error('Save downloader settings failed:', error)
|
||||||
$toast.error(t('setupWizard.saveDownloaderSettingsFailed'))
|
$toast.error(t('setupWizard.saveDownloaderSettingsFailed'))
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 没有选择下载器时,清空现有配置
|
// 没有选择下载器时,清空现有配置
|
||||||
console.log('No downloader selected, skipping save')
|
console.log('No downloader selected, skipping save')
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1019,13 +1155,16 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await api.post('system/setting/MediaServers', [mediaServer])
|
await api.post('system/setting/MediaServers', [mediaServer])
|
||||||
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Save media server settings failed:', error)
|
console.error('Save media server settings failed:', error)
|
||||||
$toast.error(t('setupWizard.saveMediaServerSettingsFailed'))
|
$toast.error(t('setupWizard.saveMediaServerSettingsFailed'))
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 没有选择媒体服务器时,清空现有配置
|
// 没有选择媒体服务器时,清空现有配置
|
||||||
console.log('No media server selected, skipping save')
|
console.log('No media server selected, skipping save')
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1046,13 +1185,46 @@ export function useSetupWizard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await api.post('system/setting/Notifications', [notification])
|
await api.post('system/setting/Notifications', [notification])
|
||||||
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Save notification settings failed:', error)
|
console.error('Save notification settings failed:', error)
|
||||||
$toast.error(t('setupWizard.saveNotificationSettingsFailed'))
|
$toast.error(t('setupWizard.saveNotificationSettingsFailed'))
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 没有选择通知时,清空现有配置
|
// 没有选择通知时,清空现有配置
|
||||||
console.log('No notification selected, skipping save')
|
console.log('No notification selected, skipping save')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存智能助手设置
|
||||||
|
async function saveAgentSettings() {
|
||||||
|
try {
|
||||||
|
const agentSettings = {
|
||||||
|
AI_AGENT_ENABLE: wizardData.value.agent.enabled,
|
||||||
|
AI_AGENT_GLOBAL: wizardData.value.agent.enabled ? wizardData.value.agent.global : false,
|
||||||
|
AI_AGENT_VERBOSE: wizardData.value.agent.enabled ? wizardData.value.agent.verbose : false,
|
||||||
|
LLM_PROVIDER: wizardData.value.agent.provider,
|
||||||
|
LLM_MODEL: wizardData.value.agent.model,
|
||||||
|
LLM_SUPPORT_IMAGE_INPUT: wizardData.value.agent.supportImageInput,
|
||||||
|
LLM_API_KEY: wizardData.value.agent.apiKey,
|
||||||
|
LLM_BASE_URL: wizardData.value.agent.baseUrl || null,
|
||||||
|
LLM_MAX_CONTEXT_TOKENS: wizardData.value.agent.maxContextTokens,
|
||||||
|
AI_AGENT_JOB_INTERVAL: wizardData.value.agent.enabled ? wizardData.value.agent.jobInterval : 0,
|
||||||
|
AI_AGENT_RETRY_TRANSFER: wizardData.value.agent.enabled ? wizardData.value.agent.retryTransfer : false,
|
||||||
|
AI_RECOMMEND_ENABLED:
|
||||||
|
wizardData.value.agent.enabled && wizardData.value.agent.recommendEnabled,
|
||||||
|
AI_RECOMMEND_USER_PREFERENCE: wizardData.value.agent.recommendUserPreference,
|
||||||
|
AI_RECOMMEND_MAX_ITEMS: wizardData.value.agent.recommendMaxItems,
|
||||||
|
}
|
||||||
|
|
||||||
|
await api.post('system/env', agentSettings)
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Save agent settings failed:', error)
|
||||||
|
$toast.error(t('setupWizard.saveAgentSettingsFailed'))
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1081,9 +1253,11 @@ export function useSetupWizard() {
|
|||||||
console.error('Save rule sequences failed:', error)
|
console.error('Save rule sequences failed:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Save preference settings failed:', error)
|
console.error('Save preference settings failed:', error)
|
||||||
$toast.error(t('setupWizard.savePreferenceSettingsFailed'))
|
$toast.error(t('setupWizard.savePreferenceSettingsFailed'))
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1115,12 +1289,32 @@ export function useSetupWizard() {
|
|||||||
if (result.data.PROXY_HOST) {
|
if (result.data.PROXY_HOST) {
|
||||||
wizardData.value.basic.proxyHost = result.data.PROXY_HOST
|
wizardData.value.basic.proxyHost = result.data.PROXY_HOST
|
||||||
}
|
}
|
||||||
|
if (result.data.RECOGNIZE_SOURCE) {
|
||||||
|
wizardData.value.basic.recognizeSource = result.data.RECOGNIZE_SOURCE
|
||||||
|
}
|
||||||
|
if (result.data.OCR_HOST) {
|
||||||
|
wizardData.value.basic.ocrHost = result.data.OCR_HOST
|
||||||
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
if (result.data.SUPERUSER) {
|
if (result.data.SUPERUSER) {
|
||||||
wizardData.value.basic.username = result.data.SUPERUSER
|
wizardData.value.basic.username = result.data.SUPERUSER
|
||||||
}
|
}
|
||||||
|
wizardData.value.agent.enabled = Boolean(result.data.AI_AGENT_ENABLE)
|
||||||
|
wizardData.value.agent.global = Boolean(result.data.AI_AGENT_GLOBAL)
|
||||||
|
wizardData.value.agent.verbose = Boolean(result.data.AI_AGENT_VERBOSE)
|
||||||
|
wizardData.value.agent.provider = result.data.LLM_PROVIDER || 'deepseek'
|
||||||
|
wizardData.value.agent.model = result.data.LLM_MODEL || ''
|
||||||
|
wizardData.value.agent.supportImageInput = result.data.LLM_SUPPORT_IMAGE_INPUT ?? true
|
||||||
|
wizardData.value.agent.apiKey = result.data.LLM_API_KEY || ''
|
||||||
|
wizardData.value.agent.baseUrl = result.data.LLM_BASE_URL || ''
|
||||||
|
wizardData.value.agent.maxContextTokens = result.data.LLM_MAX_CONTEXT_TOKENS || 64
|
||||||
|
wizardData.value.agent.jobInterval = result.data.AI_AGENT_JOB_INTERVAL || 0
|
||||||
|
wizardData.value.agent.retryTransfer = Boolean(result.data.AI_AGENT_RETRY_TRANSFER)
|
||||||
|
wizardData.value.agent.recommendEnabled = Boolean(result.data.AI_RECOMMEND_ENABLED)
|
||||||
|
wizardData.value.agent.recommendUserPreference = result.data.AI_RECOMMEND_USER_PREFERENCE || ''
|
||||||
|
wizardData.value.agent.recommendMaxItems = result.data.AI_RECOMMEND_MAX_ITEMS || 50
|
||||||
|
|
||||||
// 如果没有API Token,则创建一个随机的
|
// 如果没有API Token,则创建一个随机的
|
||||||
if (!wizardData.value.basic.apiToken) {
|
if (!wizardData.value.basic.apiToken) {
|
||||||
@@ -1234,6 +1428,7 @@ export function useSetupWizard() {
|
|||||||
validateDownloaderFields,
|
validateDownloaderFields,
|
||||||
validateMediaServerFields,
|
validateMediaServerFields,
|
||||||
validateNotificationFields,
|
validateNotificationFields,
|
||||||
|
validateAgentFields,
|
||||||
clearValidationErrors,
|
clearValidationErrors,
|
||||||
testConnectivity,
|
testConnectivity,
|
||||||
nextStep,
|
nextStep,
|
||||||
|
|||||||
@@ -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',
|
||||||
|
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',
|
||||||
passwordUpdateSuccess: 'Password updated successfully',
|
passwordUpdateSuccess: 'Password updated successfully',
|
||||||
@@ -3268,6 +3269,18 @@ export default {
|
|||||||
senderPassword: 'Sender Password',
|
senderPassword: 'Sender Password',
|
||||||
receiverEmail: 'Receiver Email',
|
receiverEmail: 'Receiver Email',
|
||||||
},
|
},
|
||||||
|
agent: {
|
||||||
|
title: 'AI Assistant',
|
||||||
|
description: 'Configure the Agent assistant and LLM parameters',
|
||||||
|
info: 'AI Assistant Configuration',
|
||||||
|
infoDesc:
|
||||||
|
'After enabling it, you can use the Agent in message conversations and optionally turn on transfer-failure takeover and AI recommendations.',
|
||||||
|
providerRequired: 'LLM provider is required',
|
||||||
|
apiKeyRequired: 'LLM API key is required',
|
||||||
|
modelRequired: 'LLM model name is required',
|
||||||
|
maxContextTokensRequired: 'LLM max context tokens must be greater than 0',
|
||||||
|
recommendMaxItemsRequired: 'AI recommendation analysis limit must be greater than 0',
|
||||||
|
},
|
||||||
preferences: {
|
preferences: {
|
||||||
title: 'Resource Preferences',
|
title: 'Resource Preferences',
|
||||||
description: 'Set resource download preferences',
|
description: 'Set resource download preferences',
|
||||||
|
|||||||
+12
-4
@@ -3147,6 +3147,7 @@ export default {
|
|||||||
saveMediaServerSettingsFailed: '保存媒体服务器设置失败',
|
saveMediaServerSettingsFailed: '保存媒体服务器设置失败',
|
||||||
notificationSettingsSaved: '通知设置保存成功',
|
notificationSettingsSaved: '通知设置保存成功',
|
||||||
saveNotificationSettingsFailed: '保存通知设置失败',
|
saveNotificationSettingsFailed: '保存通知设置失败',
|
||||||
|
saveAgentSettingsFailed: '保存智能助手设置失败',
|
||||||
preferenceSettingsSaved: '偏好设置保存成功',
|
preferenceSettingsSaved: '偏好设置保存成功',
|
||||||
savePreferenceSettingsFailed: '保存偏好设置失败',
|
savePreferenceSettingsFailed: '保存偏好设置失败',
|
||||||
passwordUpdateSuccess: '密码更新成功',
|
passwordUpdateSuccess: '密码更新成功',
|
||||||
@@ -3231,6 +3232,17 @@ export default {
|
|||||||
senderPassword: '发送密码',
|
senderPassword: '发送密码',
|
||||||
receiverEmail: '接收邮箱',
|
receiverEmail: '接收邮箱',
|
||||||
},
|
},
|
||||||
|
agent: {
|
||||||
|
title: '智能助手',
|
||||||
|
description: '配置 Agent 助手与 LLM 参数',
|
||||||
|
info: '智能助手配置说明',
|
||||||
|
infoDesc: '启用后可在消息会话中使用 Agent 能力,也可开启失败整理接管和智能推荐。',
|
||||||
|
providerRequired: 'LLM 提供商不能为空',
|
||||||
|
apiKeyRequired: 'LLM API 密钥不能为空',
|
||||||
|
modelRequired: 'LLM 模型名称不能为空',
|
||||||
|
maxContextTokensRequired: 'LLM 最大上下文 Token 数量必须大于 0',
|
||||||
|
recommendMaxItemsRequired: '智能推荐分析条目上限必须大于 0',
|
||||||
|
},
|
||||||
preferences: {
|
preferences: {
|
||||||
title: '资源偏好',
|
title: '资源偏好',
|
||||||
description: '设置资源下载偏好',
|
description: '设置资源下载偏好',
|
||||||
@@ -3273,7 +3285,3 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply patch to add category strings
|
|
||||||
// This is a temporary placeholder command to show intent.
|
|
||||||
// I will use replace_file_content to actually edit the file safely.
|
|
||||||
|
|||||||
@@ -3148,6 +3148,7 @@ export default {
|
|||||||
saveMediaServerSettingsFailed: '保存媒體服務器設置失敗',
|
saveMediaServerSettingsFailed: '保存媒體服務器設置失敗',
|
||||||
notificationSettingsSaved: '通知設置保存成功',
|
notificationSettingsSaved: '通知設置保存成功',
|
||||||
saveNotificationSettingsFailed: '保存通知設置失敗',
|
saveNotificationSettingsFailed: '保存通知設置失敗',
|
||||||
|
saveAgentSettingsFailed: '保存智能助手設置失敗',
|
||||||
preferenceSettingsSaved: '偏好設置保存成功',
|
preferenceSettingsSaved: '偏好設置保存成功',
|
||||||
savePreferenceSettingsFailed: '保存偏好設置失敗',
|
savePreferenceSettingsFailed: '保存偏好設置失敗',
|
||||||
passwordUpdateSuccess: '密碼更新成功',
|
passwordUpdateSuccess: '密碼更新成功',
|
||||||
@@ -3232,6 +3233,17 @@ export default {
|
|||||||
senderPassword: '發送密碼',
|
senderPassword: '發送密碼',
|
||||||
receiverEmail: '接收信箱',
|
receiverEmail: '接收信箱',
|
||||||
},
|
},
|
||||||
|
agent: {
|
||||||
|
title: '智能助手',
|
||||||
|
description: '配置 Agent 助手與 LLM 參數',
|
||||||
|
info: '智能助手配置說明',
|
||||||
|
infoDesc: '啟用後可在消息對話中使用 Agent 能力,也可開啟失敗整理接管與智能推薦。',
|
||||||
|
providerRequired: 'LLM 提供商不能為空',
|
||||||
|
apiKeyRequired: 'LLM API 密鑰不能為空',
|
||||||
|
modelRequired: 'LLM 模型名稱不能為空',
|
||||||
|
maxContextTokensRequired: 'LLM 最大上下文 Token 數量必須大於 0',
|
||||||
|
recommendMaxItemsRequired: '智能推薦分析條目上限必須大於 0',
|
||||||
|
},
|
||||||
preferences: {
|
preferences: {
|
||||||
title: '資源偏好',
|
title: '資源偏好',
|
||||||
description: '設定資源下載偏好',
|
description: '設定資源下載偏好',
|
||||||
|
|||||||
+7
-1
@@ -8,6 +8,7 @@ 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'
|
||||||
import NotificationSettingsStep from '@/views/setup/NotificationSettingsStep.vue'
|
import NotificationSettingsStep from '@/views/setup/NotificationSettingsStep.vue'
|
||||||
|
import AgentSettingsStep from '@/views/setup/AgentSettingsStep.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'
|
import { useDisplay } from 'vuetify'
|
||||||
@@ -121,8 +122,13 @@ onMounted(async () => {
|
|||||||
<NotificationSettingsStep />
|
<NotificationSettingsStep />
|
||||||
</VStepperWindowItem>
|
</VStepperWindowItem>
|
||||||
|
|
||||||
<!-- 步骤6:资源偏好 -->
|
<!-- 步骤6:智能助手 -->
|
||||||
<VStepperWindowItem :value="6">
|
<VStepperWindowItem :value="6">
|
||||||
|
<AgentSettingsStep />
|
||||||
|
</VStepperWindowItem>
|
||||||
|
|
||||||
|
<!-- 步骤7:资源偏好 -->
|
||||||
|
<VStepperWindowItem :value="7">
|
||||||
<PreferencesSettingsStep />
|
<PreferencesSettingsStep />
|
||||||
</VStepperWindowItem>
|
</VStepperWindowItem>
|
||||||
</VStepperWindow>
|
</VStepperWindow>
|
||||||
|
|||||||
@@ -0,0 +1,262 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import api from '@/api'
|
||||||
|
import { useSetupWizard } from '@/composables/useSetupWizard'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const { wizardData, validationErrors } = useSetupWizard()
|
||||||
|
|
||||||
|
const llmModels = ref<string[]>([])
|
||||||
|
const loadingModels = ref(false)
|
||||||
|
|
||||||
|
const providerItems = [
|
||||||
|
{ title: 'OpenAI', value: 'openai' },
|
||||||
|
{ title: 'Google', value: 'google' },
|
||||||
|
{ title: 'DeepSeek', value: 'deepseek' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const jobIntervalItems = computed(() => [
|
||||||
|
{ title: t('setting.system.aiAgentJobIntervalDisabled'), value: 0 },
|
||||||
|
{ title: t('setting.system.aiAgentJobInterval1h'), value: 1 },
|
||||||
|
{ title: t('setting.system.aiAgentJobInterval3h'), value: 3 },
|
||||||
|
{ title: t('setting.system.aiAgentJobInterval6h'), value: 6 },
|
||||||
|
{ title: t('setting.system.aiAgentJobInterval12h'), value: 12 },
|
||||||
|
{ title: t('setting.system.aiAgentJobInterval24h'), value: 24 },
|
||||||
|
{ title: t('setting.system.aiAgentJobInterval1w'), value: 168 },
|
||||||
|
{ title: t('setting.system.aiAgentJobInterval1M'), value: 720 },
|
||||||
|
])
|
||||||
|
|
||||||
|
async function loadLlmModels() {
|
||||||
|
if (!wizardData.value.agent.provider || !wizardData.value.agent.apiKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingModels.value = true
|
||||||
|
try {
|
||||||
|
const result: { [key: string]: any } = await api.get('system/llm-models', {
|
||||||
|
params: {
|
||||||
|
provider: wizardData.value.agent.provider,
|
||||||
|
api_key: wizardData.value.agent.apiKey,
|
||||||
|
base_url: wizardData.value.agent.baseUrl,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
llmModels.value = result.data || []
|
||||||
|
if (!wizardData.value.agent.model && llmModels.value.length > 0) {
|
||||||
|
wizardData.value.agent.model = llmModels.value[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Load LLM models failed:', error)
|
||||||
|
} finally {
|
||||||
|
loadingModels.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (wizardData.value.agent.enabled && wizardData.value.agent.apiKey) {
|
||||||
|
loadLlmModels()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<VCard variant="outlined">
|
||||||
|
<VCardText>
|
||||||
|
<div class="text-center mb-6">
|
||||||
|
<h3 class="text-h4 mb-2">{{ t('setupWizard.agent.title') }}</h3>
|
||||||
|
<p class="text-body-1 text-medium-emphasis">{{ t('setupWizard.agent.description') }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<VRow>
|
||||||
|
<VCol cols="12">
|
||||||
|
<VAlert type="info" variant="tonal" class="mb-4">
|
||||||
|
<VAlertTitle>{{ t('setupWizard.agent.info') }}</VAlertTitle>
|
||||||
|
{{ t('setupWizard.agent.infoDesc') }}
|
||||||
|
</VAlert>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12">
|
||||||
|
<VSwitch
|
||||||
|
v-model="wizardData.agent.enabled"
|
||||||
|
:label="t('setting.system.aiAgentEnable')"
|
||||||
|
:hint="t('setting.system.aiAgentEnableHint')"
|
||||||
|
persistent-hint
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<template v-if="wizardData.agent.enabled">
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VSwitch
|
||||||
|
v-model="wizardData.agent.global"
|
||||||
|
:label="t('setting.system.aiAgentGlobal')"
|
||||||
|
:hint="t('setting.system.aiAgentGlobalHint')"
|
||||||
|
persistent-hint
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VSwitch
|
||||||
|
v-model="wizardData.agent.verbose"
|
||||||
|
:label="t('setting.system.aiAgentVerbose')"
|
||||||
|
:hint="t('setting.system.aiAgentVerboseHint')"
|
||||||
|
persistent-hint
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12" md="4">
|
||||||
|
<VSwitch
|
||||||
|
v-model="wizardData.agent.supportImageInput"
|
||||||
|
:label="t('setting.system.llmSupportImageInput')"
|
||||||
|
:hint="t('setting.system.llmSupportImageInputHint')"
|
||||||
|
persistent-hint
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="wizardData.agent.provider"
|
||||||
|
:label="t('setting.system.llmProvider')"
|
||||||
|
:hint="t('setting.system.llmProviderHint')"
|
||||||
|
:items="providerItems"
|
||||||
|
:error="validationErrors.agent.provider"
|
||||||
|
:error-messages="validationErrors.agent.provider ? [t('setupWizard.agent.providerRequired')] : []"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-robot-outline"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="wizardData.agent.baseUrl"
|
||||||
|
:label="t('setting.system.llmBaseUrl')"
|
||||||
|
:hint="t('setting.system.llmBaseUrlHint')"
|
||||||
|
placeholder="https://api.deepseek.com"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-link-variant"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="wizardData.agent.apiKey"
|
||||||
|
:label="t('setting.system.llmApiKey')"
|
||||||
|
:hint="t('setting.system.llmApiKeyHint')"
|
||||||
|
:placeholder="t('setting.system.llmApiKeyPlaceholder')"
|
||||||
|
:error="validationErrors.agent.apiKey"
|
||||||
|
:error-messages="validationErrors.agent.apiKey ? [t('setupWizard.agent.apiKeyRequired')] : []"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-key-variant"
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VCombobox
|
||||||
|
v-model="wizardData.agent.model"
|
||||||
|
:label="t('setting.system.llmModel')"
|
||||||
|
:hint="t('setting.system.llmModelHint')"
|
||||||
|
:items="llmModels"
|
||||||
|
:loading="loadingModels"
|
||||||
|
:error="validationErrors.agent.model"
|
||||||
|
:error-messages="validationErrors.agent.model ? [t('setupWizard.agent.modelRequired')] : []"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-brain"
|
||||||
|
>
|
||||||
|
<template #append-inner>
|
||||||
|
<VBtn
|
||||||
|
variant="text"
|
||||||
|
icon="mdi-refresh"
|
||||||
|
size="small"
|
||||||
|
:disabled="!wizardData.agent.provider || !wizardData.agent.apiKey"
|
||||||
|
@click="loadLlmModels"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</VCombobox>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model.number="wizardData.agent.maxContextTokens"
|
||||||
|
:label="t('setting.system.llmMaxContextTokens')"
|
||||||
|
:hint="t('setting.system.llmMaxContextTokensHint')"
|
||||||
|
:error="validationErrors.agent.maxContextTokens"
|
||||||
|
:error-messages="
|
||||||
|
validationErrors.agent.maxContextTokens ? [t('setupWizard.agent.maxContextTokensRequired')] : []
|
||||||
|
"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-counter"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="wizardData.agent.jobInterval"
|
||||||
|
:label="t('setting.system.aiAgentJobInterval')"
|
||||||
|
:hint="t('setting.system.aiAgentJobIntervalHint')"
|
||||||
|
:items="jobIntervalItems"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-timer-outline"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12">
|
||||||
|
<VSwitch
|
||||||
|
v-model="wizardData.agent.retryTransfer"
|
||||||
|
:label="t('setting.system.aiAgentRetryTransfer')"
|
||||||
|
:hint="t('setting.system.aiAgentRetryTransferHint')"
|
||||||
|
persistent-hint
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12">
|
||||||
|
<VSwitch
|
||||||
|
v-model="wizardData.agent.recommendEnabled"
|
||||||
|
:label="t('setting.system.aiRecommendEnabled')"
|
||||||
|
:hint="t('setting.system.aiRecommendEnabledHint')"
|
||||||
|
persistent-hint
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol v-if="wizardData.agent.recommendEnabled" cols="12" md="6">
|
||||||
|
<VTextarea
|
||||||
|
v-model="wizardData.agent.recommendUserPreference"
|
||||||
|
:label="t('setting.system.aiRecommendUserPreference')"
|
||||||
|
:hint="t('setting.system.aiRecommendUserPreferenceHint')"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-account-heart-outline"
|
||||||
|
rows="2"
|
||||||
|
auto-grow
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol v-if="wizardData.agent.recommendEnabled" cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model.number="wizardData.agent.recommendMaxItems"
|
||||||
|
:label="t('setting.system.aiRecommendMaxItems')"
|
||||||
|
:hint="t('setting.system.aiRecommendMaxItemsHint')"
|
||||||
|
:error="validationErrors.agent.recommendMaxItems"
|
||||||
|
:error-messages="
|
||||||
|
validationErrors.agent.recommendMaxItems ? [t('setupWizard.agent.recommendMaxItemsRequired')] : []
|
||||||
|
"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-format-list-numbered"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</template>
|
||||||
|
</VRow>
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
</template>
|
||||||
@@ -37,6 +37,11 @@ const confirmPasswordErrorMessage = computed(() => {
|
|||||||
return ''
|
return ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const recognizeSourceItems = [
|
||||||
|
{ title: 'TheMovieDb', value: 'themoviedb' },
|
||||||
|
{ title: '豆瓣', value: 'douban' },
|
||||||
|
]
|
||||||
|
|
||||||
// API Token验证
|
// API Token验证
|
||||||
const apiTokenError = computed(() => {
|
const apiTokenError = computed(() => {
|
||||||
return !wizardData.value.basic.apiToken && hasErrors.value
|
return !wizardData.value.basic.apiToken && hasErrors.value
|
||||||
@@ -119,6 +124,26 @@ const usernameErrorMessage = computed(() => {
|
|||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VSelect
|
||||||
|
v-model="wizardData.basic.recognizeSource"
|
||||||
|
:label="t('setupWizard.basic.recognizeSource')"
|
||||||
|
:hint="t('setupWizard.basic.recognizeSourceHint')"
|
||||||
|
:items="recognizeSourceItems"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-database-search"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField
|
||||||
|
v-model="wizardData.basic.ocrHost"
|
||||||
|
:label="t('setting.system.ocrHost')"
|
||||||
|
:hint="t('setting.system.ocrHostHint')"
|
||||||
|
placeholder="https://movie-pilot.org"
|
||||||
|
persistent-hint
|
||||||
|
prepend-inner-icon="mdi-text-recognition"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField
|
<VTextField
|
||||||
v-model="wizardData.basic.proxyHost"
|
v-model="wizardData.basic.proxyHost"
|
||||||
|
|||||||
@@ -345,6 +345,10 @@ const notificationTypes = [
|
|||||||
v-model="wizardData.notification.config.QQ_APP_ID"
|
v-model="wizardData.notification.config.QQ_APP_ID"
|
||||||
:label="t('notification.qqbot.appId')"
|
:label="t('notification.qqbot.appId')"
|
||||||
:hint="t('notification.qqbot.appIdHint')"
|
:hint="t('notification.qqbot.appIdHint')"
|
||||||
|
:error="validationErrors.notification.QQ_APP_ID"
|
||||||
|
:error-messages="
|
||||||
|
validationErrors.notification.QQ_APP_ID ? [t('notification.qqbot.appIdRequired')] : []
|
||||||
|
"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
prepend-inner-icon="mdi-application"
|
prepend-inner-icon="mdi-application"
|
||||||
/>
|
/>
|
||||||
@@ -354,6 +358,12 @@ const notificationTypes = [
|
|||||||
v-model="wizardData.notification.config.QQ_APP_SECRET"
|
v-model="wizardData.notification.config.QQ_APP_SECRET"
|
||||||
:label="t('notification.qqbot.appSecret')"
|
:label="t('notification.qqbot.appSecret')"
|
||||||
:hint="t('notification.qqbot.appSecretHint')"
|
:hint="t('notification.qqbot.appSecretHint')"
|
||||||
|
:error="validationErrors.notification.QQ_APP_SECRET"
|
||||||
|
:error-messages="
|
||||||
|
validationErrors.notification.QQ_APP_SECRET
|
||||||
|
? [t('notification.qqbot.appSecretRequired')]
|
||||||
|
: []
|
||||||
|
"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
prepend-inner-icon="mdi-key"
|
prepend-inner-icon="mdi-key"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user