diff --git a/src/api/types.ts b/src/api/types.ts index 4414c6b6..a25fdb57 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -847,22 +847,124 @@ export interface SystemNotification { date: string } -// 下载目录/媒体库目录 -export interface MediaDirectory { - // 类型 download/library - type?: string - // 别名 - name?: string - // 路径 - path?: string - // 媒体类型 电影/电视剧 - media_type?: string - // 媒体类别 动画电影/国产剧 - category?: string - // 刮削媒体信息 - scrape?: boolean - // 自动二级分类,未指定类别时自动分类 - auto_category?: boolean - // 优先级 - priority?: number +// 下载器配置 +export interface DownloaderConf { + // 名称 + name: string + // 类型 qbittorrent/transmission + type: string + // 是否默认 + default: boolean + // 配置 + config?: { [key: string]: any } + // 是否启用 + enabled: boolean +} + +// 通知配置 +export interface NotificationConf { + // 名称 + name: string + // 类型 telegram/wechat/vocechat/synologychat + type: string + // 配置 + config?: { [key: string]: any } + // 场景开关 + switchs?: string[] + // 是否启用 + enabled: boolean +} + +// 通知场景开关配置 +export interface NotificationSwitchConf { + // 场景名称 + type: string + // 通知范围 all/user/admin + action: string +} + +// 存储配置 +export interface StorageConf { + // 类型 local/alipan/u115/rclone + type: string + // 配置 + config?: { [key: string]: any } +} + +// 媒体服务器配置 +export interface MediaServerConf { + // 名称 + name: string + // 类型 emby/jellyfin/plex + type: string + // 配置 + config?: { [key: string]: any } + // 是否启用 + enabled: boolean + // 同步媒体体库列表 + sync_libraries?: string[] +} + +// 文件整理目录配置 +export interface TransferDirectoryConf { + // 名称 + name: string + // 优先级 + priority: number + // 存储 + storage: string + // 下载目录 + download_path?: string + // 适用媒体类型 + media_type?: string + // 适用媒体类别 + media_category?: string + // 下载类型子目录 + download_type_folder?: boolean + // 下载类别子目录 + download_category_folder?: boolean + // 监控方式 downloader/monitor,None为不监控 + monitor_type?: string + // 整理方式 move/copy/link/softlink + transfer_type?: string + // 文件覆盖模式 always/size/never/latest + overwrite_mode?: string + // 整理到媒体库目录 + library_path?: string + // 媒体库目录存储 + library_storage?: string + // 智能重命名 + renaming?: boolean + // 刮削 + scraping?: boolean + // 媒体库类型子目录 + library_type_folder?: boolean + // 媒体库类别子目录 + library_category_folder?: boolean +} + +// 自定义规则项 +export interface CustomRule { + // 规则ID + id: string + // 名称 + name: string + // 包含 + include?: string[] + // 排除 + exclude?: string[] + // 大小范围 + size_range?: string + // 最少做种人数 + seeders?: string +} + +// 过滤规则组 +export interface FilterRuleGroup { + // 名称 + name: string + // 规则串 + rule_string?: string + // 适用类媒体类别 None-全部 电影/电视剧 + media_type?: string } diff --git a/src/components/cards/CustomRuleCard.vue b/src/components/cards/CustomRuleCard.vue new file mode 100644 index 00000000..11645e02 --- /dev/null +++ b/src/components/cards/CustomRuleCard.vue @@ -0,0 +1,2 @@ + + diff --git a/src/components/cards/DownloaderCard.vue b/src/components/cards/DownloaderCard.vue new file mode 100644 index 00000000..11645e02 --- /dev/null +++ b/src/components/cards/DownloaderCard.vue @@ -0,0 +1,2 @@ + + diff --git a/src/components/cards/FilterRuleGroupCard.vue b/src/components/cards/FilterRuleGroupCard.vue new file mode 100644 index 00000000..11645e02 --- /dev/null +++ b/src/components/cards/FilterRuleGroupCard.vue @@ -0,0 +1,2 @@ + + diff --git a/src/components/cards/MediaServerCard.vue b/src/components/cards/MediaServerCard.vue new file mode 100644 index 00000000..11645e02 --- /dev/null +++ b/src/components/cards/MediaServerCard.vue @@ -0,0 +1,2 @@ + + diff --git a/src/components/cards/NotificationChannelCard.vue b/src/components/cards/NotificationChannelCard.vue new file mode 100644 index 00000000..11645e02 --- /dev/null +++ b/src/components/cards/NotificationChannelCard.vue @@ -0,0 +1,2 @@ + + diff --git a/src/components/cards/StorageCard.vue b/src/components/cards/StorageCard.vue new file mode 100644 index 00000000..11645e02 --- /dev/null +++ b/src/components/cards/StorageCard.vue @@ -0,0 +1,2 @@ + + diff --git a/src/components/cards/UserCard.vue b/src/components/cards/UserCard.vue new file mode 100644 index 00000000..11645e02 --- /dev/null +++ b/src/components/cards/UserCard.vue @@ -0,0 +1,2 @@ + + diff --git a/src/components/dialog/UserAddEditDialog.vue b/src/components/dialog/UserAddEditDialog.vue new file mode 100644 index 00000000..11645e02 --- /dev/null +++ b/src/components/dialog/UserAddEditDialog.vue @@ -0,0 +1,2 @@ + + diff --git a/src/layouts/components/UserProfile.vue b/src/layouts/components/UserProfile.vue index 33a3ba39..964d8898 100644 --- a/src/layouts/components/UserProfile.vue +++ b/src/layouts/components/UserProfile.vue @@ -79,7 +79,6 @@ watch(isCompactMode, value => { - diff --git a/src/pages/setting.vue b/src/pages/setting.vue index d9364805..6e577067 100644 --- a/src/pages/setting.vue +++ b/src/pages/setting.vue @@ -10,6 +10,7 @@ import AccountSettingSubscribe from '@/views/setting/AccountSettingSubscribe.vue import AccountSettingService from '@/views/setting/AccountSettingService.vue' import AccountSettingSystem from '@/views/setting/AccountSettingSystem.vue' import AccountSettingDirectory from '@/views/setting/AccountSettingDirectory.vue' +import AccountSettingRule from '@/views/setting/AccountSettingRule.vue' import { SettingTabs } from '@/router/menu' const route = useRoute() @@ -58,6 +59,13 @@ function jumpTab(tab: string) { + + + + + + + diff --git a/src/router/menu.ts b/src/router/menu.ts index 836079e1..43ce4b04 100644 --- a/src/router/menu.ts +++ b/src/router/menu.ts @@ -134,7 +134,7 @@ export const SettingTabs = [ description: '下载器(Qbittorrent、Transmission)、媒体服务器(Emby、Jellyfin、Plex)', }, { - title: '目录', + title: '存储 & 目录', icon: 'mdi-folder', tab: 'directory', description: '下载目录、媒体库目录、整理模式', @@ -145,6 +145,12 @@ export const SettingTabs = [ tab: 'site', description: '站点同步、下载优先规则、站点重置', }, + { + title: '规则', + icon: 'mdi-filter', + tab: 'rule', + description: '优先级规则组', + }, { title: '搜索', icon: 'mdi-magnify', diff --git a/src/styles/styles.scss b/src/styles/styles.scss index bc9453d6..2318b7de 100644 --- a/src/styles/styles.scss +++ b/src/styles/styles.scss @@ -181,6 +181,16 @@ padding-block-end: 1rem; } +.grid-user-card { + grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); + padding-block-end: 1rem; +} + +.grid-app-card { + grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); + padding-block-end: 1rem; +} + .v-tabs:not(.v-tabs-pill).v-tabs--horizontal { border-block-end: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)); } diff --git a/src/views/discover/TorrentCardListView.vue b/src/views/discover/TorrentCardListView.vue index 57efba45..a646b16b 100644 --- a/src/views/discover/TorrentCardListView.vue +++ b/src/views/discover/TorrentCardListView.vue @@ -125,7 +125,6 @@ onMounted(() => { } }) groupedDataList.value = groupMap - }) // 只监听filterForm和groupedDataList的变化。因为displayDataList的变化不需要清空列表 @@ -265,12 +264,16 @@ function loadMore({ done }: { done: any }) { - - - - - - - + + + + + + + diff --git a/src/views/setting/AccountSettingDirectory.vue b/src/views/setting/AccountSettingDirectory.vue index 48d31cf0..8d205e4d 100644 --- a/src/views/setting/AccountSettingDirectory.vue +++ b/src/views/setting/AccountSettingDirectory.vue @@ -4,39 +4,14 @@ import { useToast } from 'vue-toast-notification' import draggable from 'vuedraggable' import { VRow } from 'vuetify/lib/components/index.mjs' import api from '@/api' -import { MediaDirectory } from '@/api/types' +import { TransferDirectoryConf, StorageConf } from '@/api/types' import DirectoryCard from '@/components/cards/DirectoryCard.vue' -// 媒体库设置项 -const transferSettings = ref({ - TRANSFER_TYPE: 'copy', - OVERWRITE_MODE: 'size', - TRANSFER_SAME_DISK: true, -}) - -// 转移方式字典 -const transferTypeItems = [ - { title: '硬链接', value: 'link' }, - { title: '复制', value: 'copy' }, - { title: '移动', value: 'move' }, - { title: '软链接', value: 'softlink' }, - { title: 'rclone复制', value: 'rclone_copy' }, - { title: 'rclone移动', value: 'rclone_move' }, -] - -// 覆盖模式字典 -const overwriteModeItems = [ - { title: '从不覆盖', value: 'never' }, - { title: '按大小覆盖', value: 'size' }, - { title: '总是覆盖', value: 'always' }, - { title: '仅保留最新版本', value: 'latest' }, -] - // 所有下载目录 -const downloadDirectories = ref([]) +const directories = ref([]) -// 所有媒体库目录 -const libraryDirectories = ref([]) +// 所有存储 +const storages = ref([]) // 二级分类策略 const mediaCategories = ref<{ [key: string]: any }>({}) @@ -44,148 +19,32 @@ const mediaCategories = ref<{ [key: string]: any }>({}) // 提示框 const $toast = useToast() -// 加载媒体库设置 -async function loadTransferSettings() { - try { - const result: { [key: string]: any } = await api.get('system/env') - if (result.success) { - const { TRANSFER_TYPE, OVERWRITE_MODE, TRANSFER_SAME_DISK } = result.data - transferSettings.value = { - TRANSFER_TYPE, - OVERWRITE_MODE, - TRANSFER_SAME_DISK, - } - } - } catch (error) { - console.log(error) - } -} - -// 调用API保存媒体设置 -async function saveTransferSetting() { - try { - const result: { [key: string]: any } = await api.post('system/env', transferSettings.value) - - if (result.success) $toast.success('保存媒体库设置成功') - else $toast.error('保存媒体库设置失败!') - } catch (error) { - console.log(error) - } -} - // 移动结束 -function orderDownloadCards() { +function orderDirectoryCards() { // 更新所有目录的优先级 - downloadDirectories.value.forEach((item, index) => { + directories.value.forEach((item, index) => { item.priority = index }) } -// 移动结束 -function orderLibraryCards() { - // 更新所有目录的优先级 - libraryDirectories.value.forEach((item, index) => { - item.priority = index - }) -} +// 查询存储 +async function loadStorages() {} -// 关闭目录卡片 -function libraryCardClose(name: string) { - libraryDirectories.value = libraryDirectories.value.filter(item => item.name !== name) -} +// 保存存储 +async function saveStorages() {} +// 添加存储 +function addStorage() {} -// 关闭下载卡片 -function downloadCardClose(name: string) { - downloadDirectories.value = downloadDirectories.value.filter(item => item.name !== name) -} +// 查询目录 +async function loadDirectories() {} -// 查询下载目录 -async function loadDownloadDirectories() { - try { - const result: { [key: string]: any } = await api.get('system/setting/DownloadDirectories') - if (result.success && result.data?.value) { - downloadDirectories.value = result.data.value - } - } catch (error) { - console.log(error) - } -} - -// 保存下载目录 -async function saveDownloadDirectories() { - orderDownloadCards() - try { - const value = downloadDirectories.value.map(item => { - return { - name: item.name, - path: item.path, - media_type: item.media_type, - category: item.category, - auto_category: item.auto_category, - priority: item.priority, - } - }) - const result: { [key: string]: any } = await api.post('system/setting/DownloadDirectories', value) - if (result.success) $toast.success('下载目录设置保存成功!') - } catch (e) { - console.error('保存下载目录设置失败') - } -} - -// 添加下载目录 -function addDownloadDirectory() { - downloadDirectories.value.push({ - name: `下载目录${downloadDirectories.value.length + 1}`, - path: '', - media_type: '全部', - category: '', - }) -} - -// 查询媒体库目录 -async function loadLibraryDirectories() { - try { - const result: { [key: string]: any } = await api.get('system/setting/LibraryDirectories') - if (result.success && result.data?.value) { - libraryDirectories.value = result.data.value - } - } catch (error) { - console.log(error) - } -} - -// 保存媒体库目录 -async function saveLibraryDirectories() { - orderLibraryCards() - try { - const value = libraryDirectories.value.map(item => { - return { - name: item.name, - path: item.path, - media_type: item.media_type, - category: item.category, - auto_category: item.auto_category, - scrape: item.scrape, - priority: item.priority, - } - }) - const result: { [key: string]: any } = await api.post('system/setting/LibraryDirectories', value) - if (result.success) $toast.success('媒体库目录设置保存成功!') - } catch (e) { - console.error('保存媒体库目录设置失败') - } +// 保存目录 +async function saveDirectories() { + orderDirectoryCards() } // 添加媒体库目录 -function addLibraryDirectory() { - libraryDirectories.value.push({ - name: `媒体库目录${libraryDirectories.value.length + 1}`, - path: '', - media_type: '全部', - category: '', - scrape: true, - }) -} +function addDirectory() {} // 调用API查询自动分类配置 async function loadMediaCategories() { @@ -198,10 +57,9 @@ async function loadMediaCategories() { // 加载数据 onMounted(() => { - loadTransferSettings() + loadDirectories() + loadStorages() loadMediaCategories() - loadDownloadDirectories() - loadLibraryDirectories() }) @@ -210,32 +68,25 @@ onMounted(() => { - 下载目录 - 设置下载目录路径和分类,按顺序依次匹配使用。 + 存储 + 设置本地或网盘存储参数。 - (element.path = value)" - @close="downloadCardClose(element.name)" - /> + - 保存 - + 保存 + @@ -244,17 +95,17 @@ onMounted(() => { - 媒体库目录 - 设置媒体文件整理后存储目录和分类,按顺序依次匹配使用。 + 目录 + 设置媒体文件整理目录结构,按先后顺序依次匹配。 { :directory="element" :categories="mediaCategories" @update:modelValue="(value: string) => (element.path = value)" - @close="libraryCardClose(element.name)" /> - 保存 - + 保存 + - - - - 整理模式 - 设置文件整理方式和偏好。 - - - - - - - - - - - - - - - - - - {}"> - - 保存 - - - - - diff --git a/src/views/setting/AccountSettingNotification.vue b/src/views/setting/AccountSettingNotification.vue index a9ee1461..2083e5f7 100644 --- a/src/views/setting/AccountSettingNotification.vue +++ b/src/views/setting/AccountSettingNotification.vue @@ -1,38 +1,12 @@ @@ -187,231 +49,28 @@ onMounted(() => { 通知渠道 - 只有选中的渠道才会发送消息。 + 设置消息发送渠道参数。 - - - - - - - - - - 微信 - Telegram - Slack - SynologyChat - VoceChat - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + {}"> - 保存 + 保存 + + + @@ -422,55 +81,14 @@ onMounted(() => { - 消息类型 - 对应消息类型只会发送给选中的消息渠道。 + 通知发送范围 + 对应消息类型只会发送给设定的用户。 - - - - 消息类型 - 微信 - Telegram - Slack - SynologyChat - VoceChat - WebPush - - - - - - {{ message.mtype }} - - - - - - - - - - - - - - - - - - - - - - 没有设置任何通知渠道 - - - - + {}"> - 保存 + 保存 diff --git a/src/views/setting/AccountSettingRule.vue b/src/views/setting/AccountSettingRule.vue new file mode 100644 index 00000000..a980ba67 --- /dev/null +++ b/src/views/setting/AccountSettingRule.vue @@ -0,0 +1,157 @@ + + + + + + + + + 自定义规则 + 自定义优先级规则项 + + + + + + + + + + 保存 + + + + + + + + + + 优先级规则组 + 预设优先级规则组,以便在搜索和订阅中使用。 + + + + + + + + + + 保存 + + + + + + + + + + 下载规则 + 按站点或做种数量优先下载。 + + + + + + + + + + + + 保存 + + + + + diff --git a/src/views/setting/AccountSettingSearch.vue b/src/views/setting/AccountSettingSearch.vue index 8ccb7369..149bb428 100644 --- a/src/views/setting/AccountSettingSearch.vue +++ b/src/views/setting/AccountSettingSearch.vue @@ -1,26 +1,11 @@ @@ -282,8 +121,8 @@ onMounted(() => { - 媒体数据源 - 设定搜索时展示哪些源的媒体信息。 + 搜索数据源 & 规则组 + 设定数据源、规则组等基础信息。 @@ -293,15 +132,26 @@ onMounted(() => { multiple chips :items="mediaSourcesDict" - label="当前使用数据源" + label="媒体数据源" hint="搜索媒体信息时使用的数据源以及排序" persistent-hint /> + + + - 保存 + 保存 @@ -330,118 +180,5 @@ onMounted(() => { - - - - - - - - - - - - - 分享 - - - - - - 导入 - - - - - - 搜索优先级 - 设置在搜索时默认使用的优先级排序,未在优先级中的资源将不在搜索结果中显示。 - - - - - - - - - - 保存 - - - - - - - - - - 默认过滤规则 - 设置在搜索时默认使用的过滤规则。 - - - - - - - - - - - - - - - - - - - - - 保存 - - - - - - diff --git a/src/views/setting/AccountSettingSite.vue b/src/views/setting/AccountSettingSite.vue index 4d0ace71..53dbde27 100644 --- a/src/views/setting/AccountSettingSite.vue +++ b/src/views/setting/AccountSettingSite.vue @@ -14,9 +14,6 @@ const resetSitesText = ref('重置站点数据') // 站点重置按钮可用状态 const resetSitesDisabled = ref(false) -// 种子优先规则 -const selectedTorrentPriority = ref('seeder') - // CookieCloud设置项 const cookieCloudSetting = ref({ COOKIECLOUD_HOST: '', @@ -28,12 +25,6 @@ const cookieCloudSetting = ref({ COOKIECLOUD_BLACKLIST: '', }) -// 种子优先规则下拉框 -const TorrentPriorityItems = [ - { title: '站点优先', value: 'site' }, - { title: '做种数优先', value: 'seeder' }, -] - // 同步间隔下拉框 const CookieCloudIntervalItems = [ { title: '每小时', value: 60 }, @@ -62,33 +53,6 @@ async function resetSites() { } } -// 查询种子优先规则 -async function queryTorrentPriority() { - try { - const result: { [key: string]: any } = await api.get('system/setting/TorrentsPriority') - - selectedTorrentPriority.value = result.data?.value - } catch (error) { - console.log(error) - } -} - -// 保存种子优先规则 -async function saveTorrentPriority() { - try { - // 用户名密码 - const result: { [key: string]: any } = await api.post( - 'system/setting/TorrentsPriority', - selectedTorrentPriority.value, - ) - - if (result.success) $toast.success('优先规则保存成功') - else $toast.error('优先规则保存失败!') - } catch (error) { - console.log(error) - } -} - // 加载CookieCloud设置 async function loadCookieCloudSettings() { try { @@ -132,7 +96,6 @@ async function saveCookieCloudetting() { // 加载数据 onMounted(() => { - queryTorrentPriority() loadCookieCloudSettings() }) @@ -219,32 +182,7 @@ onMounted(() => { - - - - 下载优先规则 - 按站点或做种数量优先下载。 - - - - - - - - - - - - 保存 - - - + diff --git a/src/views/setting/AccountSettingSubscribe.vue b/src/views/setting/AccountSettingSubscribe.vue index 98ba388d..68df5008 100644 --- a/src/views/setting/AccountSettingSubscribe.vue +++ b/src/views/setting/AccountSettingSubscribe.vue @@ -1,37 +1,19 @@ @@ -313,23 +119,9 @@ onMounted(() => { - 订阅站点 - 只有选中的站点才会在订阅中使用。 + 订阅模式 & 规则 + 设定订阅模式、周期等基础设置 - - - - {{ site.name }} - - - @@ -351,6 +143,17 @@ onMounted(() => { persistent-hint /> + + + @@ -372,190 +175,27 @@ onMounted(() => { - - - - - - - - - - 分享 - - - - - - 导入 - - - - - - 订阅优先级 - 设置在正常订阅时默认使用的优先级,未在优先级中的资源将不会自动下载。 + 订阅站点 + 只有选中的站点才会在订阅中使用。 - - - - - + + + {{ site.name }} + + - 保存 - - - - - - - - - - 洗版优先级 - - - - - - - - - - 分享 - - - - - - 导入 - - - - - - 设置在订阅洗版时使用的优先级,匹配优先级1时洗版完成。 - - - - - - - - - - 保存 - - - - - - - - - - 默认过滤规则 - 设置在订阅时默认使用的过滤规则。 - - - - - - - - - - - - - - - - - - - - - - - - - - - 保存 + 保存 - - - diff --git a/src/views/setting/AccountSettingSystem.vue b/src/views/setting/AccountSettingSystem.vue index 92042b2c..6531884c 100644 --- a/src/views/setting/AccountSettingSystem.vue +++ b/src/views/setting/AccountSettingSystem.vue @@ -2,216 +2,37 @@ - - - - - - - - - - - - - - 用户名 - 邮箱 - 状态 - 管理员 - - - - - - - {{ user.name }} - - {{ user.email }} - - 激活 - 冻结 - - {{ user.is_superuser ? '是' : '否' }} - - - - - - - - - - - {{ user.is_active ? '冻结' : '解冻' }} - - - - - - - 删除 - - - - - - - - - - - - - - - - - {}"> - - - - - - - - - - - - - - - 取消 - - 确定 - - - + + + + + + { @click="addUserDialog = true" :class="{ 'mb-12': appMode }" /> + + +