feat(search): 筛选选项只列结果里有的值,季集合并成一个维度放最前
对照 MP useTorrentFilter:分面选项按实际结果生成,计数为 0 的不再列出(已选的值仍列出,可删除); 新增季集维度 seasonEpisode(MP season_episode 写法 `S01` / `S01-S02` / `S02 E14` / `S01 E25-E48`), 整季在前、单集在后、季号新的在前,在筛选条最前。旧的季 / 集维度保留(旧链接仍生效,以标签列出)。 搜索结果页与站点资源 Tab 共用这套口径。 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
18ee466664
commit
f6d976a963
7 files changed
+181
-91
No files matched your search
@@ -20,7 +20,8 @@ import {
|
||||
} from "./quick-filter-state.ts";
|
||||
|
||||
// 资源快速筛选条(SEARCH-3,对照 MP 搜索结果筛选):一行可横向滚动的维度按钮,点开多选;
|
||||
// 选项带计数(按其它维度的已选条件),0 计数置灰;已选条件以可移除标签列出,只有一个「清除」。
|
||||
// 选项只列结果里有的值(服务端不给 0 计数的选项),带计数(按其它维度的已选条件);已选但当前为 0 的置灰;
|
||||
// 已选条件以可移除标签列出,只有一个「清除」。季集在最前(整季在前、单集在后)。
|
||||
// 桌面用弹层,手机用底部抽屉。搜索结果页与站点资源 Tab 共用。
|
||||
// POLISH-1:H&R 是分面维度;页面可在同一行右侧放大小 / 排序(aside),手机上收进「大小与排序」抽屉。
|
||||
// SEARCH-4:对齐 MP 后维度变多,常用的留在筛选条上,其余(音频、字幕、配音、特性、年份、分类、大小、做种、H&R)
|
||||
@@ -62,7 +63,7 @@ export function QuickFilterBar({
|
||||
const dims = g.dims.filter((d) => !hide.includes(d.dim));
|
||||
if (!dims.length) return false;
|
||||
const opts = dims.flatMap((d) => facets?.[d.dim] ?? []);
|
||||
// 站点只有一个时不用筛;季/集只在有剧集资源时出现
|
||||
// 站点只有一个时不用筛;季集只在有剧集资源时出现
|
||||
if (g.key === "site" && opts.length <= 1 && !selectedCount(value, ["site"])) return false;
|
||||
return (
|
||||
opts.length > 0 ||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
// SEARCH-3 快速筛选:qf 参数、选择切换、已选标签、本机记忆只存固定档位
|
||||
|
||||
const empty = {
|
||||
seasonEpisode: [],
|
||||
resolution: [],
|
||||
videoCodec: [],
|
||||
releaseType: [],
|
||||
@@ -41,9 +42,11 @@ describe("快速筛选状态", () => {
|
||||
expect(formatQuickFilter({ hdr: ["DV"], resolution: ["2160p"], releaseGroup: ["A,B"] })).toBe(
|
||||
"hdr:DV;resolution:2160p;releaseGroup:A%2CB",
|
||||
);
|
||||
expect(formatQuickFilter({ seeders: ["0"], audioCodec: ["TrueHD"], site: ["s"] })).toBe(
|
||||
"site:s;audioCodec:TrueHD;seeders:0",
|
||||
expect(formatQuickFilter({ seeders: ["0"], audioCodec: ["TrueHD"], site: ["s"], seasonEpisode: ["S01"] })).toBe(
|
||||
"seasonEpisode:S01;site:s;audioCodec:TrueHD;seeders:0",
|
||||
);
|
||||
// 旧的季 / 集维度排在最后,仍会带上
|
||||
expect(formatQuickFilter({ season: ["2"], resolution: ["2160p"] })).toBe("resolution:2160p;season:2");
|
||||
expect(formatQuickFilter({ resolution: [] })).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -57,11 +60,16 @@ describe("快速筛选状态", () => {
|
||||
expect(selectedCount(clearDims(s, ["season", "episode"]))).toBe(1);
|
||||
});
|
||||
|
||||
it("已选标签取分面里的选项名,取不到用原值", () => {
|
||||
const facets = { ...empty, releaseType: [{ value: "encode", label: "蓝光压制", count: 3 }] };
|
||||
expect(selectedTags({ releaseType: ["encode"], season: ["2"] }, facets)).toEqual([
|
||||
{ dim: "season", value: "2", dimLabel: "季", label: "2" },
|
||||
it("已选标签取分面里的选项名,取不到用原值;季集在最前,旧的季 / 集在最后", () => {
|
||||
const facets = {
|
||||
...empty,
|
||||
releaseType: [{ value: "encode", label: "蓝光压制", count: 3 }],
|
||||
seasonEpisode: [{ value: "S02 E14", label: "S02 E14", count: 1 }],
|
||||
};
|
||||
expect(selectedTags({ releaseType: ["encode"], season: ["2"], seasonEpisode: ["S02 E14"] }, facets)).toEqual([
|
||||
{ dim: "seasonEpisode", value: "S02 E14", dimLabel: "季集", label: "S02 E14" },
|
||||
{ dim: "releaseType", value: "encode", dimLabel: "质量", label: "蓝光压制" },
|
||||
{ dim: "season", value: "2", dimLabel: "季", label: "2" },
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -86,7 +94,7 @@ describe("快速筛选状态", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("「更多筛选」收的是 MP 筛选条之外的维度", () => {
|
||||
it("「更多筛选」收的是 MP 筛选条之外的维度;季集放最前", () => {
|
||||
expect(MORE_DIMS).toEqual([
|
||||
"audioCodec",
|
||||
"subtitle",
|
||||
@@ -99,8 +107,8 @@ describe("快速筛选状态", () => {
|
||||
"hr",
|
||||
]);
|
||||
expect(QUICK_GROUPS.filter((g) => !g.more).map((g) => g.label)).toEqual([
|
||||
"季集",
|
||||
"站点",
|
||||
"季/集",
|
||||
"促销",
|
||||
"编码",
|
||||
"质量",
|
||||
|
||||
@@ -8,9 +8,10 @@ export type QuickDim = keyof ReleaseQuickFacets;
|
||||
export type QuickSelection = Partial<Record<QuickDim, string[]>>;
|
||||
|
||||
/**
|
||||
* 筛选条上的一个入口;「季/集」一个入口里分两节。
|
||||
* 筛选条上的一个入口。
|
||||
* more = 收进「更多筛选」(SEARCH-4):常用维度按 MP 搜索结果筛选条的顺序放在前面
|
||||
* (站点、季集、促销、视频编码、质量、分辨率、制作组;HDR 在 MP 里并在「质量」,这里单列紧跟其后)。
|
||||
* (季集放最前,再是站点、促销、视频编码、质量、分辨率、制作组;HDR 在 MP 里并在「质量」,这里单列紧跟其后)。
|
||||
* 季集是一个维度(MP 的 season_episode):整季在前、单集在后,服务端排好序。
|
||||
*/
|
||||
export type QuickGroup = { key: string; label: string; dims: { dim: QuickDim; label: string }[]; more?: boolean };
|
||||
|
||||
@@ -22,15 +23,8 @@ const one = (dim: QuickDim, label: string, more?: boolean): QuickGroup => ({
|
||||
});
|
||||
|
||||
export const QUICK_GROUPS: QuickGroup[] = [
|
||||
one("seasonEpisode", "季集"),
|
||||
one("site", "站点"),
|
||||
{
|
||||
key: "seasonEpisode",
|
||||
label: "季/集",
|
||||
dims: [
|
||||
{ dim: "season", label: "季" },
|
||||
{ dim: "episode", label: "集" },
|
||||
],
|
||||
},
|
||||
one("promotion", "促销"),
|
||||
one("videoCodec", "编码"),
|
||||
one("releaseType", "质量"),
|
||||
@@ -51,7 +45,16 @@ export const QUICK_GROUPS: QuickGroup[] = [
|
||||
export const MORE_GROUPS = QUICK_GROUPS.filter((g) => g.more);
|
||||
export const MORE_DIMS: QuickDim[] = MORE_GROUPS.flatMap((g) => g.dims.map((d) => d.dim));
|
||||
|
||||
const DIM_ORDER: QuickDim[] = QUICK_GROUPS.flatMap((g) => g.dims.map((d) => d.dim));
|
||||
/** 旧的季 / 集两个维度:筛选条上不再出现,旧链接带着时仍生效,并以可删除的标签列出 */
|
||||
const LEGACY_DIMS: { dim: QuickDim; label: string }[] = [
|
||||
{ dim: "season", label: "季" },
|
||||
{ dim: "episode", label: "集" },
|
||||
];
|
||||
|
||||
const DIM_ORDER: QuickDim[] = [
|
||||
...QUICK_GROUPS.flatMap((g) => g.dims.map((d) => d.dim)),
|
||||
...LEGACY_DIMS.map((d) => d.dim),
|
||||
];
|
||||
|
||||
/** 记住上次选择的维度:与具体结果无关的档位;站点、制作组、季集、年份、分类随搜索变化,不记 */
|
||||
const PERSISTED: QuickDim[] = [
|
||||
@@ -97,7 +100,9 @@ export function selectedTags(
|
||||
sel: QuickSelection,
|
||||
facets: ReleaseQuickFacets | undefined,
|
||||
): { dim: QuickDim; value: string; dimLabel: string; label: string }[] {
|
||||
const dimLabel = new Map(QUICK_GROUPS.flatMap((g) => g.dims.map((d) => [d.dim, d.label] as const)));
|
||||
const dimLabel = new Map(
|
||||
[...QUICK_GROUPS.flatMap((g) => g.dims), ...LEGACY_DIMS].map((d) => [d.dim, d.label] as const),
|
||||
);
|
||||
return DIM_ORDER.flatMap((dim) =>
|
||||
(sel[dim] ?? []).map((value) => ({
|
||||
dim,
|
||||
|
||||
Reference in new issue
Block a user