From 018c5f857baf22f3167028bee7921fa2377cbf1a Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sat, 12 Oct 2024 12:30:56 +0800 Subject: [PATCH 01/10] feat(downloading): add NoDataFound component for empty downloaders Add the NoDataFound component to the downloading page to display a 404 error message when there are no enabled downloaders. This component will show an error code, title, and description to guide users on how to configure and enable downloaders in the settings. Closes #197 --- src/pages/downloading.vue | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pages/downloading.vue b/src/pages/downloading.vue index 141b3a2c..c6c20df2 100644 --- a/src/pages/downloading.vue +++ b/src/pages/downloading.vue @@ -3,6 +3,7 @@ import api from '@/api' import { DownloaderConf } from '@/api/types' import DownloadingListView from '@/views/reorganize/DownloadingListView.vue' import router from '@/router' +import NoDataFound from '@/components/NoDataFound.vue' const route = useRoute() const activeTab = ref(route.query.tab) @@ -36,7 +37,7 @@ onMounted(() => { From ddf682d66acd54b70240f1868f0d92b30c0bb50f Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Mon, 14 Oct 2024 01:35:27 +0800 Subject: [PATCH 02/10] feat(security): update douban image proxy URL --- src/components/cards/MediaCard.vue | 2 +- src/views/discover/MediaDetailView.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/cards/MediaCard.vue b/src/components/cards/MediaCard.vue index 87b577e1..ff6cd459 100644 --- a/src/components/cards/MediaCard.vue +++ b/src/components/cards/MediaCard.vue @@ -374,7 +374,7 @@ const getImgUrl: Ref = computed(() => { return `${import.meta.env.VITE_API_BASE_URL}system/cache/image?url=${encodeURIComponent(url)}` // 如果地址中包含douban则使用中转代理 if (url.includes('doubanio.com')) - return `${import.meta.env.VITE_API_BASE_URL}douban/img?imgurl=${encodeURIComponent(url)}` + return `${import.meta.env.VITE_API_BASE_URL}system/img/0?imgurl=${encodeURIComponent(url)}` return url }) diff --git a/src/views/discover/MediaDetailView.vue b/src/views/discover/MediaDetailView.vue index aa75aa9d..205c835b 100644 --- a/src/views/discover/MediaDetailView.vue +++ b/src/views/discover/MediaDetailView.vue @@ -334,7 +334,7 @@ const getPosterUrl: Ref = computed(() => { return `${import.meta.env.VITE_API_BASE_URL}system/cache/image?url=${encodeURIComponent(url)}` // 如果地址中包含douban则使用中转代理 if (url.includes('doubanio.com')) - return `${import.meta.env.VITE_API_BASE_URL}douban/img?imgurl=${encodeURIComponent(url)}` + return `${import.meta.env.VITE_API_BASE_URL}system/img/0?imgurl=${encodeURIComponent(url)}` return url }) From e9a5c0ae69386a6ac1150928fb926424abdf2aca Mon Sep 17 00:00:00 2001 From: jxxghp Date: Tue, 15 Oct 2024 20:18:38 +0800 Subject: [PATCH 03/10] refactor(setting): remove unnecessary function call in AccountSettingDirectory.vue --- src/views/setting/AccountSettingDirectory.vue | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/views/setting/AccountSettingDirectory.vue b/src/views/setting/AccountSettingDirectory.vue index e09b22e3..0e9c06c5 100644 --- a/src/views/setting/AccountSettingDirectory.vue +++ b/src/views/setting/AccountSettingDirectory.vue @@ -63,7 +63,6 @@ async function saveStorages() { // 修改后生效 async function updatedStorage() { - await saveStorages() loadStorages() } @@ -98,9 +97,9 @@ async function saveDirectories() { // 添加媒体库目录 function addDirectory() { - let name = `目录${directories.value.length + 1}`; + let name = `目录${directories.value.length + 1}` while (directories.value.some(item => item.name === name)) { - name = `目录${parseInt(name.split('目录')[1]) + 1}`; + name = `目录${parseInt(name.split('目录')[1]) + 1}` } directories.value.push({ name: name, From 06e0f4234fb67715246f3d0b455b2ae84826f3dd Mon Sep 17 00:00:00 2001 From: jxxghp Date: Wed, 16 Oct 2024 15:23:54 +0800 Subject: [PATCH 04/10] refactor(setting): update TorrentPriorityItems in AccountSettingRule.vue --- src/views/setting/AccountSettingRule.vue | 25 +++++++++++++----------- src/views/setting/AccountSettingSite.vue | 6 +++++- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/views/setting/AccountSettingRule.vue b/src/views/setting/AccountSettingRule.vue index dd0d3f70..c1713b66 100644 --- a/src/views/setting/AccountSettingRule.vue +++ b/src/views/setting/AccountSettingRule.vue @@ -25,9 +25,10 @@ const $toast = useToast() // 种子优先规则下拉框 const TorrentPriorityItems = [ - { title: '站点排序优先', value: 'site' }, - { title: '站点上传量优先', value: 'upload' }, - { title: '资源做种数优先', value: 'seeder' }, + { title: '资源优先级', value: 'torrent' }, + { title: '站点优先级', value: 'site' }, + { title: '站点上传量', value: 'upload' }, + { title: '资源做种数', value: 'seeder' }, ] // 调用API查询自动分类配置 @@ -52,13 +53,13 @@ async function saveCustomRules() { // 添加自定义规则 function addCustomRule() { - let id = `RULE${customRules.value.length + 1}`; + let id = `RULE${customRules.value.length + 1}` while (customRules.value.some(item => item.id === id)) { - id = `RULE${parseInt(id.split('RULE')[1]) + 1}`; + id = `RULE${parseInt(id.split('RULE')[1]) + 1}` } - let name = `规则${customRules.value.length + 1}`; + let name = `规则${customRules.value.length + 1}` while (customRules.value.some(item => item.name === name)) { - name = `规则${parseInt(name.split('规则')[1]) + 1}`; + name = `规则${parseInt(name.split('规则')[1]) + 1}` } customRules.value.push({ id: id, @@ -97,9 +98,9 @@ async function saveFilterRuleGroups() { // 添加规则组 function addFilterRuleGroup() { - let name = `规则组${filterRuleGroups.value.length + 1}`; + let name = `规则组${filterRuleGroups.value.length + 1}` while (filterRuleGroups.value.some(item => item.name === name)) { - name = `规则组${parseInt(name.split('规则组')[1]) + 1}`; + name = `规则组${parseInt(name.split('规则组')[1]) + 1}` } filterRuleGroups.value.push({ name: name, @@ -245,7 +246,7 @@ onMounted(() => { 下载规则 - 按站点或做种数量优先下载。 + 同时命中多个资源时择优下载。 @@ -254,8 +255,10 @@ onMounted(() => { diff --git a/src/views/setting/AccountSettingSite.vue b/src/views/setting/AccountSettingSite.vue index 91f165b7..8df2f888 100644 --- a/src/views/setting/AccountSettingSite.vue +++ b/src/views/setting/AccountSettingSite.vue @@ -14,6 +14,8 @@ const resetSitesText = ref('重置站点数据') // 站点重置按钮可用状态 const resetSitesDisabled = ref(false) +const isPasswordVisible = ref(false) + // CookieCloud设置项 const siteSetting = ref({ COOKIECLOUD_HOST: '', @@ -155,7 +157,9 @@ onMounted(() => { Date: Wed, 16 Oct 2024 15:45:25 +0800 Subject: [PATCH 05/10] refactor(cards): update MediaServerCard.vue --- src/components/cards/MediaServerCard.vue | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/components/cards/MediaServerCard.vue b/src/components/cards/MediaServerCard.vue index 44815bdb..686e90a7 100644 --- a/src/components/cards/MediaServerCard.vue +++ b/src/components/cards/MediaServerCard.vue @@ -16,8 +16,8 @@ const props = defineProps({ // 所有媒体服务器 mediaservers: { type: Array as PropType, - required: true - } + required: true, + }, }) // 提示框 @@ -46,7 +46,12 @@ const infoItems = ref([ ]) // 同步媒体库选项 -const librariesOptions = ref<{ title: string; value: string | undefined }[]>([]) +const librariesOptions = ref<{ title: string; value: string | undefined }[]>([ + { + title: '全部', + value: 'all', + }, +]) // 媒体服务器详情弹窗 const mediaServerInfoDialog = ref(false) @@ -68,6 +73,9 @@ function openMediaServerInfoDialog() { mediaServerInfo.value = props.mediaserver mediaServerName.value = props.mediaserver.name mediaServerInfoDialog.value = true + if (!props.mediaserver.sync_libraries) { + mediaServerInfo.value.sync_libraries = ['all'] + } } // 保存详情数据 @@ -78,7 +86,7 @@ function saveMediaServerInfo() { return } // 重名判断 - if (props.mediaservers.some(item => item.name === mediaServerName.value && item!== props.mediaserver)) { + if (props.mediaservers.some(item => item.name === mediaServerName.value && item !== props.mediaserver)) { $toast.error(`【${mediaServerName.value}】已存在,请替换为其他名称`) return } @@ -148,7 +156,13 @@ async function loadLibrary(server: string) { title: item.name, value: item.id?.toString(), })) + } else { + librariesOptions.value = [] } + librariesOptions.value.unshift({ + title: '全部', + value: 'all', + }) } catch (e) { console.log(e) } @@ -174,7 +188,7 @@ onMounted(() => { - + From 004c9eadd5a217e28e903c4b931c10e074bbd6a7 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Wed, 16 Oct 2024 18:35:27 +0800 Subject: [PATCH 06/10] =?UTF-8?q?feat=EF=BC=9A=E4=BC=98=E5=8C=96=E7=AB=99?= =?UTF-8?q?=E7=82=B9=E5=8D=A1=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/types.ts | 38 +++ src/components/cards/SiteCard.vue | 211 ++++++----------- src/components/dialog/SiteAddEditDialog.vue | 119 +++++++--- .../dialog/SiteCookieUpdateDialog.vue | 114 +++++++++ src/components/dialog/SiteResourceDialog.vue | 222 ++++++++++++++++++ src/components/dialog/SiteUserDataDialog.vue | 43 ++++ src/components/table/SiteTorrentTable.vue | 202 ---------------- src/views/site/SiteCardListView.vue | 13 +- 8 files changed, 589 insertions(+), 373 deletions(-) create mode 100644 src/components/dialog/SiteCookieUpdateDialog.vue create mode 100644 src/components/dialog/SiteResourceDialog.vue create mode 100644 src/components/dialog/SiteUserDataDialog.vue delete mode 100644 src/components/table/SiteTorrentTable.vue diff --git a/src/api/types.ts b/src/api/types.ts index b4041a75..b0ad8df9 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -435,6 +435,44 @@ export interface SiteStatistic { note?: string } +// 站点用户数据 +export interface SiteUserData { + // 站点域名 + domain?: string + // 用户名 + username?: string + // 用户ID + userid?: number + // 用户等级 + user_level?: string + // 加入时间 + join_at?: string + // 积分 + bonus?: number // 默认为 0.0 + // 上传量 + upload?: number // 默认为 0 + // 下载量 + download?: number // 默认为 0 + // 分享率 + ratio?: number // 默认为 0 + // 做种数 + seeding?: number // 默认为 0 + // 下载数 + leeching?: number // 默认为 0 + // 做种体积 + seeding_size?: number // 默认为 0 + // 下载体积 + leeching_size?: number // 默认为 0 + // 做种人数, 种子大小 + seeding_info?: any[] // 默认为空数组 + // 未读消息 + message_unread?: number // 默认为 0 + // 未读消息内容 + message_unread_contents?: any[] // 默认为空数组 + // 错误信息 + err_msg?: string | null // 默认为 null +} + // 正在下载 export interface DownloadingInfo { // HASH diff --git a/src/components/cards/SiteCard.vue b/src/components/cards/SiteCard.vue index 91b6cedf..0b650648 100644 --- a/src/components/cards/SiteCard.vue +++ b/src/components/cards/SiteCard.vue @@ -2,16 +2,13 @@ import type { PropType } from 'vue' import { useToast } from 'vue-toast-notification' import SiteAddEditDialog from '../dialog/SiteAddEditDialog.vue' -import SiteTorrentTable from '../table/SiteTorrentTable.vue' -import { requiredValidator } from '@/@validators' +import SiteUserDataDialog from '../dialog/SiteUserDataDialog.vue' +import SiteResourceDialog from '../dialog/SiteResourceDialog.vue' +import SiteCookieUpdateDialog from '../dialog/SiteCookieUpdateDialog.vue' import api from '@/api' import type { Site, SiteStatistic } from '@/api/types' import { isNullOrEmptyObject } from '@/@core/utils' -import { useDisplay } from 'vuetify' -import ProgressDialog from '../dialog/ProgressDialog.vue' - -// 显示器宽度 -const display = useDisplay() +import { VCardActions, VExpandTransition, VSpacer } from 'vuetify/lib/components/index.mjs' // 输入参数 const cardProps = defineProps({ @@ -23,9 +20,6 @@ const cardProps = defineProps({ // 定义触发的自定义事件 const emit = defineEmits(['update', 'remove']) -// 密码输入 -const isPasswordVisible = ref(false) - // 图标 const siteIcon = ref('') @@ -38,9 +32,6 @@ const testButtonText = ref('测试') // 测试按钮可用性 const testButtonDisable = ref(false) -// 更新按钮可用性 -const updateButtonDisable = ref(false) - // 更新站点Cookie UA弹窗 const siteCookieDialog = ref(false) @@ -50,18 +41,11 @@ const siteEditDialog = ref(false) // 资源浏览弹窗 const resourceDialog = ref(false) -// 进度条 -const progressDialog = ref(false) +// 用户数据弹窗 +const siteUserDataDialog = ref(false) -// 进度文本 -const progressText = ref('请稍候 ...') - -// 用户名密码表单 -const userPwForm = ref({ - username: '', - password: '', - code: '', -}) +// 站点操作显示 +const siteActionShow = ref(false) // 站点使用统计 const siteStats = ref({}) @@ -113,34 +97,9 @@ async function handleResourceBrowse() { resourceDialog.value = true } -// 调用API,更新站点Cookie UA -async function updateSiteCookie() { - try { - if (!userPwForm.value.username || !userPwForm.value.password) return - - // 更新按钮状态 - siteCookieDialog.value = false - updateButtonDisable.value = true - - progressDialog.value = true - progressText.value = `正在更新 ${cardProps.site?.name} Cookie & UA ...` - - const result: { [key: string]: any } = await api.get(`site/cookie/${cardProps.site?.id}`, { - params: { - username: userPwForm.value.username, - password: userPwForm.value.password, - code: userPwForm.value.code, - }, - }) - - if (result.success) $toast.success(`${cardProps.site?.name} 更新Cookie & UA 成功!`) - else $toast.error(`${cardProps.site?.name} 更新失败:${result.message}`) - - progressDialog.value = false - updateButtonDisable.value = false - } catch (error) { - console.error(error) - } +// 打开站点用户数据弹窗 +async function handleSiteUserData() { + siteUserDataDialog.value = true } // 打开站点页面 @@ -162,17 +121,24 @@ const statColor = computed(() => { } }) -// 监听resourceDialog,如果为false则重新查询站点使用统计 -watch(resourceDialog, value => { - if (!value) getSiteStats() -}) - // 保存站点 function saveSite() { siteEditDialog.value = false emit('update') } +// 更新站点Cookie UA后的回调 +function onSiteCookieUpdated() { + siteCookieDialog.value = false + getSiteStats() +} + +// 资源浏览弹窗关闭后的回调 +function onSiteResourceDone() { + resourceDialog.value = false + getSiteStats() +} + // 装载时查询站点图标 onMounted(() => { getSiteIcon() @@ -194,7 +160,7 @@ onMounted(() => { - + {{ cardProps.site?.name }} @@ -202,7 +168,7 @@ onMounted(() => { {{ cardProps.site?.url }} - + - - - - 更新 - - - - {{ testButtonText }} - - - - 浏览 - + + + + +
+ + + 更新 + + + + {{ testButtonText }} + + + + 浏览 + + + + 数据 + +
+
mdi-drag
- - - - - - - - - - - - - - - - - - - - - - - 开始更新 - - - + { @remove="emit('remove')" @close="siteEditDialog = false" /> + + - - - - - - - - - - - + :site="cardProps.site" + @close="onSiteResourceDone" + /> - - diff --git a/src/components/dialog/SiteAddEditDialog.vue b/src/components/dialog/SiteAddEditDialog.vue index 8c870d46..56f71461 100644 --- a/src/components/dialog/SiteAddEditDialog.vue +++ b/src/components/dialog/SiteAddEditDialog.vue @@ -40,6 +40,12 @@ const siteForm = ref({ // 提示框 const $toast = useToast() +// 维护类型 +const siteType = ref('cookie') + +// 是否限流 +const isLimit = ref(false) + // 状态下拉项 const statusItems = [ { title: '启用', value: true }, @@ -106,6 +112,15 @@ async function deleteSiteInfo() { async function updateSiteInfo() { startNProgress() try { + if (isLimit.value) { + siteForm.value.limit_interval = siteForm.value.limit_interval || 0 + siteForm.value.limit_count = siteForm.value.limit_count || 0 + siteForm.value.limit_seconds = siteForm.value.limit_seconds || 0 + } else { + siteForm.value.limit_interval = 0 + siteForm.value.limit_count = 0 + siteForm.value.limit_seconds = 0 + } const result: { [key: string]: any } = await api.put('site/', siteForm.value) if (result.success) { $toast.success(`${siteForm.value?.name} 更新成功!`) @@ -120,9 +135,12 @@ async function updateSiteInfo() { doneNProgress() } -onMounted(() => { +onMounted(async () => { if (props.oper !== 'add') { - fetchSiteInfo() + await fetchSiteInfo() + if (siteForm.value.limit_interval || siteForm.value.limit_count || siteForm.value.limit_seconds) + isLimit.value = true + if (siteForm.value.apikey) siteType.value = 'api' } }) @@ -179,35 +197,69 @@ onMounted(() => { - - - - - - - - - - - + + + +
+ + Cookie +
+
+ +
+ + API +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + - + { - + - + diff --git a/src/components/dialog/SiteCookieUpdateDialog.vue b/src/components/dialog/SiteCookieUpdateDialog.vue new file mode 100644 index 00000000..a3d0d366 --- /dev/null +++ b/src/components/dialog/SiteCookieUpdateDialog.vue @@ -0,0 +1,114 @@ + + diff --git a/src/components/dialog/SiteResourceDialog.vue b/src/components/dialog/SiteResourceDialog.vue new file mode 100644 index 00000000..5ef8eeb5 --- /dev/null +++ b/src/components/dialog/SiteResourceDialog.vue @@ -0,0 +1,222 @@ + + + + diff --git a/src/components/dialog/SiteUserDataDialog.vue b/src/components/dialog/SiteUserDataDialog.vue new file mode 100644 index 00000000..67aba6ad --- /dev/null +++ b/src/components/dialog/SiteUserDataDialog.vue @@ -0,0 +1,43 @@ + + + diff --git a/src/components/table/SiteTorrentTable.vue b/src/components/table/SiteTorrentTable.vue deleted file mode 100644 index 0eeedc88..00000000 --- a/src/components/table/SiteTorrentTable.vue +++ /dev/null @@ -1,202 +0,0 @@ - - - diff --git a/src/views/site/SiteCardListView.vue b/src/views/site/SiteCardListView.vue index 06a2f8fe..63fac637 100644 --- a/src/views/site/SiteCardListView.vue +++ b/src/views/site/SiteCardListView.vue @@ -53,6 +53,12 @@ async function savaSitesPriority() { } } +// 更新站点事件时 +function onSiteSave() { + siteAddDialog.value = false + fetchData() +} + // 加载时获取数据 onBeforeMount(fetchData) @@ -102,12 +108,7 @@ onActivated(() => { v-if="siteAddDialog" v-model="siteAddDialog" oper="add" - @save=" - () => { - siteAddDialog = false - fetchData() - } - " + @save="onSiteSave" @close="siteAddDialog = false" /> From 01eaef2bf965ffdf30273929e672c86454d7363b Mon Sep 17 00:00:00 2001 From: jxxghp Date: Thu, 17 Oct 2024 12:15:49 +0800 Subject: [PATCH 07/10] =?UTF-8?q?feat=EF=BC=9A=E7=AB=99=E7=82=B9=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/@core/utils/formatters.ts | 16 +- src/api/types.ts | 4 + src/components/dialog/SiteUserDataDialog.vue | 348 ++++++++++++++++++- src/components/dialog/UserAddEditDialog.vue | 8 +- src/views/user/UserProfileView.vue | 38 +- 5 files changed, 380 insertions(+), 34 deletions(-) diff --git a/src/@core/utils/formatters.ts b/src/@core/utils/formatters.ts index 1b54972c..4a8b4d3f 100644 --- a/src/@core/utils/formatters.ts +++ b/src/@core/utils/formatters.ts @@ -60,19 +60,25 @@ export const prefixWithPlus = (value: number) => (value > 0 ? `+${value}` : valu export const formatSeason = (value: string) => (value ? `S${value.padStart(2, '0')}` : '') // 格式化为xx[TGMK]B -export function formatFileSize(bytes: number, decimals = 2) { - if (bytes < 0) throw new Error('字节数不能为负数。') +export function formatFileSize(bytes: number, decimals = 2, prefix = false) { + // 负数标记 + let negative = false + let size = bytes + if (bytes < 0) { + negative = true + size = Math.abs(bytes) + } const units = ['B', 'KB', 'MB', 'GB', 'TB'] - let size = bytes let unitIndex = 0 while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024 unitIndex++ } - - return `${size.toFixed(decimals)} ${units[unitIndex]}` + if (negative) return `-${size.toFixed(decimals)} ${units[unitIndex]}` + else + return prefix ? `+${size.toFixed(decimals)} ${units[unitIndex]}` : `${size.toFixed(decimals)} ${units[unitIndex]}` } // 将时间秒格式化为时分秒 diff --git a/src/api/types.ts b/src/api/types.ts index b0ad8df9..7ecea116 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -471,6 +471,10 @@ export interface SiteUserData { message_unread_contents?: any[] // 默认为空数组 // 错误信息 err_msg?: string | null // 默认为 null + // 更新日期 + updated_day?: string + // 更新时间 + updated_time?: string } // 正在下载 diff --git a/src/components/dialog/SiteUserDataDialog.vue b/src/components/dialog/SiteUserDataDialog.vue index 67aba6ad..0ad530e4 100644 --- a/src/components/dialog/SiteUserDataDialog.vue +++ b/src/components/dialog/SiteUserDataDialog.vue @@ -1,7 +1,11 @@ diff --git a/src/components/dialog/UserAddEditDialog.vue b/src/components/dialog/UserAddEditDialog.vue index c0ec2e1b..8b1615a5 100644 --- a/src/components/dialog/UserAddEditDialog.vue +++ b/src/components/dialog/UserAddEditDialog.vue @@ -218,14 +218,14 @@ watch(() => store.state.auth.avatar, () => { - + - 还原当前头像 + 重置 - - 重置默认头像 + + 默认 diff --git a/src/views/user/UserProfileView.vue b/src/views/user/UserProfileView.vue index f2db1bf0..6d55a61c 100644 --- a/src/views/user/UserProfileView.vue +++ b/src/views/user/UserProfileView.vue @@ -1,12 +1,12 @@