@@ -0,0 +1,161 @@
|
||||
import { pickPlayletCardTags, type CardTagItem } from "@/components/common/card-tags";
|
||||
import type { BadgeTone } from "@/components/home/data";
|
||||
import { resolveContentBadge } from "@/components/home/section-heading";
|
||||
import { formatHeat } from "@/components/ranking/data";
|
||||
import type { PlayletListItem, PlayletListResp, PlayletThemeItem } from "@/lib/api/types";
|
||||
|
||||
export const genres = ["全部", "甜宠", "逆袭", "霸总", "重生", "豪门", "古装", "玄幻", "都市"] as const;
|
||||
/** 路由排序段(英文);展示文案见 sortLabels */
|
||||
export const sorts = ["hot", "latest"] as const;
|
||||
export const sortLabels: Record<(typeof sorts)[number], string> = {
|
||||
hot: "热度优先",
|
||||
latest: "最近更新",
|
||||
};
|
||||
/** 兼容旧中文路径 → 英文 sort */
|
||||
export const sortPathAliases: Record<string, (typeof sorts)[number]> = {
|
||||
hot: "hot",
|
||||
latest: "latest",
|
||||
热度优先: "hot",
|
||||
最近更新: "latest",
|
||||
};
|
||||
export const regions = ["全部", "内地", "中国香港", "中国台湾", "韩国", "日本", "欧美"] as const;
|
||||
|
||||
export type Genre = (typeof genres)[number];
|
||||
export type Sort = (typeof sorts)[number];
|
||||
export type Region = (typeof regions)[number];
|
||||
|
||||
export type ShortDramaItem =
|
||||
| {
|
||||
kind: "drama";
|
||||
id: string;
|
||||
slug?: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
/** 左下角标签,最多 3 个(优先带 id 以便跳转) */
|
||||
tags: CardTagItem[];
|
||||
heat: string;
|
||||
episodes: string;
|
||||
badge?: BadgeTone;
|
||||
cover: string;
|
||||
/** 覆盖默认详情/播放链接(如历史继续看到某集) */
|
||||
href?: string;
|
||||
}
|
||||
| {
|
||||
kind: "ad";
|
||||
id: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
cover: string;
|
||||
href?: string;
|
||||
};
|
||||
|
||||
export const PAGE_SIZE = 24;
|
||||
export const TOTAL_PAGES = 11;
|
||||
|
||||
export function shortDramaSortType(sort: Sort): 1 | 2 {
|
||||
return sort === "latest" ? 2 : 1;
|
||||
}
|
||||
|
||||
export function mapPlayletListItem(item: PlayletListItem): ShortDramaItem | null {
|
||||
if (item.type === 2) {
|
||||
return {
|
||||
kind: "ad",
|
||||
id: `ad-${item.advertiseId || item.id || item.link || "unknown"}`,
|
||||
title: (item.title || "").trim() || "广告",
|
||||
subtitle: "",
|
||||
cover: (item.cover || "").trim(),
|
||||
href: (item.link || "").trim() || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
if (!item.id) return null;
|
||||
const tags = pickPlayletCardTags(item);
|
||||
const slug = (item.slug || "").trim();
|
||||
return {
|
||||
kind: "drama",
|
||||
id: String(item.id),
|
||||
slug: slug || undefined,
|
||||
title: (item.title || "").trim() || "未命名",
|
||||
subtitle: (item.desc || "").trim(),
|
||||
tags,
|
||||
heat: formatHeat(item.heatValue ?? item.countBrowse),
|
||||
episodes: item.totalEpisode ? `${item.totalEpisode}集` : "",
|
||||
badge: resolveContentBadge(item),
|
||||
cover: (item.cover || "").trim(),
|
||||
};
|
||||
}
|
||||
|
||||
export type ShortDramaListResult = {
|
||||
items: ShortDramaItem[];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalCount: number;
|
||||
totalPages: number;
|
||||
sort: Sort;
|
||||
themes: PlayletThemeItem[];
|
||||
themeSlug?: string;
|
||||
};
|
||||
|
||||
export function buildShortDramaList(
|
||||
data: PlayletListResp | null | undefined,
|
||||
sort: Sort,
|
||||
fallbackPage = 1,
|
||||
fallbackPageSize = PAGE_SIZE,
|
||||
options?: { themes?: PlayletThemeItem[]; themeSlug?: string },
|
||||
): ShortDramaListResult {
|
||||
const page = data?.pageNum || fallbackPage;
|
||||
const pageSize = data?.pageSize || fallbackPageSize;
|
||||
const totalCount = data?.totalCount || 0;
|
||||
const items = (data?.list ?? [])
|
||||
.map(mapPlayletListItem)
|
||||
.filter((item): item is ShortDramaItem => Boolean(item));
|
||||
|
||||
return {
|
||||
items,
|
||||
page,
|
||||
pageSize,
|
||||
totalCount,
|
||||
totalPages: Math.max(1, Math.ceil(totalCount / pageSize) || 1),
|
||||
sort,
|
||||
themes: options?.themes ?? [],
|
||||
themeSlug: options?.themeSlug,
|
||||
};
|
||||
}
|
||||
|
||||
/** @deprecated 本地 mock,供「更多」等未接接口页面临时使用 */
|
||||
const adSlots = new Set([4, 9, 14, 18, 23]);
|
||||
const freshSlots = new Set([3, 11, 21]);
|
||||
|
||||
export function getShortDramas(genre: Genre, sort: Sort, page: number, region: Region = "全部"): ShortDramaItem[] {
|
||||
const seed = `${genres.indexOf(genre)}-${sorts.indexOf(sort)}-${regions.indexOf(region)}-${page}`;
|
||||
|
||||
return Array.from({ length: PAGE_SIZE }, (_, index): ShortDramaItem => {
|
||||
const id = `short-${seed}-${index}`;
|
||||
const cover = "";
|
||||
|
||||
if (adSlots.has(index)) {
|
||||
return { kind: "ad", id, title: "广告文案", subtitle: "我是副标题", cover };
|
||||
}
|
||||
|
||||
return {
|
||||
kind: "drama",
|
||||
id,
|
||||
title: "有恩必报",
|
||||
subtitle: "权谋交锋爱意藏在局中",
|
||||
tags: [{ name: genre === "全部" ? "标签" : genre }],
|
||||
heat: "25.8万 热度",
|
||||
episodes: "30集",
|
||||
badge: freshSlots.has(index) ? "fresh" : "original",
|
||||
cover,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/** 有 slug 时路径用 slug,否则回退 id */
|
||||
export function dramaPathKey(id: string | number, slug?: string | null) {
|
||||
return (slug?.trim() || String(id)).trim();
|
||||
}
|
||||
|
||||
export function dramaHref(id: string | number, slug?: string | null) {
|
||||
return `/short-drama/${dramaPathKey(id, slug)}`;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { sortPathAliases, sorts, type Sort } from "./data";
|
||||
|
||||
/**
|
||||
* 短剧列表路径:
|
||||
* - /short-drama/{sort} sort = hot | latest
|
||||
* - /short-drama/{sort}/{page}
|
||||
* - /short-drama/{sort}/{themeSlug}
|
||||
* - /short-drama/{sort}/{themeSlug}/{page}
|
||||
*/
|
||||
export function shortDramaListHref(sort: Sort = "hot", page = 1, themeSlug?: string | null) {
|
||||
const base = `/short-drama/${sort}`;
|
||||
const slug = themeSlug?.trim();
|
||||
if (slug) {
|
||||
const withTheme = `${base}/${encodeURIComponent(slug)}`;
|
||||
return page > 1 ? `${withTheme}/${page}` : withTheme;
|
||||
}
|
||||
return page > 1 ? `${base}/${page}` : base;
|
||||
}
|
||||
|
||||
export function resolveShortDramaSort(value: string): Sort | null {
|
||||
try {
|
||||
return sortPathAliases[decodeURIComponent(value)] ?? null;
|
||||
} catch {
|
||||
return sortPathAliases[value] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
export function isShortDramaSort(value: string): value is Sort {
|
||||
return resolveShortDramaSort(value) != null;
|
||||
}
|
||||
|
||||
export function parseShortDramaSortSegment(value: string): Sort | null {
|
||||
return resolveShortDramaSort(value);
|
||||
}
|
||||
|
||||
function decodeSegment(value: string) {
|
||||
try {
|
||||
return decodeURIComponent(value);
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function parsePageNumber(value: string): number | null {
|
||||
const num = Number(value);
|
||||
if (!Number.isInteger(num) || num < 1) return null;
|
||||
return num;
|
||||
}
|
||||
|
||||
export type ShortDramaListPath = {
|
||||
themeSlug?: string;
|
||||
page: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析列表尾段:纯数字视为页码,否则视为题材 slug。
|
||||
* 非法组合返回 null → notFound。
|
||||
*/
|
||||
export function parseShortDramaListPath(segments: string[] | undefined): ShortDramaListPath | null {
|
||||
if (!segments?.length) return { page: 1 };
|
||||
|
||||
if (segments.length === 1) {
|
||||
const only = decodeSegment(segments[0]);
|
||||
const page = parsePageNumber(only);
|
||||
if (page != null) return { page };
|
||||
if (!only) return null;
|
||||
return { themeSlug: only, page: 1 };
|
||||
}
|
||||
|
||||
if (segments.length === 2) {
|
||||
const themeSlug = decodeSegment(segments[0]).trim();
|
||||
const page = parsePageNumber(decodeSegment(segments[1]));
|
||||
if (!themeSlug || page == null) return null;
|
||||
// 题材段不能是纯页码形态(避免 /sort/2/3 歧义)
|
||||
if (parsePageNumber(themeSlug) != null) return null;
|
||||
return { themeSlug, page };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export { sorts };
|
||||
@@ -0,0 +1,144 @@
|
||||
import Link from "next/link";
|
||||
|
||||
import { CardTags } from "@/components/common/card-tags";
|
||||
import { DecryptedImage } from "@/components/common/decrypted-image";
|
||||
import {
|
||||
MEDIA_CARD_COVER,
|
||||
MEDIA_CARD_DESC,
|
||||
MEDIA_CARD_DESC_SM,
|
||||
MEDIA_CARD_EPISODES,
|
||||
MEDIA_CARD_EPISODES_H5,
|
||||
MEDIA_CARD_FOOTER_H5,
|
||||
MEDIA_CARD_INFO_H5,
|
||||
MEDIA_CARD_META,
|
||||
MEDIA_CARD_ROOT,
|
||||
MEDIA_CARD_TITLE_H5,
|
||||
MEDIA_CARD_TITLE_PC,
|
||||
} from "@/components/common/media-card-hover";
|
||||
import { CoverRankBadge, RankNumber } from "@/components/common/rank-number";
|
||||
import { Badge } from "@/components/home/section-heading";
|
||||
import { IMG_PLACEHOLDER } from "@/lib/image/decrypt";
|
||||
|
||||
import { dramaHref, type ShortDramaItem } from "./data";
|
||||
|
||||
export function ShortDramaCard({ item, rank }: { item: ShortDramaItem; rank?: number }) {
|
||||
if (item.kind === "ad") {
|
||||
return (
|
||||
<a
|
||||
href={item.href || "#download"}
|
||||
target={item.href ? "_blank" : undefined}
|
||||
rel={item.href ? "sponsored nofollow noopener noreferrer" : undefined}
|
||||
className={`block w-[266px] ${MEDIA_CARD_ROOT}`}
|
||||
>
|
||||
<div className="relative h-[355px] overflow-hidden rounded-t-lg">
|
||||
<DecryptedImage src={item.cover} fallback={IMG_PLACEHOLDER} alt="" className={MEDIA_CARD_COVER} />
|
||||
<div className="absolute inset-0 bg-black/40" />
|
||||
<img src="/logo.svg" alt="海角社区" className="absolute top-[21px] left-[21px] h-[19px] w-[84px]" />
|
||||
</div>
|
||||
<div className="flex min-h-[96px] flex-col justify-center gap-1 px-2.5 py-3">
|
||||
<h3 className={MEDIA_CARD_TITLE_PC}>{item.title}</h3>
|
||||
<p className={`${MEDIA_CARD_DESC} text-white/60`}>{item.subtitle}</p>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
const detailHref = item.href || dramaHref(item.id, item.slug);
|
||||
|
||||
return (
|
||||
<article data-component="short-drama-card" className={`w-[266px] ${MEDIA_CARD_ROOT}`}>
|
||||
<Link href={detailHref} className="block">
|
||||
<div className="relative h-[355px] overflow-hidden rounded-t-lg">
|
||||
<DecryptedImage src={item.cover} fallback={IMG_PLACEHOLDER} alt="" className={MEDIA_CARD_COVER} />
|
||||
{item.badge ? (
|
||||
<div className="absolute top-2 right-2">
|
||||
<Badge tone={item.badge} />
|
||||
</div>
|
||||
) : null}
|
||||
{item.episodes ? (
|
||||
<span
|
||||
className={`absolute right-2 bottom-2 rounded-md bg-black/60 px-2.5 py-1.5 font-medium text-white ${MEDIA_CARD_EPISODES}`}
|
||||
>
|
||||
{item.episodes}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-col gap-1 px-2.5 pt-3">
|
||||
<h3 className={`flex min-h-7 items-center gap-1 ${MEDIA_CARD_TITLE_PC}`}>
|
||||
<RankNumber rank={rank} />
|
||||
<span className="min-w-0 truncate">{item.title}</span>
|
||||
</h3>
|
||||
<p className={`${MEDIA_CARD_DESC} text-white/60`}>{item.subtitle}</p>
|
||||
</div>
|
||||
</Link>
|
||||
<div className={`flex min-w-0 items-center justify-between gap-2 px-2.5 pt-1 pb-3 ${MEDIA_CARD_META}`}>
|
||||
<CardTags tags={item.tags} kind="playlet" className="min-w-0 text-white/60" />
|
||||
<span className="shrink-0 text-[#F92882]">{item.heat}</span>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
export function H5ShortDramaCard({
|
||||
item,
|
||||
rank,
|
||||
showEpisodes = true,
|
||||
}: {
|
||||
item: ShortDramaItem;
|
||||
rank?: number;
|
||||
showEpisodes?: boolean;
|
||||
}) {
|
||||
if (item.kind === "ad") {
|
||||
return (
|
||||
<a
|
||||
href={item.href || "#h5-download"}
|
||||
target={item.href ? "_blank" : undefined}
|
||||
rel={item.href ? "sponsored nofollow noopener noreferrer" : undefined}
|
||||
className={`block ${MEDIA_CARD_ROOT}`}
|
||||
>
|
||||
<div className="relative aspect-[3/4] overflow-hidden rounded-t-lg">
|
||||
<DecryptedImage src={item.cover} fallback={IMG_PLACEHOLDER} alt="" className={MEDIA_CARD_COVER} />
|
||||
<div className="absolute inset-0 bg-black/40" />
|
||||
<img src="/logo.svg" alt="海角社区" className="absolute top-[17px] left-[13px] h-4 w-[71px]" />
|
||||
</div>
|
||||
<div className={`${MEDIA_CARD_INFO_H5} pb-1.5`}>
|
||||
<h3 className={MEDIA_CARD_TITLE_H5}>{item.title}</h3>
|
||||
<p className={`${MEDIA_CARD_DESC_SM} text-[#8C859C]`}>{item.subtitle}</p>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
const detailHref = item.href || dramaHref(item.id, item.slug);
|
||||
|
||||
return (
|
||||
<article data-component="h5-short-drama-card" className={MEDIA_CARD_ROOT}>
|
||||
<Link href={detailHref} className="block">
|
||||
<div className="relative aspect-[3/4] overflow-hidden rounded-t-lg">
|
||||
<DecryptedImage src={item.cover} fallback={IMG_PLACEHOLDER} alt="" className={MEDIA_CARD_COVER} />
|
||||
{item.badge ? (
|
||||
<div className="absolute top-2 right-2">
|
||||
<Badge tone={item.badge} className="h-6" />
|
||||
</div>
|
||||
) : null}
|
||||
<CoverRankBadge rank={rank} />
|
||||
{showEpisodes ? (
|
||||
<span
|
||||
className={`absolute right-2 bottom-2 rounded-lg bg-[#08060D]/88 px-1.5 py-0.5 text-white ${MEDIA_CARD_EPISODES_H5}`}
|
||||
>
|
||||
{item.episodes}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className={MEDIA_CARD_INFO_H5}>
|
||||
<h3 className={MEDIA_CARD_TITLE_H5}>{item.title}</h3>
|
||||
<p className={`${MEDIA_CARD_DESC_SM} text-[#8C859C]`}>{item.subtitle}</p>
|
||||
</div>
|
||||
</Link>
|
||||
<div className={MEDIA_CARD_FOOTER_H5}>
|
||||
<CardTags tags={item.tags} kind="playlet" className="min-w-0 text-[#A89EB5]" />
|
||||
<span className="shrink-0 text-[#F12B9E]">{item.heat}</span>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
import { AdBannersSlot, AdIconsSlot, FriendLinksSlot, H5FriendLinksSlot } from "@/components/common/ads/slots";
|
||||
import { ChannelHeader, H5ChannelHeader } from "@/components/common/channel-header";
|
||||
import { EmptyState } from "@/components/common/empty-state";
|
||||
import { FilterChipLink, FilterOptionLink } from "@/components/common/filter-link";
|
||||
import { Pagination } from "@/components/common/pagination";
|
||||
import { buildPageHrefMap } from "@/components/common/pagination-map";
|
||||
import { FloatingActions } from "@/components/home/floating-actions";
|
||||
import { H5SectionHeading } from "@/components/home/h5-cards";
|
||||
import { DownloadBanner } from "@/components/home/page-sections";
|
||||
import { SeoBreadcrumb } from "@/components/seo/seo-breadcrumb";
|
||||
|
||||
import { sortLabels, sorts, type ShortDramaListResult } from "./data";
|
||||
import { shortDramaListHref } from "./route";
|
||||
import { H5ShortDramaCard, ShortDramaCard } from "./short-drama-card";
|
||||
|
||||
export function ShortDramaPage({ list }: { list: ShortDramaListResult }) {
|
||||
const hrefMap = buildPageHrefMap(list.totalPages, (page) =>
|
||||
shortDramaListHref(list.sort, page, list.themeSlug),
|
||||
);
|
||||
const resetHref = shortDramaListHref("hot", 1);
|
||||
const themes = list.themes.filter((theme) => theme.name);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SeoBreadcrumb items={[{ name: "首页", href: "/" }, { name: "短剧" }]} />
|
||||
<main data-component="short-drama-page" className="flex w-full min-w-0 flex-col gap-[18px] overflow-x-clip px-4 py-3 lg:hidden">
|
||||
<H5ChannelHeader title="短剧" />
|
||||
<AdIconsSlot variant="h5" />
|
||||
|
||||
<div
|
||||
id="h5-list"
|
||||
className="flex scroll-mt-16 flex-col gap-[9px] rounded-2xl border border-[#292138] bg-[#110D19] px-[19px] py-4"
|
||||
>
|
||||
{themes.length ? (
|
||||
<div className="flex items-start gap-3">
|
||||
<span className="shrink-0 text-xs leading-[18px] text-[#948F9E]">题材:</span>
|
||||
<div className="flex min-w-0 flex-1 flex-wrap gap-x-3.5 gap-y-2">
|
||||
<FilterOptionLink
|
||||
label="全部"
|
||||
href={shortDramaListHref(list.sort, 1)}
|
||||
active={!list.themeSlug}
|
||||
/>
|
||||
{themes.map((theme) => (
|
||||
<FilterOptionLink
|
||||
key={theme.id}
|
||||
label={theme.name || ""}
|
||||
href={shortDramaListHref(list.sort, 1, theme.slug)}
|
||||
active={Boolean(theme.slug && theme.slug === list.themeSlug)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex items-start gap-3">
|
||||
<span className="shrink-0 text-xs leading-[18px] text-[#948F9E]">排序:</span>
|
||||
<div className="flex min-w-0 flex-1 flex-wrap gap-x-3.5 gap-y-2">
|
||||
{sorts.map((item) => (
|
||||
<FilterOptionLink
|
||||
key={item}
|
||||
label={sortLabels[item]}
|
||||
href={shortDramaListHref(item, 1, list.themeSlug)}
|
||||
active={item === list.sort}
|
||||
/>
|
||||
))}
|
||||
<FilterOptionLink label="重置筛选" href={resetHref} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section className="flex flex-col gap-[18px]">
|
||||
<H5SectionHeading title="全部短剧" more={false} />
|
||||
{list.items.length ? (
|
||||
<>
|
||||
<div className="grid grid-cols-2 gap-x-[7px] gap-y-3">
|
||||
{list.items.map((item) => (
|
||||
<H5ShortDramaCard key={item.id} item={item} />
|
||||
))}
|
||||
</div>
|
||||
{list.totalPages > 1 ? (
|
||||
<Pagination page={list.page} total={list.totalPages} hrefMap={hrefMap} showJump={false} />
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<EmptyState
|
||||
title="暂无短剧"
|
||||
description="当前筛选条件下没有可展示的短剧,换个题材或排序试试吧"
|
||||
className="rounded-2xl px-4 py-12"
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<AdBannersSlot variant="h5" />
|
||||
<H5FriendLinksSlot />
|
||||
</main>
|
||||
|
||||
<main className="hidden lg:block">
|
||||
<div className="design-frame flex flex-col gap-6 py-[43px]">
|
||||
<ChannelHeader title="短剧" />
|
||||
<AdIconsSlot variant="pc" />
|
||||
|
||||
<div
|
||||
id="pc-list"
|
||||
className="flex scroll-mt-24 flex-col gap-4 rounded-[17px] border border-[#34304B] bg-[#151326] px-5 py-4"
|
||||
>
|
||||
{themes.length ? (
|
||||
<div className="flex items-start gap-3">
|
||||
<span className="shrink-0 pt-1.5 text-xs leading-[18px] text-white">题材:</span>
|
||||
<div className="flex min-w-0 flex-1 flex-wrap gap-2">
|
||||
<FilterChipLink label="全部" href={shortDramaListHref(list.sort, 1)} active={!list.themeSlug} />
|
||||
{themes.map((theme) => (
|
||||
<FilterChipLink
|
||||
key={theme.id}
|
||||
label={theme.name || ""}
|
||||
href={shortDramaListHref(list.sort, 1, theme.slug)}
|
||||
active={Boolean(theme.slug && theme.slug === list.themeSlug)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex items-start gap-3">
|
||||
<span className="shrink-0 pt-1.5 text-xs leading-[18px] text-white">排序:</span>
|
||||
<div className="flex min-w-0 flex-1 flex-wrap gap-2">
|
||||
{sorts.map((item) => (
|
||||
<FilterChipLink
|
||||
key={item}
|
||||
label={sortLabels[item]}
|
||||
href={shortDramaListHref(item, 1, list.themeSlug)}
|
||||
active={item === list.sort}
|
||||
/>
|
||||
))}
|
||||
<FilterChipLink label="重置筛选" href={resetHref} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{list.items.length ? (
|
||||
<div className="grid grid-cols-[repeat(6,266px)] justify-between gap-y-6">
|
||||
{list.items.map((item) => (
|
||||
<ShortDramaCard key={item.id} item={item} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<EmptyState
|
||||
title="暂无短剧"
|
||||
description="当前筛选条件下没有可展示的短剧,换个题材或排序试试吧"
|
||||
className="rounded-3xl px-[110px] py-24"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{/* 分页放在 design-frame 外,避免 Chrome CSS zoom 热区错位导致点击无响应 */}
|
||||
{list.items.length && list.totalPages > 1 ? (
|
||||
<div className="flex justify-center px-6 py-6">
|
||||
<Pagination page={list.page} total={list.totalPages} hrefMap={hrefMap} />
|
||||
</div>
|
||||
) : null}
|
||||
<div className="design-frame flex flex-col gap-6 pb-[43px]">
|
||||
<AdBannersSlot variant="pc" />
|
||||
<DownloadBanner />
|
||||
<FriendLinksSlot />
|
||||
</div>
|
||||
</main>
|
||||
<FloatingActions />
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user