From d703909177a56821fb91b5c36d3836b4f663406a Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Sun, 6 Oct 2024 17:37:01 +0800 Subject: [PATCH] refactor(search): optimize sorting logic for season filter options --- src/views/discover/TorrentCardListView.vue | 135 +++++++++++++++++---- src/views/discover/TorrentRowListView.vue | 135 +++++++++++++++++---- 2 files changed, 218 insertions(+), 52 deletions(-) diff --git a/src/views/discover/TorrentCardListView.vue b/src/views/discover/TorrentCardListView.vue index a646b16b..c55b68bb 100644 --- a/src/views/discover/TorrentCardListView.vue +++ b/src/views/discover/TorrentCardListView.vue @@ -71,37 +71,120 @@ function initOptions(data: Context) { // 对季过滤选项进行排序 const sortSeasonFilterOptions = computed(() => { - return seasonFilterOptions.value.sort((a, b) => { - // 按季,集降序排序 + // 预解析所有选项 + const parsedOptions = seasonFilterOptions.value.map((option, index) => { const parseSeasonEpisode = (str: string) => { - const seasonRangeMatch = str.match(/S(\d+)(?:-S(\d+))?/) - const episodeRangeMatch = str.match(/E(\d+)(?:-E(\d+))?/) + 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 { - seasonStart: seasonRangeMatch?.[1] ? parseInt(seasonRangeMatch[1]) : 0, - seasonEnd: seasonRangeMatch?.[2] ? parseInt(seasonRangeMatch[2]) : 0, - episodeStart: episodeRangeMatch?.[1] ? parseInt(episodeRangeMatch[1]) : 0, - episodeEnd: episodeRangeMatch?.[2] ? parseInt(episodeRangeMatch[2]) : 0, + original: str, + seasonStart, + seasonEnd, + episodeStart, + episodeEnd, + maxSeason, + maxEpisode, + index, } } - const parsedA = parseSeasonEpisode(a) - const parsedB = parseSeasonEpisode(b) - // 先按季降序排序 - if (parsedB.seasonStart !== parsedA.seasonStart) { - return parsedB.seasonStart - parsedA.seasonStart - } - if (parsedB.seasonEnd !== parsedA.seasonEnd) { - return parsedB.seasonEnd - parsedA.seasonEnd - } - // 按集降序排序 - if (parsedB.episodeStart !== parsedA.episodeStart) { - return parsedB.episodeStart - parsedA.episodeStart - } - if (parsedB.episodeEnd !== parsedA.episodeEnd) { - return parsedB.episodeEnd - parsedA.episodeEnd - } - // 兜底 - return b.localeCompare(a) + + 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) }) // 计算分组后的列表 diff --git a/src/views/discover/TorrentRowListView.vue b/src/views/discover/TorrentRowListView.vue index 08b4f16b..4aada805 100644 --- a/src/views/discover/TorrentRowListView.vue +++ b/src/views/discover/TorrentRowListView.vue @@ -81,37 +81,120 @@ function initOptions(data: Context) { // 对季过滤选项进行排序 const sortSeasonFilterOptions = computed(() => { - return seasonFilterOptions.value.sort((a, b) => { - // 按季,集降序排序 + // 预解析所有选项 + const parsedOptions = seasonFilterOptions.value.map((option, index) => { const parseSeasonEpisode = (str: string) => { - const seasonRangeMatch = str.match(/S(\d+)(?:-S(\d+))?/) - const episodeRangeMatch = str.match(/E(\d+)(?:-E(\d+))?/) + 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 { - seasonStart: seasonRangeMatch?.[1] ? parseInt(seasonRangeMatch[1]) : 0, - seasonEnd: seasonRangeMatch?.[2] ? parseInt(seasonRangeMatch[2]) : 0, - episodeStart: episodeRangeMatch?.[1] ? parseInt(episodeRangeMatch[1]) : 0, - episodeEnd: episodeRangeMatch?.[2] ? parseInt(episodeRangeMatch[2]) : 0, + original: str, + seasonStart, + seasonEnd, + episodeStart, + episodeEnd, + maxSeason, + maxEpisode, + index, } } - const parsedA = parseSeasonEpisode(a) - const parsedB = parseSeasonEpisode(b) - // 先按季降序排序 - if (parsedB.seasonStart !== parsedA.seasonStart) { - return parsedB.seasonStart - parsedA.seasonStart - } - if (parsedB.seasonEnd !== parsedA.seasonEnd) { - return parsedB.seasonEnd - parsedA.seasonEnd - } - // 按集降序排序 - if (parsedB.episodeStart !== parsedA.episodeStart) { - return parsedB.episodeStart - parsedA.episodeStart - } - if (parsedB.episodeEnd !== parsedA.episodeEnd) { - return parsedB.episodeEnd - parsedA.episodeEnd - } - // 兜底 - return b.localeCompare(a) + + 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) }) // 排序