更新国际化支持:在多个组件中引入 vue-i18n,优化文本翻译和结构

This commit is contained in:
jxxghp
2025-04-27 22:19:57 +08:00
parent 7a3d566875
commit 71787ece64
10 changed files with 298 additions and 244 deletions
+51 -38
View File
@@ -387,6 +387,7 @@ export default {
currentVersion: 'Current Version', currentVersion: 'Current Version',
viewChangelog: 'View Changelog', viewChangelog: 'View Changelog',
changelog: 'Changelog', changelog: 'Changelog',
dataDirectory: '/moviepilot',
}, },
system: { system: {
basicSettings: 'Basic Settings', basicSettings: 'Basic Settings',
@@ -439,6 +440,11 @@ export default {
copyFailed: 'Copy failed: browser may not support or user blocked!', copyFailed: 'Copy failed: browser may not support or user blocked!',
copyError: 'Copy failed!', copyError: 'Copy failed!',
reloading: 'Applying configuration...', reloading: 'Applying configuration...',
qbittorrent: 'Qbittorrent',
transmission: 'Transmission',
emby: 'Emby',
jellyfin: 'Jellyfin',
plex: 'Plex',
}, },
site: { site: {
siteSync: 'Site Synchronization', siteSync: 'Site Synchronization',
@@ -517,6 +523,11 @@ export default {
manualProcess: 'Manual Process', manualProcess: 'Manual Process',
plugin: 'Plugin', plugin: 'Plugin',
other: 'Other', other: 'Other',
telegram: 'Telegram',
slack: 'Slack',
synologyChat: 'SynologyChat',
voceChat: 'VoceChat',
webPush: 'WebPush',
}, },
words: { words: {
customIdentifiers: 'Custom Identifiers', customIdentifiers: 'Custom Identifiers',
@@ -586,11 +597,16 @@ export default {
downloadUser: 'Remote Search Auto Download User List', downloadUser: 'Remote Search Auto Download User List',
downloadUserHint: downloadUserHint:
'Whether to automatically download when searching with Telegram, WeChat, etc., comma separated, set to all to represent all users auto-download', 'Whether to automatically download when searching with Telegram, WeChat, etc., comma separated, set to all to represent all users auto-download',
downloadUserPlaceholder: 'UserID1,UserID2',
multipleNameSearch: 'Multiple Name Resource Search', multipleNameSearch: 'Multiple Name Resource Search',
multipleNameSearchHint: multipleNameSearchHint:
'Search site resources using multiple names (Chinese, English, etc.) and merge search results, will increase site access frequency', 'Search site resources using multiple names (Chinese, English, etc.) and merge search results, will increase site access frequency',
downloadSubtitle: 'Download Site Subtitles', downloadSubtitle: 'Download Site Subtitles',
downloadSubtitleHint: 'Check if site resources have separate subtitle files and download them automatically', downloadSubtitleHint: 'Check if site resources have separate subtitle files and download them automatically',
mediaSource: 'Media Search Data Source',
mediaSourceHint: 'Data sources and sorting used when searching for media information',
filterRuleGroupHint: 'Filter results by selected filter rule groups when searching for media information',
downloadLabelPlaceholder: 'MOVIEPILOT',
}, },
directory: { directory: {
storage: 'Storage', storage: 'Storage',
@@ -620,24 +636,29 @@ export default {
priorityRuleGroups: 'Priority Rule Groups', priorityRuleGroups: 'Priority Rule Groups',
priorityRuleGroupsDesc: 'Preset priority rule groups for use in search and subscription.', priorityRuleGroupsDesc: 'Preset priority rule groups for use in search and subscription.',
downloadRules: 'Download Rules', downloadRules: 'Download Rules',
downloadRulesDesc: 'Prioritize downloads when multiple resources match.', downloadRulesDesc: 'Choose the best option when multiple resources are matched.',
resourcePriority: 'Resource Priority',
sitePriority: 'Site Priority',
siteUpload: 'Site Upload',
resourceSeeder: 'Resource Seeders',
emptyIdError: 'A rule has an empty ID, cannot save. Please modify!',
emptyNameError: 'A rule has an empty name, cannot save. Please modify!',
duplicateIdError: 'Duplicate rule IDs exist! Cannot save, please modify!',
duplicateNameError: 'Duplicate rule names exist! Cannot save, please modify!',
customRuleSaveSuccess: 'Custom rules saved successfully',
customRuleSaveFailed: 'Failed to save custom rules!',
emptyGroupNameError: 'A rule group has an empty name! Cannot save, please modify!',
duplicateGroupNameError: 'Duplicate rule group names exist! Cannot save, please modify!',
ruleGroupSaveSuccess: 'Priority rule groups saved successfully',
ruleGroupSaveFailed: 'Failed to save priority rule groups!',
customRuleCopySuccess: 'Custom rules copied to clipboard!',
customRuleCopyFailed: 'Failed to copy custom rules: browser may not support or user blocked!',
customRuleCopyError: 'Failed to copy custom rules!',
ruleGroupCopySuccess: 'Priority rule groups copied to clipboard!',
ruleGroupCopyFailed: 'Failed to copy priority rule groups: browser may not support or user blocked!',
ruleGroupCopyError: 'Failed to copy priority rule groups!',
currentPriorityRules: 'Current Download Priority Rules', currentPriorityRules: 'Current Download Priority Rules',
currentPriorityRulesHint: 'Higher priority for items at the front, unselected items are not included in sorting', currentPriorityRulesHint: 'Higher priority for items at the front, unselected items are not included in sorting',
saveSuccess: 'Custom rules saved successfully',
saveFailed: 'Failed to save custom rules!',
groupSaveSuccess: 'Priority rule groups saved successfully',
groupSaveFailed: 'Failed to save priority rule groups!',
prioritySaveSuccess: 'Priority rules saved successfully',
prioritySaveFailed: 'Failed to save priority rules!',
emptyRuleId: 'Rules with empty IDs exist! Cannot save, please modify!',
emptyRuleName: 'Rules with empty names exist! Cannot save, please modify!',
duplicateRuleId: 'Duplicate rule IDs exist! Cannot save, please modify!',
duplicateRuleName: 'Duplicate rule names exist! Cannot save, please modify!',
emptyGroupName: 'Rule groups with empty names exist! Cannot save, please modify!',
duplicateGroupName: 'Duplicate rule group names exist! Cannot save, please modify!',
copySuccess: 'Copied to clipboard!',
copyFailed: 'Copy failed: browser may not support or user blocked!',
copyError: 'Copy failed!',
importCustomRules: 'Import Custom Rules', importCustomRules: 'Import Custom Rules',
importRuleGroups: 'Import Priority Rule Groups', importRuleGroups: 'Import Priority Rule Groups',
importFailed: 'Failed to import rules! Cannot parse input data!', importFailed: 'Failed to import rules! Cannot parse input data!',
@@ -645,33 +666,27 @@ export default {
duplicateValue: 'Duplicate values exist', duplicateValue: 'Duplicate values exist',
importNoId: 'Import failed! Found rules without IDs, may belong to priority rule groups!', importNoId: 'Import failed! Found rules without IDs, may belong to priority rule groups!',
importHasId: 'Import failed! Found rules with IDs, may belong to custom rules!', importHasId: 'Import failed! Found rules with IDs, may belong to custom rules!',
torrentPriority: {
torrent: 'Resource Priority',
site: 'Site Priority',
upload: 'Site Upload Amount',
seeder: 'Resource Seeders',
},
}, },
scheduler: { scheduler: {
scheduledTasks: 'Scheduled Tasks', title: 'Scheduled Jobs',
scheduledTasksDesc: subtitle:
'Includes system built-in services and plugin-provided services. Manual execution will not affect the normal schedule of tasks.', "Includes built-in system services and plugin services. Manual execution will not affect the job's normal schedule.",
provider: 'Provider', provider: 'Provider',
taskName: 'Task Name', taskName: 'Task Name',
taskStatus: 'Task Status', taskStatus: 'Task Status',
nextRunTime: 'Next Run Time', nextRunTime: 'Next Run Time',
execute: 'Execute', execute: 'Execute',
noServices: 'No background services', noService: 'No background services',
submitSuccess: 'Task execution request submitted successfully!', running: 'Running',
status: { stopped: 'Stopped',
running: 'Running', waiting: 'Waiting',
stopped: 'Stopped', executeSuccess: 'Scheduled job execution request submitted successfully!',
waiting: 'Waiting',
},
}, },
subscribe: { subscribe: {
basicSettings: 'Basic Settings', basicSettings: 'Basic Settings',
basicSettingsDesc: 'Set subscription mode, cycle and other basic settings', basicSettingsDesc: 'Set subscription mode, cycle and other basic settings',
subscribeSites: 'Subscribe Sites',
subscribeSitesDesc: 'Only selected sites will be used in subscriptions.',
mode: 'Subscription Mode', mode: 'Subscription Mode',
modeHint: 'Auto: automatically crawl site homepage, Site RSS: subscribe via site RSS link', modeHint: 'Auto: automatically crawl site homepage, Site RSS: subscribe via site RSS link',
rssInterval: 'Site RSS Interval', rssInterval: 'Site RSS Interval',
@@ -684,12 +699,6 @@ export default {
timedSearchHint: 'Search all sites every 24 hours to supplement resources that may be missed by subscription', timedSearchHint: 'Search all sites every 24 hours to supplement resources that may be missed by subscription',
checkLocalMedia: 'Check Local Media Library', checkLocalMedia: 'Check Local Media Library',
checkLocalMediaHint: 'Check if resources exist on storage disk to avoid duplicate downloads', checkLocalMediaHint: 'Check if resources exist on storage disk to avoid duplicate downloads',
subscribeSites: 'Subscription Sites',
subscribeSitesDesc: 'Only selected sites will be used in subscription.',
saveSuccess: 'Subscription sites saved successfully',
saveFailed: 'Failed to save subscription sites!',
settingsSaveSuccess: 'Subscription basic settings saved successfully',
settingsSaveFailed: 'Failed to save subscription basic settings!',
modes: { modes: {
auto: 'Auto', auto: 'Auto',
rss: 'Site RSS', rss: 'Site RSS',
@@ -703,6 +712,10 @@ export default {
hour12: '12 hours', hour12: '12 hours',
day1: '1 day', day1: '1 day',
}, },
saveSuccess: 'Subscription sites saved successfully',
saveFailed: 'Failed to save subscription sites!',
settingsSaveSuccess: 'Subscription basic settings saved successfully',
settingsSaveFailed: 'Failed to save subscription basic settings!',
}, },
}, },
} }
+91 -77
View File
@@ -326,47 +326,6 @@ export default {
site: { site: {
noSites: '没有站点', noSites: '没有站点',
sitesWillBeShownHere: '已添加并支持的站点将会在这里显示。', sitesWillBeShownHere: '已添加并支持的站点将会在这里显示。',
siteSync: '站点同步',
siteSyncDesc: '从CookieCloud快速同步站点数据。',
enableLocalCookieCloud: '启用本地CookieCloud服务器',
enableLocalCookieCloudHint: '使用内建CookieCloud服务同步站点数据,服务地址为:http://localhost:3000/cookiecloud',
serviceAddress: '服务地址',
serviceAddressPlaceholder: 'https://movie-pilot.org/cookiecloud',
serviceAddressHint: '远端CookieCloud服务地址,格式:https://movie-pilot.org/cookiecloud',
userKey: '用户KEY',
userKeyHint: 'CookieCloud浏览器插件生成的用户KEY',
e2ePassword: '端对端加密密码',
e2ePasswordHint: 'CookieCloud浏览器插件生成的端对端加密密码',
autoSyncInterval: '自动同步间隔',
autoSyncIntervalHint: '从CookieCloud服务器自动同步站点Cookie到MoviePilot的时间间隔',
syncBlacklist: '同步域名黑名单',
syncBlacklistPlaceholder: '多个域名,分割',
syncBlacklistHint: 'CookieCloud同步域名黑名单,多个域名,分割',
userAgent: '浏览器User-Agent',
userAgentHint: 'CookieCloud插件所在的浏览器的User-Agent',
siteDataRefresh: '站点数据刷新',
siteDataRefreshInterval: '站点数据刷新间隔',
siteDataRefreshIntervalHint: '刷新站点用户上传下载等数据的时间间隔',
readSiteMessage: '阅读站点消息',
readSiteMessageHint: '刷新数据时读取站点消息并发送通知',
siteReset: '站点重置',
confirmReset: '确认删除所有站点数据并重新同步。',
confirmResetHint: '删除所有站点数据并重新从CookieCloud同步,操作请先清空涉及站点的相关设置。',
resetSites: '重置站点数据',
resettingSites: '正在重置...',
syncInterval: {
hourly: '每小时',
every6Hours: '每6小时',
every12Hours: '每12小时',
daily: '每天',
weekly: '每周',
monthly: '每月',
never: '永不',
},
saveSuccess: '保存站点设置成功',
saveFailed: '站点设置保存失败!',
resetSuccess: '站点重置成功,请等待CookieCloud同步完成!',
resetFailed: '站点重置失败!',
}, },
message: { message: {
loadMore: '加载更多', loadMore: '加载更多',
@@ -426,6 +385,7 @@ export default {
currentVersion: '当前版本', currentVersion: '当前版本',
viewChangelog: '查看变更日志', viewChangelog: '查看变更日志',
changelog: '变更日志', changelog: '变更日志',
dataDirectory: '/moviepilot',
}, },
system: { system: {
basicSettings: '基础设置', basicSettings: '基础设置',
@@ -478,6 +438,54 @@ export default {
copyFailed: '复制失败:可能是浏览器不支持或被用户阻止!', copyFailed: '复制失败:可能是浏览器不支持或被用户阻止!',
copyError: '复制失败!', copyError: '复制失败!',
reloading: '正在应用配置...', reloading: '正在应用配置...',
qbittorrent: 'Qbittorrent',
transmission: 'Transmission',
emby: 'Emby',
jellyfin: 'Jellyfin',
plex: 'Plex',
},
site: {
siteSync: '站点同步',
siteSyncDesc: '从CookieCloud快速同步站点数据。',
enableLocalCookieCloud: '启用本地CookieCloud服务器',
enableLocalCookieCloudHint: '使用内建CookieCloud服务同步站点数据,服务地址为:http://localhost:3000/cookiecloud',
serviceAddress: '服务地址',
serviceAddressPlaceholder: 'https://movie-pilot.org/cookiecloud',
serviceAddressHint: '远端CookieCloud服务地址,格式:https://movie-pilot.org/cookiecloud',
userKey: '用户KEY',
userKeyHint: 'CookieCloud浏览器插件生成的用户KEY',
e2ePassword: '端对端加密密码',
e2ePasswordHint: 'CookieCloud浏览器插件生成的端对端加密密码',
autoSyncInterval: '自动同步间隔',
autoSyncIntervalHint: '从CookieCloud服务器自动同步站点Cookie到MoviePilot的时间间隔',
syncBlacklist: '同步域名黑名单',
syncBlacklistPlaceholder: '多个域名,分割',
syncBlacklistHint: 'CookieCloud同步域名黑名单,多个域名,分割',
userAgent: '浏览器User-Agent',
userAgentHint: 'CookieCloud插件所在的浏览器的User-Agent',
siteDataRefresh: '站点数据刷新',
siteDataRefreshInterval: '站点数据刷新间隔',
siteDataRefreshIntervalHint: '刷新站点用户上传下载等数据的时间间隔',
readSiteMessage: '阅读站点消息',
readSiteMessageHint: '刷新数据时读取站点消息并发送通知',
siteReset: '站点重置',
confirmReset: '确认删除所有站点数据并重新同步。',
confirmResetHint: '删除所有站点数据并重新从CookieCloud同步,操作请先清空涉及站点的相关设置。',
resetSites: '重置站点数据',
resettingSites: '正在重置...',
syncInterval: {
hourly: '每小时',
every6Hours: '每6小时',
every12Hours: '每12小时',
daily: '每天',
weekly: '每周',
monthly: '每月',
never: '永不',
},
saveSuccess: '保存站点设置成功',
saveFailed: '站点设置保存失败!',
resetSuccess: '站点重置成功,请等待CookieCloud同步完成!',
resetFailed: '站点重置失败!',
}, },
notification: { notification: {
channels: '通知渠道', channels: '通知渠道',
@@ -510,6 +518,11 @@ export default {
manualProcess: '手动处理', manualProcess: '手动处理',
plugin: '插件', plugin: '插件',
other: '其它', other: '其它',
telegram: 'Telegram',
slack: 'Slack',
synologyChat: 'SynologyChat',
voceChat: 'VoceChat',
webPush: 'WebPush',
}, },
words: { words: {
customIdentifiers: '自定义识别词', customIdentifiers: '自定义识别词',
@@ -572,12 +585,16 @@ export default {
tvCategory: '电视剧', tvCategory: '电视剧',
animeCategory: '动漫', animeCategory: '动漫',
downloadUser: '远程搜索自动下载用户', downloadUser: '远程搜索自动下载用户',
downloadUserHint:
'使用Telegram、微信等远程搜索时是否自动下载,多个用户使用英文逗号分隔,设置为all代表所有用户自动下载',
multipleNameSearch: '多名称资源搜索', multipleNameSearch: '多名称资源搜索',
multipleNameSearchHint: '使用多个名称(中文、英文等)搜索站点资源并合并搜索结果,会增加站点访问频率', multipleNameSearchHint: '使用多个名称(中文、英文等)搜索站点资源并合并搜索结果,会增加站点访问频率',
downloadSubtitle: '下载站点字幕', downloadSubtitle: '下载站点字幕',
downloadSubtitleHint: '检查站点资源是否有单独的字幕文件并自动下载', downloadSubtitleHint: '检查站点资源是否有单独的字幕文件并自动下载',
mediaSource: '媒体搜索数据源',
mediaSourceHint: '搜索媒体信息时使用的数据源以及排序',
filterRuleGroupHint: '搜索媒体信息时按选定的过滤规则组对结果进行过滤',
downloadUserPlaceholder: '用户ID1,用户ID2',
downloadUserHint: '使用Telegram、微信等搜索时是否自动下载,使用逗号分割,设置为 all 代表所有用户自动择优下载',
downloadLabelPlaceholder: 'MOVIEPILOT',
}, },
directory: { directory: {
storage: '存储', storage: '存储',
@@ -607,23 +624,28 @@ export default {
priorityRuleGroupsDesc: '预设优先级规则组,以便在搜索和订阅中使用。', priorityRuleGroupsDesc: '预设优先级规则组,以便在搜索和订阅中使用。',
downloadRules: '下载规则', downloadRules: '下载规则',
downloadRulesDesc: '同时命中多个资源时择优下载。', downloadRulesDesc: '同时命中多个资源时择优下载。',
resourcePriority: '资源优先级',
sitePriority: '站点优先级',
siteUpload: '站点上传量',
resourceSeeder: '资源做种数',
emptyIdError: '存在空ID的规则,无法保存,请修改!',
emptyNameError: '存在空名字的规则,无法保存,请修改!',
duplicateIdError: '存在重复规则ID!无法保存,请修改!',
duplicateNameError: '存在重复规则名称!无法保存,请修改!',
customRuleSaveSuccess: '自定义规则保存成功',
customRuleSaveFailed: '自定义规则保存失败!',
emptyGroupNameError: '存在空名字的规则组!无法保存,请修改!',
duplicateGroupNameError: '存在重复规则组名称!无法保存,请修改!',
ruleGroupSaveSuccess: '优先级规则组保存成功',
ruleGroupSaveFailed: '优先级规则组保存失败!',
customRuleCopySuccess: '自定义规则已复制到剪贴板!',
customRuleCopyFailed: '自定义规则复制失败:可能是浏览器不支持或被用户阻止!',
customRuleCopyError: '自定义规则复制失败!',
ruleGroupCopySuccess: '优先级规则组已复制到剪贴板!',
ruleGroupCopyFailed: '优先级规则组复制失败:可能是浏览器不支持或被用户阻止!',
ruleGroupCopyError: '优先级规则组复制失败!',
currentPriorityRules: '当前使用下载优先规则', currentPriorityRules: '当前使用下载优先规则',
currentPriorityRulesHint: '排在前面的优先级越高,未选择的项不纳入排序', currentPriorityRulesHint: '排在前面的优先级越高,未选择的项不纳入排序',
saveSuccess: '自定义规则保存成功',
saveFailed: '自定义规则保存失败!',
groupSaveSuccess: '优先级规则组保存成功',
groupSaveFailed: '优先级规则组保存失败!',
prioritySaveSuccess: '优先规则保存成功',
prioritySaveFailed: '优先规则保存失败!',
emptyRuleId: '存在空ID的规则,无法保存,请修改!',
emptyRuleName: '存在空名字的规则,无法保存,请修改!',
duplicateRuleId: '存在重复规则ID!无法保存,请修改!',
duplicateRuleName: '存在重复规则名称!无法保存,请修改!',
emptyGroupName: '存在空名字的规则组!无法保存,请修改!',
duplicateGroupName: '存在重复规则组名称!无法保存,请修改!',
copySuccess: '已复制到剪贴板!',
copyFailed: '复制失败:可能是浏览器不支持或被用户阻止!',
copyError: '复制失败!',
importCustomRules: '导入自定义规则', importCustomRules: '导入自定义规则',
importRuleGroups: '导入优先级规则组', importRuleGroups: '导入优先级规则组',
importFailed: '导入规则失败!无法解析输入的数据!', importFailed: '导入规则失败!无法解析输入的数据!',
@@ -631,12 +653,6 @@ export default {
duplicateValue: '存在重名值', duplicateValue: '存在重名值',
importNoId: '导入失败!发现有规则不存在ID,可能属于优先级规则组!', importNoId: '导入失败!发现有规则不存在ID,可能属于优先级规则组!',
importHasId: '导入失败!发现有规则存在相同ID,可能属于自定义规则!', importHasId: '导入失败!发现有规则存在相同ID,可能属于自定义规则!',
torrentPriority: {
torrent: '资源优先级',
site: '站点优先级',
upload: '站点上传量',
seeder: '资源做种数',
},
}, },
scheduler: { scheduler: {
scheduledTasks: '定时作业', scheduledTasks: '定时作业',
@@ -646,17 +662,17 @@ export default {
taskStatus: '任务状态', taskStatus: '任务状态',
nextRunTime: '下一次执行时间', nextRunTime: '下一次执行时间',
execute: '执行', execute: '执行',
noServices: '没有后台服务', noService: '没有后台服务',
submitSuccess: '定时作业执行请求提交成功!', running: '正在运行',
status: { stopped: '已停止',
running: '正在运行', waiting: '等待',
stopped: '已停止', executeSuccess: '定时作业执行请求提交成功!',
waiting: '等待',
},
}, },
subscribe: { subscribe: {
basicSettings: '基础设置', basicSettings: '基础设置',
basicSettingsDesc: '设定订阅模式、周期等基础设置', basicSettingsDesc: '设定订阅模式、周期等基础设置',
subscribeSites: '订阅站点',
subscribeSitesDesc: '只有选中的站点才会在订阅中使用。',
mode: '订阅模式', mode: '订阅模式',
modeHint: '自动:自动爬取站点首页,站点RSS:通过站点RSS链接订阅', modeHint: '自动:自动爬取站点首页,站点RSS:通过站点RSS链接订阅',
rssInterval: '站点RSS周期', rssInterval: '站点RSS周期',
@@ -669,12 +685,6 @@ export default {
timedSearchHint: '每隔24小时全站搜索,以补全订阅可能漏掉的资源', timedSearchHint: '每隔24小时全站搜索,以补全订阅可能漏掉的资源',
checkLocalMedia: '检查本地媒体库资源', checkLocalMedia: '检查本地媒体库资源',
checkLocalMediaHint: '检查存储盘是否存在资源,以避免重复下载', checkLocalMediaHint: '检查存储盘是否存在资源,以避免重复下载',
subscribeSites: '订阅站点',
subscribeSitesDesc: '只有选中的站点才会在订阅中使用。',
saveSuccess: '订阅站点保存成功',
saveFailed: '订阅站点保存失败!',
settingsSaveSuccess: '订阅基础设置保存成功',
settingsSaveFailed: '订阅基础设置保存失败!',
modes: { modes: {
auto: '自动', auto: '自动',
rss: '站点RSS', rss: '站点RSS',
@@ -688,6 +698,10 @@ export default {
hour12: '12小时', hour12: '12小时',
day1: '1天', day1: '1天',
}, },
saveSuccess: '订阅站点保存成功',
saveFailed: '订阅站点保存失败!',
settingsSaveSuccess: '订阅基础设置保存成功',
settingsSaveFailed: '订阅基础设置保存失败!',
}, },
}, },
} }
+3 -1
View File
@@ -140,7 +140,9 @@ onMounted(() => {
<div class="max-w-6xl py-4 sm:grid sm:grid-cols-3 sm:gap-4"> <div class="max-w-6xl py-4 sm:grid sm:grid-cols-3 sm:gap-4">
<dt class="block text-sm font-bold">{{ t('setting.about.dataDir') }}</dt> <dt class="block text-sm font-bold">{{ t('setting.about.dataDir') }}</dt>
<dd class="flex text-sm sm:col-span-2 sm:mt-0"> <dd class="flex text-sm sm:col-span-2 sm:mt-0">
<span class="flex-grow undefined"><code>/moviepilot</code></span> <span class="flex-grow undefined"
><code>{{ t('setting.about.dataDirectory') }}</code></span
>
</dd> </dd>
</div> </div>
</div> </div>
+19 -16
View File
@@ -8,6 +8,9 @@ import { TransferDirectoryConf, StorageConf } from '@/api/types'
import DirectoryCard from '@/components/cards/DirectoryCard.vue' import DirectoryCard from '@/components/cards/DirectoryCard.vue'
import StorageCard from '@/components/cards/StorageCard.vue' import StorageCard from '@/components/cards/StorageCard.vue'
import ProgressDialog from '@/components/dialog/ProgressDialog.vue' import ProgressDialog from '@/components/dialog/ProgressDialog.vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
// 所有下载目录 // 所有下载目录
const directories = ref<TransferDirectoryConf[]>([]) const directories = ref<TransferDirectoryConf[]>([])
@@ -195,8 +198,8 @@ onMounted(() => {
<VCol cols="12"> <VCol cols="12">
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle>存储</VCardTitle> <VCardTitle>{{ t('setting.directory.storage') }}</VCardTitle>
<VCardSubtitle>设置本地或网盘存储</VCardSubtitle> <VCardSubtitle>{{ t('setting.directory.storageDesc') }}</VCardSubtitle>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<draggable <draggable
@@ -214,7 +217,7 @@ onMounted(() => {
<VCardText> <VCardText>
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4"> <div class="d-flex flex-wrap gap-4 mt-4">
<VBtn type="submit" class="me-2" @click="saveStorages"> 保存 </VBtn> <VBtn type="submit" class="me-2" @click="saveStorages"> {{ t('common.save') }} </VBtn>
</div> </div>
</VForm> </VForm>
</VCardText> </VCardText>
@@ -225,8 +228,8 @@ onMounted(() => {
<VCol cols="12"> <VCol cols="12">
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle>目录</VCardTitle> <VCardTitle>{{ t('setting.directory.directory') }}</VCardTitle>
<VCardSubtitle>设置媒体文件整理目录结构按先后顺序依次匹配</VCardSubtitle> <VCardSubtitle>{{ t('setting.directory.directoryDesc') }}</VCardSubtitle>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<draggable <draggable
@@ -250,7 +253,7 @@ onMounted(() => {
<VCardText> <VCardText>
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4"> <div class="d-flex flex-wrap gap-4 mt-4">
<VBtn type="submit" @click="saveDirectories"> 保存 </VBtn> <VBtn type="submit" @click="saveDirectories"> {{ t('common.save') }} </VBtn>
<VBtn color="success" variant="tonal" @click="addDirectory"> <VBtn color="success" variant="tonal" @click="addDirectory">
<VIcon icon="mdi-plus" /> <VIcon icon="mdi-plus" />
</VBtn> </VBtn>
@@ -264,8 +267,8 @@ onMounted(() => {
<VCol cols="12"> <VCol cols="12">
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle>整理 & 刮削</VCardTitle> <VCardTitle>{{ t('setting.directory.organizeAndScrap') }}</VCardTitle>
<VCardSubtitle>设置重命名格式刮削选项等</VCardSubtitle> <VCardSubtitle>{{ t('setting.directory.organizeAndScrapDesc') }}</VCardSubtitle>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<VRow> <VRow>
@@ -273,16 +276,16 @@ onMounted(() => {
<VSelect <VSelect
v-model="SystemSettings.Basic.SCRAP_SOURCE" v-model="SystemSettings.Basic.SCRAP_SOURCE"
:items="sourceItems" :items="sourceItems"
label="刮削数据源" :label="t('setting.directory.scrapSource')"
hint="刮削时的元数据来源" :hint="t('setting.directory.scrapSourceHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
<VCol cols="12"> <VCol cols="12">
<VTextarea <VTextarea
v-model="SystemSettings.Basic.MOVIE_RENAME_FORMAT" v-model="SystemSettings.Basic.MOVIE_RENAME_FORMAT"
label="电影重命名格式" :label="t('setting.directory.movieRenameFormat')"
hint="使用Jinja2语法,格式参考:https://jinja.palletsprojects.com/en/3.0.x/templates" :hint="t('setting.directory.movieRenameFormatHint')"
persistent-hint persistent-hint
clearable clearable
active active
@@ -291,8 +294,8 @@ onMounted(() => {
<VCol cols="12"> <VCol cols="12">
<VTextarea <VTextarea
v-model="SystemSettings.Basic.TV_RENAME_FORMAT" v-model="SystemSettings.Basic.TV_RENAME_FORMAT"
label="电视剧重命名格式" :label="t('setting.directory.tvRenameFormat')"
hint="使用Jinja2语法,格式参考:https://jinja.palletsprojects.com/en/3.0.x/templates" :hint="t('setting.directory.tvRenameFormatHint')"
persistent-hint persistent-hint
clearable clearable
active active
@@ -303,7 +306,7 @@ onMounted(() => {
<VCardText> <VCardText>
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4"> <div class="d-flex flex-wrap gap-4 mt-4">
<VBtn type="submit" @click="saveSystemSettings(SystemSettings.Basic)"> 保存</VBtn> <VBtn type="submit" @click="saveSystemSettings(SystemSettings.Basic)"> {{ t('common.save') }}</VBtn>
</div> </div>
</VForm> </VForm>
</VCardText> </VCardText>
@@ -311,5 +314,5 @@ onMounted(() => {
</VCol> </VCol>
</VRow> </VRow>
<!-- 进度框 --> <!-- 进度框 -->
<ProgressDialog v-if="progressDialog" v-model="progressDialog" text="正在应用配置..." /> <ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="t('setting.system.reloading')" />
</template> </template>
@@ -216,19 +216,19 @@ onMounted(() => {
<VListItemTitle>{{ t('setting.notification.wechat') }}</VListItemTitle> <VListItemTitle>{{ t('setting.notification.wechat') }}</VListItemTitle>
</VListItem> </VListItem>
<VListItem @click="addNotification('telegram')"> <VListItem @click="addNotification('telegram')">
<VListItemTitle>Telegram</VListItemTitle> <VListItemTitle>{{ t('setting.notification.telegram') }}</VListItemTitle>
</VListItem> </VListItem>
<VListItem @click="addNotification('slack')"> <VListItem @click="addNotification('slack')">
<VListItemTitle>Slack</VListItemTitle> <VListItemTitle>{{ t('setting.notification.slack') }}</VListItemTitle>
</VListItem> </VListItem>
<VListItem @click="addNotification('synologychat')"> <VListItem @click="addNotification('synologychat')">
<VListItemTitle>SynologyChat</VListItemTitle> <VListItemTitle>{{ t('setting.notification.synologyChat') }}</VListItemTitle>
</VListItem> </VListItem>
<VListItem @click="addNotification('vocechat')"> <VListItem @click="addNotification('vocechat')">
<VListItemTitle>VoceChat</VListItemTitle> <VListItemTitle>{{ t('setting.notification.voceChat') }}</VListItemTitle>
</VListItem> </VListItem>
<VListItem @click="addNotification('webpush')"> <VListItem @click="addNotification('webpush')">
<VListItemTitle>WebPush</VListItemTitle> <VListItemTitle>{{ t('setting.notification.webPush') }}</VListItemTitle>
</VListItem> </VListItem>
</VList> </VList>
</VMenu> </VMenu>
+44 -36
View File
@@ -8,6 +8,10 @@ import { CustomRule, FilterRuleGroup } from '@/api/types'
import CustomerRuleCard from '@/components/cards/CustomRuleCard.vue' import CustomerRuleCard from '@/components/cards/CustomRuleCard.vue'
import FilterRuleGroupCard from '@/components/cards/FilterRuleGroupCard.vue' import FilterRuleGroupCard from '@/components/cards/FilterRuleGroupCard.vue'
import ImportCodeDialog from '@/components/dialog/ImportCodeDialog.vue' import ImportCodeDialog from '@/components/dialog/ImportCodeDialog.vue'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 自定义规则列表 // 自定义规则列表
const customRules = ref<CustomRule[]>([]) const customRules = ref<CustomRule[]>([])
@@ -32,10 +36,10 @@ const $toast = useToast()
// 种子优先规则下拉框 // 种子优先规则下拉框
const TorrentPriorityItems = [ const TorrentPriorityItems = [
{ title: '资源优先级', value: 'torrent' }, { title: t('setting.rule.resourcePriority'), value: 'torrent' },
{ title: '站点优先级', value: 'site' }, { title: t('setting.rule.sitePriority'), value: 'site' },
{ title: '站点上传量', value: 'upload' }, { title: t('setting.rule.siteUpload'), value: 'upload' },
{ title: '资源做种数', value: 'seeder' }, { title: t('setting.rule.resourceSeeder'), value: 'seeder' },
] ]
// 调用API查询自动分类配置 // 调用API查询自动分类配置
@@ -51,12 +55,12 @@ async function loadMediaCategories() {
async function saveCustomRules() { async function saveCustomRules() {
// 检查是否存在空id规则 // 检查是否存在空id规则
if (customRules.value.some(item => !item.id)) { if (customRules.value.some(item => !item.id)) {
$toast.error('存在空ID的规则,无法保存,请修改!') $toast.error(t('setting.rule.emptyIdError'))
return return
} }
// 检查是否存在空的规则名称 // 检查是否存在空的规则名称
if (customRules.value.some(item => !item.name)) { if (customRules.value.some(item => !item.name)) {
$toast.error('存在空名字的规则,无法保存,请修改!') $toast.error(t('setting.rule.emptyNameError'))
return return
} }
// 获取所有规则ID和名称 // 获取所有规则ID和名称
@@ -64,18 +68,18 @@ async function saveCustomRules() {
const names = customRules.value.map(item => item.name) const names = customRules.value.map(item => item.name)
// 检查是否存在重名的规则ID // 检查是否存在重名的规则ID
if (new Set(ids).size !== ids.length) { if (new Set(ids).size !== ids.length) {
$toast.error('存在重复规则ID!无法保存,请修改!') $toast.error(t('setting.rule.duplicateIdError'))
return return
} }
// 检查是否存在重名规则名称 // 检查是否存在重名规则名称
if (new Set(names).size !== names.length) { if (new Set(names).size !== names.length) {
$toast.error('存在重复规则名称!无法保存,请修改!') $toast.error(t('setting.rule.duplicateNameError'))
return return
} }
try { try {
const result: { [key: string]: any } = await api.post('system/setting/CustomFilterRules', customRules.value) const result: { [key: string]: any } = await api.post('system/setting/CustomFilterRules', customRules.value)
if (result.success) $toast.success('自定义规则保存成功') if (result.success) $toast.success(t('setting.rule.customRuleSaveSuccess'))
else $toast.error('自定义规则保存失败!') else $toast.error(t('setting.rule.customRuleSaveFailed'))
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }
@@ -117,19 +121,19 @@ async function queryFilterRuleGroups() {
async function saveFilterRuleGroups() { async function saveFilterRuleGroups() {
// 检查是否存在空的规则组名称 // 检查是否存在空的规则组名称
if (filterRuleGroups.value.some(item => !item.name)) { if (filterRuleGroups.value.some(item => !item.name)) {
$toast.error('存在空名字的规则组!无法保存,请修改!') $toast.error(t('setting.rule.emptyGroupNameError'))
return return
} }
// 检查是否存在重名规则组 // 检查是否存在重名规则组
const names = filterRuleGroups.value.map(item => item.name) const names = filterRuleGroups.value.map(item => item.name)
if (new Set(names).size !== names.length) { if (new Set(names).size !== names.length) {
$toast.error('存在重复规则组名称!无法保存,请修改!') $toast.error(t('setting.rule.duplicateGroupNameError'))
return return
} }
try { try {
const result: { [key: string]: any } = await api.post('system/setting/UserFilterRuleGroups', filterRuleGroups.value) const result: { [key: string]: any } = await api.post('system/setting/UserFilterRuleGroups', filterRuleGroups.value)
if (result.success) $toast.success('优先级规则组保存成功') if (result.success) $toast.success(t('setting.rule.ruleGroupSaveSuccess'))
else $toast.error('优先级规则组保存失败!') else $toast.error(t('setting.rule.ruleGroupSaveFailed'))
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }
@@ -157,10 +161,14 @@ async function shareRules(rules: CustomRule[] | FilterRuleGroup[], type: string)
try { try {
let success let success
success = copyToClipboard(value) success = copyToClipboard(value)
if (await success) $toast.success(`${type === 'custom' ? '自定义规则' : '优先级规则组'}已复制到剪贴板!`) if (await success)
else $toast.error(`${type === 'custom' ? '自定义规则' : '优先级规则组'}复制失败:可能是浏览器不支持或被用户阻止!`) $toast.success(
type === 'custom' ? t('setting.rule.customRuleCopySuccess') : t('setting.rule.ruleGroupCopySuccess'),
)
else
$toast.error(type === 'custom' ? t('setting.rule.customRuleCopyFailed') : t('setting.rule.ruleGroupCopyFailed'))
} catch (e) { } catch (e) {
$toast.error(`${type === 'custom' ? '自定义规则' : '优先级规则组'}复制失败!`) $toast.error(type === 'custom' ? t('setting.rule.customRuleCopyError') : t('setting.rule.ruleGroupCopyError'))
console.error(e) console.error(e)
} }
} }
@@ -178,7 +186,7 @@ function saveCodeString(type: string, codeString: any) {
try { try {
parsedCode = JSON.parse(codeString.value) parsedCode = JSON.parse(codeString.value)
} catch (e) { } catch (e) {
$toast.error('导入规则失败!无法解析输入的数据!') $toast.error(t('setting.rule.importFailed'))
console.error(e) console.error(e)
return return
} }
@@ -194,10 +202,10 @@ function saveCodeString(type: string, codeString: any) {
const newFilterRuleGroups = extractFilterRuleGroups(parsedCode) || [] const newFilterRuleGroups = extractFilterRuleGroups(parsedCode) || []
filterRuleGroups.value = [...filterRuleGroups.value, ...newFilterRuleGroups] filterRuleGroups.value = [...filterRuleGroups.value, ...newFilterRuleGroups]
} else { } else {
$toast.error('导入规则失败!未知的数据类型!') $toast.error(t('setting.rule.importUnknownType'))
} }
} catch (e) { } catch (e) {
$toast.error('导入规则失败!') $toast.error(t('setting.rule.importFailed'))
console.error(e) console.error(e)
} }
} }
@@ -272,8 +280,8 @@ function isValidValue(value: any, type: string): boolean {
function validateCustomRule(hasName: boolean, hasId: boolean, noDuplicates: boolean): boolean { function validateCustomRule(hasName: boolean, hasId: boolean, noDuplicates: boolean): boolean {
if (!hasName || !hasId || !noDuplicates) { if (!hasName || !hasId || !noDuplicates) {
if (!noDuplicates) $toast.warning(`存在重名值`) if (!noDuplicates) $toast.warning(t('setting.rule.duplicateValue'))
if (!hasId) $toast.error(`导入失败!发现有规则不存在ID,可能属于优先级规则组!`) if (!hasId) $toast.error(t('setting.rule.importNoId'))
return false return false
} }
return true return true
@@ -281,8 +289,8 @@ function validateCustomRule(hasName: boolean, hasId: boolean, noDuplicates: bool
function validateGroupRule(hasName: boolean, hasId: boolean, noDuplicates: boolean): boolean { function validateGroupRule(hasName: boolean, hasId: boolean, noDuplicates: boolean): boolean {
if (!hasName || hasId || !noDuplicates) { if (!hasName || hasId || !noDuplicates) {
if (!noDuplicates) $toast.warning(`存在重名值`) if (!noDuplicates) $toast.warning(t('setting.rule.duplicateValue'))
if (hasId) $toast.error(`导入失败!发现有规则存在相同ID,可能属于自定义规则!`) if (hasId) $toast.error(t('setting.rule.importHasId'))
return false return false
} }
return true return true
@@ -367,8 +375,8 @@ onMounted(() => {
<VCol cols="12"> <VCol cols="12">
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle>自定义规则</VCardTitle> <VCardTitle>{{ t('setting.rule.customRules') }}</VCardTitle>
<VCardSubtitle>自定义优先级规则项</VCardSubtitle> <VCardSubtitle>{{ t('setting.rule.customRulesDesc') }}</VCardSubtitle>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<draggable <draggable
@@ -391,7 +399,7 @@ onMounted(() => {
<VCardText> <VCardText>
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4"> <div class="d-flex flex-wrap gap-4 mt-4">
<VBtn type="submit" class="me-2" @click="saveCustomRules"> 保存 </VBtn> <VBtn type="submit" class="me-2" @click="saveCustomRules"> {{ t('common.save') }} </VBtn>
<VBtnGroup density="comfortable"> <VBtnGroup density="comfortable">
<VBtn color="success" variant="tonal" @click="addCustomRule"> <VBtn color="success" variant="tonal" @click="addCustomRule">
<VIcon icon="mdi-plus" /> <VIcon icon="mdi-plus" />
@@ -416,8 +424,8 @@ onMounted(() => {
<VCol cols="12"> <VCol cols="12">
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle>优先级规则组</VCardTitle> <VCardTitle>{{ t('setting.rule.priorityRuleGroups') }}</VCardTitle>
<VCardSubtitle>预设优先级规则组以便在搜索和订阅中使用</VCardSubtitle> <VCardSubtitle>{{ t('setting.rule.priorityRuleGroupsDesc') }}</VCardSubtitle>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<draggable <draggable
@@ -442,7 +450,7 @@ onMounted(() => {
<VCardText> <VCardText>
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4"> <div class="d-flex flex-wrap gap-4 mt-4">
<VBtn type="submit" class="me-2" @click="saveFilterRuleGroups"> 保存 </VBtn> <VBtn type="submit" class="me-2" @click="saveFilterRuleGroups"> {{ t('common.save') }} </VBtn>
<VBtnGroup density="comfortable"> <VBtnGroup density="comfortable">
<VBtn color="success" variant="tonal" @click="addFilterRuleGroup"> <VBtn color="success" variant="tonal" @click="addFilterRuleGroup">
<VIcon icon="mdi-plus" /> <VIcon icon="mdi-plus" />
@@ -466,7 +474,7 @@ onMounted(() => {
<ImportCodeDialog <ImportCodeDialog
v-if="importCodeDialog" v-if="importCodeDialog"
v-model="importCodeDialog" v-model="importCodeDialog"
:title="`导入${importCodeType === 'custom' ? '自定义规则' : '优先级规则组'}`" :title="importCodeType === 'custom' ? t('setting.rule.importCustomRules') : t('setting.rule.importRuleGroups')"
:dataType="importCodeType" :dataType="importCodeType"
@close="importCodeDialog = false" @close="importCodeDialog = false"
@save="saveCodeString" @save="saveCodeString"
@@ -475,8 +483,8 @@ onMounted(() => {
<VCol cols="12"> <VCol cols="12">
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle>下载规则</VCardTitle> <VCardTitle>{{ t('setting.rule.downloadRules') }}</VCardTitle>
<VCardSubtitle>同时命中多个资源时择优下载</VCardSubtitle> <VCardSubtitle>{{ t('setting.rule.downloadRulesDesc') }}</VCardSubtitle>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<VForm> <VForm>
@@ -488,8 +496,8 @@ onMounted(() => {
multiple multiple
clearable clearable
chips chips
label="当前使用下载优先规则" :label="t('setting.rule.currentPriorityRules')"
hint="排在前面的优先级越高,未选择的项不纳入排序" :hint="t('setting.rule.currentPriorityRulesHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
@@ -499,7 +507,7 @@ onMounted(() => {
<VCardText> <VCardText>
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4"> <div class="d-flex flex-wrap gap-4 mt-4">
<VBtn type="submit" @click="saveTorrentPriority"> 保存 </VBtn> <VBtn type="submit" @click="saveTorrentPriority"> {{ t('common.save') }} </VBtn>
</div> </div>
</VForm> </VForm>
</VCardText> </VCardText>
+21 -13
View File
@@ -2,6 +2,10 @@
import { useToast } from 'vue-toast-notification' import { useToast } from 'vue-toast-notification'
import api from '@/api' import api from '@/api'
import type { ScheduleInfo } from '@/api/types' import type { ScheduleInfo } from '@/api/types'
import { useI18n } from 'vue-i18n'
// 国际化
const { t } = useI18n()
// 提示框 // 提示框
const $toast = useToast() const $toast = useToast()
@@ -26,11 +30,11 @@ async function loadSchedulerList() {
// 任务状态颜色 // 任务状态颜色
function getSchedulerColor(status: string) { function getSchedulerColor(status: string) {
switch (status) { switch (status) {
case '正在运行': case t('setting.scheduler.running'):
return 'success' return 'success'
case '已停止': case t('setting.scheduler.stopped'):
return 'error' return 'error'
case '等待': case t('setting.scheduler.waiting'):
return '' return ''
default: default:
return '' return ''
@@ -46,7 +50,7 @@ function runCommand(id: string) {
jobid: id, jobid: id,
}, },
}) })
$toast.success('定时作业执行请求提交成功!') $toast.success(t('setting.scheduler.executeSuccess'))
// 1秒后刷新数据 // 1秒后刷新数据
setTimeout(() => { setTimeout(() => {
loadSchedulerList() loadSchedulerList()
@@ -77,16 +81,16 @@ onUnmounted(() => {
<template> <template>
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle>定时作业</VCardTitle> <VCardTitle>{{ t('setting.scheduler.title') }}</VCardTitle>
<VCardSubtitle>包含系统内置服务以及插件提供的服务手动执行不会影响作业正常的时间表</VCardSubtitle> <VCardSubtitle>{{ t('setting.scheduler.subtitle') }}</VCardSubtitle>
</VCardItem> </VCardItem>
<VTable class="text-no-wrap"> <VTable class="text-no-wrap">
<thead> <thead>
<tr> <tr>
<th scope="col">提供者</th> <th scope="col">{{ t('setting.scheduler.provider') }}</th>
<th scope="col">任务名称</th> <th scope="col">{{ t('setting.scheduler.taskName') }}</th>
<th scope="col">任务状态</th> <th scope="col">{{ t('setting.scheduler.taskStatus') }}</th>
<th scope="col">下一次执行时间</th> <th scope="col">{{ t('setting.scheduler.nextRunTime') }}</th>
<th scope="col" /> <th scope="col" />
</tr> </tr>
</thead> </thead>
@@ -107,16 +111,20 @@ onUnmounted(() => {
{{ scheduler.next_run }} {{ scheduler.next_run }}
</td> </td>
<td> <td>
<VBtn size="small" :disabled="scheduler.status === '正在运行'" @click="runCommand(scheduler.id)"> <VBtn
size="small"
:disabled="scheduler.status === t('setting.scheduler.running')"
@click="runCommand(scheduler.id)"
>
<template #prepend> <template #prepend>
<VIcon>mdi-play</VIcon> <VIcon>mdi-play</VIcon>
</template> </template>
执行 {{ t('setting.scheduler.execute') }}
</VBtn> </VBtn>
</td> </td>
</tr> </tr>
<tr v-if="schedulerList.length === 0"> <tr v-if="schedulerList.length === 0">
<td colspan="4" class="text-center">没有后台服务</td> <td colspan="4" class="text-center">{{ t('setting.scheduler.noService') }}</td>
</tr> </tr>
</tbody> </tbody>
</VTable> </VTable>
+22 -19
View File
@@ -2,6 +2,7 @@
import { useToast } from 'vue-toast-notification' import { useToast } from 'vue-toast-notification'
import api from '@/api' import api from '@/api'
import type { FilterRuleGroup, Site } from '@/api/types' import type { FilterRuleGroup, Site } from '@/api/types'
import { useI18n } from 'vue-i18n'
// 提示框 // 提示框
const $toast = useToast() const $toast = useToast()
@@ -172,6 +173,8 @@ async function loadSystemSettings() {
} }
} }
const { t } = useI18n()
onMounted(() => { onMounted(() => {
querySites() querySites()
queryFilterRuleGroups() queryFilterRuleGroups()
@@ -186,8 +189,8 @@ onMounted(() => {
<VCol cols="12"> <VCol cols="12">
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle>基础设置</VCardTitle> <VCardTitle>{{ t('setting.search.basicSettings') }}</VCardTitle>
<VCardSubtitle>设定数据源规则组等基础信息</VCardSubtitle> <VCardSubtitle>{{ t('setting.search.basicSettingsDesc') }}</VCardSubtitle>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<VRow> <VRow>
@@ -198,8 +201,8 @@ onMounted(() => {
clearable clearable
chips chips
:items="mediaSourcesDict" :items="mediaSourcesDict"
label="媒体搜索数据源" :label="t('setting.search.mediaSource')"
hint="搜索媒体信息时使用的数据源以及排序" :hint="t('setting.search.mediaSourceHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
@@ -210,8 +213,8 @@ onMounted(() => {
clearable clearable
chips chips
:items="filterRuleGroupOptions" :items="filterRuleGroupOptions"
label="优先级规则组" :label="t('setting.search.filterRuleGroup')"
hint="搜索媒体信息时按选定的过滤规则组对结果进行过滤" :hint="t('setting.search.filterRuleGroupHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
@@ -220,34 +223,34 @@ onMounted(() => {
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VTextField <VTextField
v-model="SystemSettings.Basic.TORRENT_TAG" v-model="SystemSettings.Basic.TORRENT_TAG"
label="下载任务标签" :label="t('setting.search.downloadLabel')"
placeholder="MOVIEPILOT" placeholder="MOVIEPILOT"
hint="MoviePilot添加的下载任务标签" :hint="t('setting.search.downloadLabelHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VCombobox <VCombobox
v-model="SystemSettings.Basic.AUTO_DOWNLOAD_USER" v-model="SystemSettings.Basic.AUTO_DOWNLOAD_USER"
label="远程搜索自动下载用户名单" :label="t('setting.search.downloadUser')"
placeholder="用户ID1,用户ID2" :placeholder="t('setting.search.downloadUserPlaceholder')"
hint="使用Telegram、微信等搜索时是否自动下载,使用逗号分割,设置为 all 代表所有用户自动择优下载" :hint="t('setting.search.downloadUserHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VSwitch <VSwitch
v-model="SystemSettings.Basic.SEARCH_MULTIPLE_NAME" v-model="SystemSettings.Basic.SEARCH_MULTIPLE_NAME"
label="多名称资源搜索" :label="t('setting.search.multipleNameSearch')"
hint="使用中英文等多个名称搜索站点资源并合并搜索结果,将会增加站点访问频率" :hint="t('setting.search.multipleNameSearchHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VSwitch <VSwitch
v-model="SystemSettings.Basic.DOWNLOAD_SUBTITLE" v-model="SystemSettings.Basic.DOWNLOAD_SUBTITLE"
label="下载站点字幕" :label="t('setting.search.downloadSubtitle')"
hint="检查站点资源是否有独立的字幕文件,有则自动下载" :hint="t('setting.search.downloadSubtitleHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
@@ -256,7 +259,7 @@ onMounted(() => {
<VCardText> <VCardText>
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4"> <div class="d-flex flex-wrap gap-4 mt-4">
<VBtn type="submit" @click="saveSearchSetting"> 保存 </VBtn> <VBtn type="submit" @click="saveSearchSetting"> {{ t('common.save') }} </VBtn>
</div> </div>
</VForm> </VForm>
</VCardText> </VCardText>
@@ -267,8 +270,8 @@ onMounted(() => {
<VCol cols="12"> <VCol cols="12">
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle>搜索站点</VCardTitle> <VCardTitle>{{ t('setting.search.downloadSite') }}</VCardTitle>
<VCardSubtitle> 只有选中的站点才会在搜索中使用</VCardSubtitle> <VCardSubtitle>{{ t('setting.search.downloadSiteDesc') }}</VCardSubtitle>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<VChipGroup v-model="selectedSites" column multiple> <VChipGroup v-model="selectedSites" column multiple>
@@ -287,7 +290,7 @@ onMounted(() => {
<VCardText> <VCardText>
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4"> <div class="d-flex flex-wrap gap-4 mt-4">
<VBtn type="submit" @click="saveSelectedSites"> 保存 </VBtn> <VBtn type="submit" @click="saveSelectedSites"> {{ t('common.save') }} </VBtn>
</div> </div>
</VForm> </VForm>
</VCardText> </VCardText>
+37 -34
View File
@@ -3,6 +3,7 @@ import { useToast } from 'vue-toast-notification'
import api from '@/api' import api from '@/api'
import type { FilterRuleGroup, Site } from '@/api/types' import type { FilterRuleGroup, Site } from '@/api/types'
import ProgressDialog from '@/components/dialog/ProgressDialog.vue' import ProgressDialog from '@/components/dialog/ProgressDialog.vue'
import { useI18n } from 'vue-i18n'
// 提示框 // 提示框
const $toast = useToast() const $toast = useToast()
@@ -24,8 +25,8 @@ const selectedBestVersionRuleGroup = ref([])
// 订阅模式选择项 // 订阅模式选择项
const subscribeModeItems = [ const subscribeModeItems = [
{ title: '自动', value: 'spider' }, { title: t('setting.subscribe.modes.auto'), value: 'spider' },
{ title: '站点RSS', value: 'rss' }, { title: t('setting.subscribe.modes.rss'), value: 'rss' },
] ]
// 所有规则组列表 // 所有规则组列表
@@ -41,13 +42,13 @@ const filterRuleGroupOptions = computed(() => {
// RSS运行周期选择项 // RSS运行周期选择项
const rssIntervalItems = [ const rssIntervalItems = [
{ title: '5分钟', value: 5 }, { title: t('setting.subscribe.intervals.min5'), value: 5 },
{ title: '10分钟', value: 10 }, { title: t('setting.subscribe.intervals.min10'), value: 10 },
{ title: '20分钟', value: 20 }, { title: t('setting.subscribe.intervals.min20'), value: 20 },
{ title: '半小时', value: 30 }, { title: t('setting.subscribe.intervals.min30'), value: 30 },
{ title: '1小时', value: 60 }, { title: t('setting.subscribe.intervals.hour1'), value: 60 },
{ title: '12小时', value: 720 }, { title: t('setting.subscribe.intervals.hour12'), value: 720 },
{ title: '1天', value: 1440 }, { title: t('setting.subscribe.intervals.day1'), value: 1440 },
] ]
// 系统设置项 // 系统设置项
@@ -99,8 +100,8 @@ async function saveSelectedRssSites() {
try { try {
const result1: { [key: string]: any } = await api.post('system/setting/RssSites', selectedRssSites.value) const result1: { [key: string]: any } = await api.post('system/setting/RssSites', selectedRssSites.value)
if (result1.success) $toast.success('订阅站点保存成功') if (result1.success) $toast.success(t('setting.subscribe.saveSuccess'))
else $toast.error('订阅站点保存失败!') else $toast.error(t('setting.subscribe.saveFailed'))
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }
@@ -154,8 +155,8 @@ async function reloadSystem() {
progressDialog.value = true progressDialog.value = true
try { try {
const result: { [key: string]: any } = await api.get('system/reload') const result: { [key: string]: any } = await api.get('system/reload')
if (result.success) $toast.success('系统配置已生效') if (result.success) $toast.success(t('setting.system.reloadSuccess'))
else $toast.error('重载系统失败!') else $toast.error(t('setting.system.reloadFailed'))
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }
@@ -178,14 +179,16 @@ async function saveSubscribeSetting() {
const result3 = await saveSystemSetting(SystemSettings.value.Basic) const result3 = await saveSystemSetting(SystemSettings.value.Basic)
if (result1.success && result2.success && result3) { if (result1.success && result2.success && result3) {
$toast.success('订阅基础设置保存成功') $toast.success(t('setting.subscribe.settingsSaveSuccess'))
await reloadSystem() await reloadSystem()
} else $toast.error('订阅基础设置保存失败!') } else $toast.error(t('setting.subscribe.settingsSaveFailed'))
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }
} }
const { t } = useI18n()
onMounted(() => { onMounted(() => {
querySites() querySites()
queryFilterRuleGroups() queryFilterRuleGroups()
@@ -200,8 +203,8 @@ onMounted(() => {
<VCol cols="12"> <VCol cols="12">
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle>基础设置</VCardTitle> <VCardTitle>{{ t('setting.subscribe.basicSettings') }}</VCardTitle>
<VCardSubtitle>设定订阅模式周期等基础设置</VCardSubtitle> <VCardSubtitle>{{ t('setting.subscribe.basicSettingsDesc') }}</VCardSubtitle>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<VForm> <VForm>
@@ -210,8 +213,8 @@ onMounted(() => {
<VSelect <VSelect
v-model="SystemSettings.Basic.SUBSCRIBE_MODE" v-model="SystemSettings.Basic.SUBSCRIBE_MODE"
:items="subscribeModeItems" :items="subscribeModeItems"
label="订阅模式" :label="t('setting.subscribe.mode')"
hint="自动:自动爬取站点首页,站点RSS:通过站点RSS链接订阅" :hint="t('setting.subscribe.modeHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
@@ -219,8 +222,8 @@ onMounted(() => {
<VSelect <VSelect
v-model="SystemSettings.Basic.SUBSCRIBE_RSS_INTERVAL" v-model="SystemSettings.Basic.SUBSCRIBE_RSS_INTERVAL"
:items="rssIntervalItems" :items="rssIntervalItems"
label="站点RSS周期" :label="t('setting.subscribe.rssInterval')"
hint="设置站点RSS运行周期,在订阅模式为`站点RSS`时生效" :hint="t('setting.subscribe.rssIntervalHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
@@ -231,8 +234,8 @@ onMounted(() => {
chips chips
multiple multiple
clearable clearable
label="订阅优先级规则组" :label="t('setting.subscribe.filterRuleGroup')"
hint="按选定的过滤规则组对订阅进行过滤" :hint="t('setting.subscribe.filterRuleGroupHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
@@ -243,8 +246,8 @@ onMounted(() => {
chips chips
multiple multiple
clearable clearable
label="洗版优先级规则组" :label="t('setting.subscribe.bestVersionRuleGroup')"
hint="按选定的过滤规则组对洗版订阅进行过滤" :hint="t('setting.subscribe.bestVersionRuleGroupHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
@@ -253,16 +256,16 @@ onMounted(() => {
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VSwitch <VSwitch
v-model="SystemSettings.Basic.SUBSCRIBE_SEARCH" v-model="SystemSettings.Basic.SUBSCRIBE_SEARCH"
label="订阅定时搜索" :label="t('setting.subscribe.timedSearch')"
hint="每隔24小时全站搜索,以补全订阅可能漏掉的资源" :hint="t('setting.subscribe.timedSearchHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
<VCol cols="12" md="6"> <VCol cols="12" md="6">
<VSwitch <VSwitch
v-model="SystemSettings.Basic.LOCAL_EXISTS_SEARCH" v-model="SystemSettings.Basic.LOCAL_EXISTS_SEARCH"
label="检查本地媒体库资源" :label="t('setting.subscribe.checkLocalMedia')"
hint="检查存储盘是否存在资源,以避免重复下载" :hint="t('setting.subscribe.checkLocalMediaHint')"
persistent-hint persistent-hint
/> />
</VCol> </VCol>
@@ -272,7 +275,7 @@ onMounted(() => {
<VCardText> <VCardText>
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4"> <div class="d-flex flex-wrap gap-4 mt-4">
<VBtn type="submit" @click="saveSubscribeSetting"> 保存 </VBtn> <VBtn type="submit" @click="saveSubscribeSetting"> {{ t('common.save') }} </VBtn>
</div> </div>
</VForm> </VForm>
</VCardText> </VCardText>
@@ -283,8 +286,8 @@ onMounted(() => {
<VCol cols="12"> <VCol cols="12">
<VCard> <VCard>
<VCardItem> <VCardItem>
<VCardTitle>订阅站点</VCardTitle> <VCardTitle>{{ t('setting.subscribe.subscribeSites') }}</VCardTitle>
<VCardSubtitle>只有选中的站点才会在订阅中使用</VCardSubtitle> <VCardSubtitle>{{ t('setting.subscribe.subscribeSitesDesc') }}</VCardSubtitle>
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<VChipGroup v-model="selectedRssSites" column multiple> <VChipGroup v-model="selectedRssSites" column multiple>
@@ -303,7 +306,7 @@ onMounted(() => {
<VCardText> <VCardText>
<VForm @submit.prevent="() => {}"> <VForm @submit.prevent="() => {}">
<div class="d-flex flex-wrap gap-4 mt-4"> <div class="d-flex flex-wrap gap-4 mt-4">
<VBtn type="submit" @click="saveSelectedRssSites"> 保存 </VBtn> <VBtn type="submit" @click="saveSelectedRssSites"> {{ t('common.save') }} </VBtn>
</div> </div>
</VForm> </VForm>
</VCardText> </VCardText>
@@ -311,5 +314,5 @@ onMounted(() => {
</VCol> </VCol>
</VRow> </VRow>
<!-- 进度框 --> <!-- 进度框 -->
<ProgressDialog v-if="progressDialog" v-model="progressDialog" text="正在应用配置..." /> <ProgressDialog v-if="progressDialog" v-model="progressDialog" :text="t('setting.system.reloading')" />
</template> </template>
+5 -5
View File
@@ -532,10 +532,10 @@ onDeactivated(() => {
<VMenu activator="parent" close-on-content-click> <VMenu activator="parent" close-on-content-click>
<VList> <VList>
<VListItem @click="addDownloader('qbittorrent')"> <VListItem @click="addDownloader('qbittorrent')">
<VListItemTitle>Qbittorrent</VListItemTitle> <VListItemTitle>{{ t('setting.system.qbittorrent') }}</VListItemTitle>
</VListItem> </VListItem>
<VListItem @click="addDownloader('transmission')"> <VListItem @click="addDownloader('transmission')">
<VListItemTitle>Transmission</VListItemTitle> <VListItemTitle>{{ t('setting.system.transmission') }}</VListItemTitle>
</VListItem> </VListItem>
</VList> </VList>
</VMenu> </VMenu>
@@ -580,13 +580,13 @@ onDeactivated(() => {
<VMenu activator="parent" close-on-content-click> <VMenu activator="parent" close-on-content-click>
<VList> <VList>
<VListItem @click="addMediaServer('emby')"> <VListItem @click="addMediaServer('emby')">
<VListItemTitle>Emby</VListItemTitle> <VListItemTitle>{{ t('setting.system.emby') }}</VListItemTitle>
</VListItem> </VListItem>
<VListItem @click="addMediaServer('jellyfin')"> <VListItem @click="addMediaServer('jellyfin')">
<VListItemTitle>Jellyfin</VListItemTitle> <VListItemTitle>{{ t('setting.system.jellyfin') }}</VListItemTitle>
</VListItem> </VListItem>
<VListItem @click="addMediaServer('plex')"> <VListItem @click="addMediaServer('plex')">
<VListItemTitle>Plex</VListItemTitle> <VListItemTitle>{{ t('setting.system.plex') }}</VListItemTitle>
</VListItem> </VListItem>
<VListItem @click="addMediaServer('trimemedia')"> <VListItem @click="addMediaServer('trimemedia')">
<VListItemTitle>{{ t('setting.system.trimeMedia') }}</VListItemTitle> <VListItemTitle>{{ t('setting.system.trimeMedia') }}</VListItemTitle>