From ef32172359765d1cf31cc258dfdc9fd933380fb0 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Mon, 21 Apr 2025 12:58:55 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=AD=A3=E9=9B=86=E9=80=89?= =?UTF-8?q?=E9=A1=B9=E6=8E=92=E5=BA=8F=E9=80=BB=E8=BE=91=EF=BC=9A=E5=B0=86?= =?UTF-8?q?=E5=AD=A3=E9=9B=86=E6=8E=92=E5=BA=8F=E5=8A=9F=E8=83=BD=E4=BB=8E?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E5=B1=9E=E6=80=A7=E7=A7=BB=E8=87=B3=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E6=96=B9=E6=B3=95=EF=BC=8C=E4=BC=98=E5=8C=96=E6=AD=A3?= =?UTF-8?q?=E5=88=99=E8=A1=A8=E8=BE=BE=E5=BC=8F=E4=BB=A5=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=96=B0=E6=A0=BC=E5=BC=8F=EF=BC=8C=E7=A1=AE=E4=BF=9D=E5=9C=A8?= =?UTF-8?q?=E9=80=89=E9=A1=B9=E6=9B=B4=E6=96=B0=E6=97=B6=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=8E=92=E5=BA=8F=EF=BC=8C=E7=AE=80=E5=8C=96=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/torrent/TorrentCardListView.vue | 191 ++++++++----------- src/views/torrent/TorrentRowListView.vue | 217 ++++++++++------------ 2 files changed, 174 insertions(+), 234 deletions(-) diff --git a/src/views/torrent/TorrentCardListView.vue b/src/views/torrent/TorrentCardListView.vue index ccefa404..5d87cf01 100644 --- a/src/views/torrent/TorrentCardListView.vue +++ b/src/views/torrent/TorrentCardListView.vue @@ -76,9 +76,6 @@ const filterMenuOpen = ref(false) const currentFilter = ref('site') const currentFilterTitle = computed(() => filterTitles[currentFilter.value]) const currentFilterOptions = computed(() => { - if (currentFilter.value === 'season') { - return sortSeasonFilterOptions.value - } return filterOptions[currentFilter.value] }) @@ -86,7 +83,13 @@ const currentFilterOptions = computed(() => { function initOptions(data: Context) { const { torrent_info, meta_info } = data const optionValue = (options: Array, value: string | undefined) => { - value && !options.includes(value) && options.push(value) + if (value && !options.includes(value)) { + options.push(value) + // 如果是season选项,立即进行排序 + if (options === filterOptions.season) { + sortSeasonOptions() + } + } } optionValue(filterOptions.site, torrent_info?.site_name) optionValue(filterOptions.season, meta_info?.season_episode) @@ -97,123 +100,80 @@ function initOptions(data: Context) { optionValue(filterOptions.resolution, meta_info?.resource_pix) } -// 对季过滤选项进行排序 -const sortSeasonFilterOptions = computed(() => { +// 直接对季集选项进行排序的函数 +function sortSeasonOptions() { + if (filterOptions.season.length <= 1) { + return // 不需要排序 + } + // 预解析所有选项 const parsedOptions = filterOptions.season.map((option, index) => { - const parseSeasonEpisode = (str: string) => { - const match = str.match(/^S(\d+)(?:-S(\d+))?(?:\s*E(\d+)(?:-E(\d+))?)?$/) - - if (!match) { - // 如果字符串格式不正确,返回默认值 - return { - original: str, - seasonStart: 0, - seasonEnd: 0, - episodeStart: 0, - episodeEnd: 0, - maxSeason: 0, - maxEpisode: 0, - index, - } - } - - const seasonStart = match[1] ? parseInt(match[1], 10) : 0 - const seasonEnd = match[2] ? parseInt(match[2], 10) : 0 - const episodeStart = match[3] ? parseInt(match[3], 10) : 0 - const episodeEnd = match[4] ? parseInt(match[4], 10) : 0 - const maxSeason = seasonEnd > 0 ? seasonEnd : seasonStart - const maxEpisode = episodeEnd > 0 ? episodeEnd : episodeStart + // 修改正则表达式以适配 "S01 E07" 格式(注意季号和集号之间的空格) + const match = option.match(/^S(\d+)(?:-S(\d+))?\s*(?:E(\d+)(?:-E(\d+))?)?$/) + if (!match) { + // 格式不符合规范的放到最后 return { - original: str, - seasonStart, - seasonEnd, - episodeStart, - episodeEnd, - maxSeason, - maxEpisode, + original: option, + seasonNum: 0, + episodeNum: 0, + maxEpisodeNum: 0, + isWholeSeason: false, index, } } - return parseSeasonEpisode(option) + const seasonNum = parseInt(match[1], 10) + const episodeNum = match[3] ? parseInt(match[3], 10) : 0 + const maxEpisodeNum = match[4] ? parseInt(match[4], 10) : episodeNum + const isWholeSeason = !match[3] // 没有E部分表示整季 + + return { + original: option, + seasonNum, + episodeNum, + maxEpisodeNum, + isWholeSeason, + index, + } }) - // 定义判断是否为整季或季范围的函数 - const isWholeSeason = (parsed: (typeof parsedOptions)[0]) => - parsed.seasonStart > 0 && - (parsed.seasonEnd === 0 || parsed.seasonEnd > parsed.seasonStart) && - parsed.episodeStart === 0 && - parsed.episodeEnd === 0 + // 先对所有项进行分类 + const wholeSeasons = parsedOptions.filter(item => item.isWholeSeason) + const episodes = parsedOptions.filter(item => !item.isWholeSeason) - // 定义判断是否包含集数的函数 - const hasEpisodes = (parsed: (typeof parsedOptions)[0]) => parsed.episodeStart > 0 || parsed.episodeEnd > 0 - - // 排序逻辑 - parsedOptions.sort((a, b) => { - const aIsWhole = isWholeSeason(a) - const bIsWhole = isWholeSeason(b) - const aHasEpisodes = hasEpisodes(a) - const bHasEpisodes = hasEpisodes(b) - - // 优先级1:整季和季范围选项优先于带有集数的选项 - if (aIsWhole && !bIsWhole) return -1 - if (!aIsWhole && bIsWhole) return 1 - - // 优先级2:如果都是整季或季范围选项,按 maxSeason 降序排列 - if (aIsWhole && bIsWhole) { - if (b.maxSeason !== a.maxSeason) { - return b.maxSeason - a.maxSeason - } - // 如果 maxSeason 相同,则按原始索引 - return a.index - b.index + // 对整季按季号降序排序 + wholeSeasons.sort((a, b) => { + if (a.seasonNum !== b.seasonNum) { + return b.seasonNum - a.seasonNum // 季号降序 } - - // 优先级3:如果都是带有集数的选项,先按 maxSeason 降序,再按 maxEpisode 降序 - if (aHasEpisodes && bHasEpisodes) { - if (b.maxSeason !== a.maxSeason) { - return b.maxSeason - a.maxSeason - } - if (b.maxEpisode !== a.maxEpisode) { - return b.maxEpisode - a.maxEpisode - } - // 如果 maxSeason 和 maxEpisode 相同,则按原始索引 - return a.index - b.index - } - - // 优先级4:如果一个有集数,一个没有,优先有集数的选项 - if (aHasEpisodes && !bHasEpisodes) return -1 - if (!aHasEpisodes && bHasEpisodes) return 1 - - // 优先级5:对于没有集数且不是整季的选项,按 seasonStart 和 seasonEnd 降序排序 - if (b.seasonStart !== a.seasonStart) { - return b.seasonStart - a.seasonStart - } - if (b.seasonEnd !== a.seasonEnd) { - return b.seasonEnd - a.seasonEnd - } - - // 优先级6:按 episodeStart 和 episodeEnd 降序排序 - if (b.episodeStart !== a.episodeStart) { - return b.episodeStart - a.episodeStart - } - if (b.episodeEnd !== a.episodeEnd) { - return b.episodeEnd - a.episodeEnd - } - - // 优先级7:兜底按字母降序排列 - if (a.original !== b.original) { - return b.original.localeCompare(a.original) - } - - // 优先级8:如果所有条件都相同,则按原始索引 - return a.index - b.index + return a.index - b.index // 相同季号按原始索引 }) - // 返回排序后的原始字符串数组 - return parsedOptions.map(option => option.original) -}) + // 对单集先按季号降序排序,季号相同时按集号降序排序 + episodes.sort((a, b) => { + if (a.seasonNum !== b.seasonNum) { + return b.seasonNum - a.seasonNum // 季号降序 + } + // 使用最大集号进行排序 (对于范围如 E01-E06) + const aMaxEp = a.maxEpisodeNum || a.episodeNum + const bMaxEp = b.maxEpisodeNum || b.episodeNum + if (aMaxEp !== bMaxEp) { + return bMaxEp - aMaxEp // 集号降序 + } + // 如果最大集号相同,再比较起始集号 + if (a.episodeNum !== b.episodeNum) { + return b.episodeNum - a.episodeNum + } + return a.index - b.index // 都相同时按原始索引 + }) + + // 合并结果:整季在前,单集在后 + const sortedOptions = [...wholeSeasons, ...episodes].map(item => item.original) + + // 直接更新 filterOptions.season + filterOptions.season = sortedOptions +} // 计算分组后的列表 onMounted(() => { @@ -236,6 +196,11 @@ onMounted(() => { } }) groupedDataList.value = groupMap + + // 确保季集选项排序 + if (filterOptions.season.length > 0) { + sortSeasonOptions() + } }) // 修改watch监听,同时监听排序字段的变化 @@ -326,6 +291,11 @@ function toggleFilterMenu(key: string) { } else { currentFilter.value = key filterMenuOpen.value = true + + // 如果是季集选项,确保已排序 + if (key === 'season' && filterOptions.season.length > 0) { + sortSeasonOptions() + } } } @@ -343,11 +313,8 @@ function clearFilter(key: string) { // 全选某个过滤项 function selectAll(key: string) { - if (key === 'season') { - filterForm[key] = [...sortSeasonFilterOptions.value] - } else { - filterForm[key] = [...filterOptions[key]] - } + // 不再需要特殊处理季集选项 + filterForm[key] = [...filterOptions[key]] } // 计算已选择的过滤条件数量 diff --git a/src/views/torrent/TorrentRowListView.vue b/src/views/torrent/TorrentRowListView.vue index 44550666..14a63b29 100644 --- a/src/views/torrent/TorrentRowListView.vue +++ b/src/views/torrent/TorrentRowListView.vue @@ -55,124 +55,6 @@ const filterOptions: Record = reactive({ releaseGroup: [] as string[], }) -// 对季过滤选项进行排序 -const sortSeasonFilterOptions = computed(() => { - // 预解析所有选项 - const parsedOptions = filterOptions.season.map((option, index) => { - const parseSeasonEpisode = (str: string) => { - const match = str.match(/^S(\d+)(?:-S(\d+))?(?:\s*E(\d+)(?:-E(\d+))?)?$/) - - if (!match) { - // 如果字符串格式不正确,返回默认值 - return { - original: str, - seasonStart: 0, - seasonEnd: 0, - episodeStart: 0, - episodeEnd: 0, - maxSeason: 0, - maxEpisode: 0, - index, - } - } - - const seasonStart = match[1] ? parseInt(match[1], 10) : 0 - const seasonEnd = match[2] ? parseInt(match[2], 10) : 0 - const episodeStart = match[3] ? parseInt(match[3], 10) : 0 - const episodeEnd = match[4] ? parseInt(match[4], 10) : 0 - const maxSeason = seasonEnd > 0 ? seasonEnd : seasonStart - const maxEpisode = episodeEnd > 0 ? episodeEnd : episodeStart - - return { - original: str, - seasonStart, - seasonEnd, - episodeStart, - episodeEnd, - maxSeason, - maxEpisode, - index, - } - } - - return parseSeasonEpisode(option) - }) - - // 定义判断是否为整季或季范围的函数 - const isWholeSeason = (parsed: (typeof parsedOptions)[0]) => - parsed.seasonStart > 0 && - (parsed.seasonEnd === 0 || parsed.seasonEnd > parsed.seasonStart) && - parsed.episodeStart === 0 && - parsed.episodeEnd === 0 - - // 定义判断是否包含集数的函数 - const hasEpisodes = (parsed: (typeof parsedOptions)[0]) => parsed.episodeStart > 0 || parsed.episodeEnd > 0 - - // 排序逻辑 - parsedOptions.sort((a, b) => { - const aIsWhole = isWholeSeason(a) - const bIsWhole = isWholeSeason(b) - const aHasEpisodes = hasEpisodes(a) - const bHasEpisodes = hasEpisodes(b) - - // 优先级1:整季和季范围选项优先于带有集数的选项 - if (aIsWhole && !bIsWhole) return -1 - if (!aIsWhole && bIsWhole) return 1 - - // 优先级2:如果都是整季或季范围选项,按 maxSeason 降序排列 - if (aIsWhole && bIsWhole) { - if (b.maxSeason !== a.maxSeason) { - return b.maxSeason - a.maxSeason - } - // 如果 maxSeason 相同,则按原始索引 - return a.index - b.index - } - - // 优先级3:如果都是带有集数的选项,先按 maxSeason 降序,再按 maxEpisode 降序 - if (aHasEpisodes && bHasEpisodes) { - if (b.maxSeason !== a.maxSeason) { - return b.maxSeason - a.maxSeason - } - if (b.maxEpisode !== a.maxEpisode) { - return b.maxEpisode - a.maxEpisode - } - // 如果 maxSeason 和 maxEpisode 相同,则按原始索引 - return a.index - b.index - } - - // 优先级4:如果一个有集数,一个没有,优先有集数的选项 - if (aHasEpisodes && !bHasEpisodes) return -1 - if (!aHasEpisodes && bHasEpisodes) return 1 - - // 优先级5:对于没有集数且不是整季的选项,按 seasonStart 和 seasonEnd 降序排序 - if (b.seasonStart !== a.seasonStart) { - return b.seasonStart - a.seasonStart - } - if (b.seasonEnd !== a.seasonEnd) { - return b.seasonEnd - a.seasonEnd - } - - // 优先级6:按 episodeStart 和 episodeEnd 降序排序 - if (b.episodeStart !== a.episodeStart) { - return b.episodeStart - a.episodeStart - } - if (b.episodeEnd !== a.episodeEnd) { - return b.episodeEnd - a.episodeEnd - } - - // 优先级7:兜底按字母降序排列 - if (a.original !== b.original) { - return b.original.localeCompare(a.original) - } - - // 优先级8:如果所有条件都相同,则按原始索引 - return a.index - b.index - }) - - // 返回排序后的原始字符串数组 - return parsedOptions.map(option => option.original) -}) - // 排序字段 const sortField = ref('default') @@ -221,7 +103,14 @@ function clearAllFilters() { function initOptions(data: Context) { const { torrent_info, meta_info } = data const optionValue = (options: Array, value: string | undefined) => { - value && !options.includes(value) && options.push(value) + if (value && !options.includes(value)) { + options.push(value) + // 如果是season选项,立即触发重新计算 + if (options === filterOptions.season) { + // 季集选项排序 + sortSeasonOptions() + } + } } optionValue(filterOptions.site, torrent_info?.site_name) @@ -233,6 +122,81 @@ function initOptions(data: Context) { optionValue(filterOptions.resolution, meta_info?.resource_pix) } +// 直接在组件中添加季集排序函数,而不是用计算属性 +function sortSeasonOptions() { + if (filterOptions.season.length <= 1) { + return // 不需要排序 + } + + // 预解析所有选项 + const parsedOptions = filterOptions.season.map((option, index) => { + // 修改正则表达式以适配 "S01 E07" 格式(注意季号和集号之间的空格) + const match = option.match(/^S(\d+)(?:-S(\d+))?\s*(?:E(\d+)(?:-E(\d+))?)?$/) + + if (!match) { + // 格式不符合规范的放到最后 + return { + original: option, + seasonNum: 0, + episodeNum: 0, + maxEpisodeNum: 0, + isWholeSeason: false, + index, + } + } + + const seasonNum = parseInt(match[1], 10) + const episodeNum = match[3] ? parseInt(match[3], 10) : 0 + const maxEpisodeNum = match[4] ? parseInt(match[4], 10) : episodeNum + const isWholeSeason = !match[3] // 没有E部分表示整季 + + return { + original: option, + seasonNum, + episodeNum, + maxEpisodeNum, + isWholeSeason, + index, + } + }) + + // 先对所有项进行分类 + const wholeSeasons = parsedOptions.filter(item => item.isWholeSeason) + const episodes = parsedOptions.filter(item => !item.isWholeSeason) + + // 对整季按季号降序排序 + wholeSeasons.sort((a, b) => { + if (a.seasonNum !== b.seasonNum) { + return b.seasonNum - a.seasonNum // 季号降序 + } + return a.index - b.index // 相同季号按原始索引 + }) + + // 对单集先按季号降序排序,季号相同时按集号降序排序 + episodes.sort((a, b) => { + if (a.seasonNum !== b.seasonNum) { + return b.seasonNum - a.seasonNum // 季号降序 + } + // 使用最大集号进行排序 (对于范围如 E01-E06) + const aMaxEp = a.maxEpisodeNum || a.episodeNum + const bMaxEp = b.maxEpisodeNum || b.episodeNum + if (aMaxEp !== bMaxEp) { + return bMaxEp - aMaxEp // 集号降序 + } + // 如果最大集号相同,再比较起始集号 + if (a.episodeNum !== b.episodeNum) { + return b.episodeNum - a.episodeNum + } + return a.index - b.index // 都相同时按原始索引 + }) + + // 合并结果:整季在前,单集在后 + const sortedOptions = [...wholeSeasons, ...episodes].map(item => item.original) + + // 直接更新 filterOptions.season + filterOptions.season = sortedOptions +} + // 修改watch监听,同时监听排序字段的变化 watch([filterForm, sortField], filterData) @@ -296,6 +260,12 @@ function filterData() { // 保存剩余数据 dataList.value = filteredData.slice(20) } + + // 确保在数据筛选完成后重新排序季集选项 + if (filterOptions.season.length > 0) { + // 直接排序,不再使用延时 + sortSeasonOptions() + } } // 过滤菜单相关 @@ -303,9 +273,7 @@ const filterMenuOpen = ref(false) const currentFilter = ref('site') const currentFilterTitle = computed(() => filterTitles[currentFilter.value]) const currentFilterOptions = computed(() => { - if (currentFilter.value === 'season') { - return sortSeasonFilterOptions.value - } + // 季集选项不再需要特殊处理,直接从 filterOptions 获取 return filterOptions[currentFilter.value] }) @@ -344,6 +312,11 @@ function toggleFilterMenu(key: string) { } else { currentFilter.value = key filterMenuOpen.value = true + + // 如果是季集选项,确保已排序 + if (key === 'season' && filterOptions.season.length > 0) { + sortSeasonOptions() + } } }