From d2c8c63a3682617844f6fe2531ef0062c4d82914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E5=AE=87=E5=AE=81?= Date: Mon, 26 Oct 2026 20:31:41 +0700 Subject: [PATCH] =?UTF-8?q?=E7=A6=8F=E5=88=A9=E5=BA=94=E7=94=A8=E5=88=A0?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/images/nav_flyy.svg | 3 - public/images/trangle_down.svg | 3 - src/app/welfare/page.tsx | 58 -------- src/components/layout/footer-nav-items.ts | 7 +- src/components/layout/nav-href.ts | 15 +- src/components/layout/site-shell.tsx | 8 +- src/components/nav/nav-page.tsx | 1 - src/components/welfare/data.ts | 64 --------- .../welfare/welfare-category-section.tsx | 132 ------------------ src/components/welfare/welfare-page.tsx | 56 -------- src/components/welfare/welfare-tabs.tsx | 66 --------- src/lib/api/applist.ts | 22 --- src/lib/api/types.ts | 34 ----- 13 files changed, 27 insertions(+), 442 deletions(-) delete mode 100644 public/images/nav_flyy.svg delete mode 100644 public/images/trangle_down.svg delete mode 100644 src/app/welfare/page.tsx delete mode 100644 src/components/welfare/data.ts delete mode 100644 src/components/welfare/welfare-category-section.tsx delete mode 100644 src/components/welfare/welfare-page.tsx delete mode 100644 src/components/welfare/welfare-tabs.tsx delete mode 100644 src/lib/api/applist.ts diff --git a/public/images/nav_flyy.svg b/public/images/nav_flyy.svg deleted file mode 100644 index d93d08c..0000000 --- a/public/images/nav_flyy.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/public/images/trangle_down.svg b/public/images/trangle_down.svg deleted file mode 100644 index ccfaa79..0000000 --- a/public/images/trangle_down.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/app/welfare/page.tsx b/src/app/welfare/page.tsx deleted file mode 100644 index a7bb89c..0000000 --- a/src/app/welfare/page.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import type { Metadata } from "next"; -import { Suspense } from "react"; - -import { PageLoading } from "@/components/common/page-loading"; -import { JsonLd } from "@/components/seo/json-ld"; -import { WelfarePage } from "@/components/welfare/welfare-page"; -import { buildWelfareCategories, type WelfareCategory } from "@/components/welfare/data"; -import { getAppListCached } from "@/lib/api/applist"; -import { buildWebPageJSONLD, getHomeBaseSEOCached, resolvePageMetadata } from "@/lib/seo"; - -export async function generateMetadata(): Promise { - return resolvePageMetadata({ - seoInfo: { - title: "福利应用", - description: "黄果黑料福利应用与精选推荐。", - keyworld: ["黄果黑料", "福利应用"], - }, - canonical: "/welfare", - }); -} - -async function WelfareContent() { - let categories: WelfareCategory[] = []; - try { - const data = await getAppListCached(0); - categories = buildWelfareCategories(data); - } catch (error) { - console.error("[welfare]", error); - } - - const siteSeo = await getHomeBaseSEOCached(); - return ( - <> - - - - ); -} - -export default function Page() { - return ( - }> - - - ); -} diff --git a/src/components/layout/footer-nav-items.ts b/src/components/layout/footer-nav-items.ts index e05602b..e2778fc 100644 --- a/src/components/layout/footer-nav-items.ts +++ b/src/components/layout/footer-nav-items.ts @@ -1,5 +1,7 @@ import type { AppTranslationInfo, TranslationInfo } from "@/lib/api/types"; +import { isRemovedNavItem } from "./nav-href"; + /** 接口失败时兜底:PC「关于平台」(与 home/base → bottonTranslations 对齐) */ export const BOTTOM_NAV_FALLBACK_NAMES = ["用户条款", "隐私政策", "联系我们", "常见问题"] as const; @@ -19,5 +21,8 @@ export function footerNavItems(bottonTranslations?: AppTranslationInfo | null): const source = bottomCats.length ? bottomCats : categories; return source .flatMap((category) => category.translationList ?? []) - .filter((item): item is TranslationInfo => Boolean(item.name?.trim()) && !isCustomerServiceName(item.name)); + .filter( + (item): item is TranslationInfo => + Boolean(item.name?.trim()) && !isCustomerServiceName(item.name) && !isRemovedNavItem(item), + ); } diff --git a/src/components/layout/nav-href.ts b/src/components/layout/nav-href.ts index d0081a8..81ae242 100644 --- a/src/components/layout/nav-href.ts +++ b/src/components/layout/nav-href.ts @@ -57,7 +57,6 @@ export function resolveNavHref(urlValue?: string | null): string { const path = raw.startsWith("/") ? raw : `/${raw}`; if (path === "/" || path === "/#top") return "/"; - if (path === "/application" || path.startsWith("/application/")) return "/welfare"; if (path === "/drama-rank") return "/ranking/drama"; if (path === "/video-rank") return "/ranking/video"; if (path.startsWith("/drama/")) return `/short-drama${path.slice("/drama".length)}`; @@ -66,6 +65,20 @@ export function resolveNavHref(urlValue?: string | null): string { return path; } +/** 已下线的福利应用入口(接口仍可能下发 /application 或「福利应用」):导航中不展示 */ +export function isRemovedNavItem(item: { name?: string | null; urlValue?: string | null }) { + const name = item.name?.trim() || ""; + const url = (item.urlValue || "").trim().replace(/^https?:\/\/[^/]+/i, ""); + const path = url.startsWith("/") ? url : `/${url}`; + return ( + name.includes("福利") || + path === "/application" || + path.startsWith("/application/") || + path === "/welfare" || + path.startsWith("/welfare/") + ); +} + /** 底部导航 / 页脚:优先接口 urlValue,无配置时再按 name 兜底 */ export function resolveAboutPlatformHref(item: { name?: string; urlValue?: string | null }): string { const name = item.name?.trim(); diff --git a/src/components/layout/site-shell.tsx b/src/components/layout/site-shell.tsx index 56c5bf1..f7f247d 100644 --- a/src/components/layout/site-shell.tsx +++ b/src/components/layout/site-shell.tsx @@ -5,6 +5,7 @@ import { getNavigationHeaderCached } from "@/lib/api/navigation"; import type { AppTranslationInfo, ConfigInfo, TranslationCategory } from "@/lib/api/types"; import { DesktopNav } from "./desktop-nav"; +import { isRemovedNavItem } from "./nav-href"; import { HideOnChromeless, HideOnMobileChromeless } from "./route-visibility"; import { SiteFooter } from "./site-footer"; import { SiteHeader } from "./site-header"; @@ -13,7 +14,12 @@ import { SiteHeader } from "./site-header"; async function loadNavigationCategories(): Promise { try { const data = await getNavigationHeaderCached(); - return data.categoryList?.filter((item) => item.name?.trim()) ?? []; + return (data.categoryList ?? []) + .filter((item) => item.name?.trim() && !isRemovedNavItem(item)) + .map((item) => ({ + ...item, + translationList: item.translationList?.filter((child) => !isRemovedNavItem(child)) ?? item.translationList, + })); } catch { return []; } diff --git a/src/components/nav/nav-page.tsx b/src/components/nav/nav-page.tsx index ba4b9a1..9a1bd29 100644 --- a/src/components/nav/nav-page.tsx +++ b/src/components/nav/nav-page.tsx @@ -57,7 +57,6 @@ function buildShortcuts(config?: ConfigInfo | null): Shortcut[] { config?.appAddr?.trim() ? { label: "APP", icon: "/logo2.png", href: hrefOf(config.appAddr), external: true } : { label: "APP", icon: "/logo2.png" }, - { label: "福利应用", icon: "/images/nav_flyy.svg", href: "/welfare" }, ]; } diff --git a/src/components/welfare/data.ts b/src/components/welfare/data.ts deleted file mode 100644 index c202b90..0000000 --- a/src/components/welfare/data.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type { AppCategoryInfo, AppListResp } from "@/lib/api/types"; - -export type WelfareApp = { - id: string; - title: string; - subtitle: string; - icon: string; - href: string; -}; - -export type WelfareCategory = { - id: string; - title: string; - description: string; - apps: WelfareApp[]; -}; - -/** PC 默认展示 12,移动端 8 */ -export const WELFARE_DESKTOP_COLLAPSE_LIMIT = 12; -export const WELFARE_MOBILE_COLLAPSE_LIMIT = 8; - -/** - * 按接口返回的 categoryList / appList 顺序组装,不做二次排序。 - * 规则对齐 jha-1043 applistser.BuildPageVM。 - */ -export function buildWelfareCategories(data: AppListResp | null | undefined): WelfareCategory[] { - if (!data) return []; - - let list = data.categoryList ?? []; - if (!list.length && data.category?.id) { - list = [data.category]; - } - if (!list.length) return []; - - return list.map((cat) => mapCategory(cat)); -} - -function mapCategory(cat: AppCategoryInfo): WelfareCategory { - const apps: WelfareApp[] = []; - for (const app of cat.appList ?? []) { - const name = (app.name || "").trim(); - if (!name) continue; - - const href = (app.url || "").trim() || "#"; - const intro = (app.intro || "").trim() || (app.description || "").trim(); - - apps.push({ - id: String(app.id), - title: name, - subtitle: intro, - icon: (app.logo || "").trim(), - href, - }); - } - - const title = (cat.title || "").trim() || "应用分类"; - - return { - id: `app-category-${cat.id}`, - title, - description: (cat.intro || "").trim(), - apps, - }; -} diff --git a/src/components/welfare/welfare-category-section.tsx b/src/components/welfare/welfare-category-section.tsx deleted file mode 100644 index ec8eb19..0000000 --- a/src/components/welfare/welfare-category-section.tsx +++ /dev/null @@ -1,132 +0,0 @@ -"use client"; - -import { useState } from "react"; - -import { DecryptedImage } from "@/components/common/decrypted-image"; -import { IMG_PLACEHOLDER } from "@/lib/image/decrypt"; - -import { - WELFARE_DESKTOP_COLLAPSE_LIMIT, - WELFARE_MOBILE_COLLAPSE_LIMIT, - type WelfareCategory, -} from "./data"; - -type Variant = "pc" | "h5"; - -const variants = { - pc: { - rowSize: WELFARE_DESKTOP_COLLAPSE_LIMIT, - section: "scroll-mt-24 gap-6", - heading: "gap-3", - title: "text-4xl leading-[50px] font-normal text-white/90", - description: "text-lg leading-6 text-white/55", - descriptionFirst: "font-semibold text-white/70", - grid: "grid-cols-6 gap-x-4 gap-y-6 xl:grid-cols-12", - icon: "aspect-square w-full max-w-[120px] rounded-[18px]", - appTitle: "text-base leading-[22px] font-medium text-white/90", - appSubtitle: "text-xs leading-[18px] text-white/55", - tile: "gap-2", - button: "h-[50px] rounded-full px-6 text-lg leading-6", - showExpandIcon: true, - }, - h5: { - rowSize: WELFARE_MOBILE_COLLAPSE_LIMIT, - section: "scroll-mt-16 gap-3", - heading: "w-full gap-3", - title: "text-lg leading-6 font-semibold text-white/90", - description: "text-sm leading-5 font-normal text-white/55", - descriptionFirst: "", - grid: "grid-cols-[repeat(4,77px)] justify-between gap-y-3", - icon: "size-[77px] overflow-hidden rounded-[18px]", - appTitle: "text-sm leading-5 font-medium text-white", - appSubtitle: "text-xs leading-[18px] font-normal text-white", - tile: "w-[77px] gap-1 rounded-xl", - button: "rounded-[25px] px-[18px] py-2.5 text-xs leading-[18px]", - showExpandIcon: false, - }, -} as const; - -export function WelfareCategorySection({ - category, - variant, - isFirst, -}: { - category: WelfareCategory; - variant: Variant; - isFirst: boolean; -}) { - const styles = variants[variant]; - const [expanded, setExpanded] = useState(false); - const limit = styles.rowSize; - const total = category.apps.length; - const expandable = total > limit; - const apps = expandable && !expanded ? category.apps.slice(0, limit) : category.apps; - - return ( -
-
-

{category.title}

- {category.description ? ( -

- {category.description} -

- ) : null} -
- - {apps.length ? ( - - ) : ( -

该分类暂无应用

- )} - - {expandable ? ( - - ) : null} -
- ); -} diff --git a/src/components/welfare/welfare-page.tsx b/src/components/welfare/welfare-page.tsx deleted file mode 100644 index 325f6f2..0000000 --- a/src/components/welfare/welfare-page.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { AdBannersSlot, AdIconsSlot, FriendLinksSlot, H5FriendLinksSlot } from "@/components/common/ads/slots"; -import { FloatingActions } from "@/components/home/floating-actions"; -import { DownloadBanner } from "@/components/home/page-sections"; -import { SeoBreadcrumb } from "@/components/seo/seo-breadcrumb"; - -import type { WelfareCategory } from "./data"; -import { WelfareCategorySection } from "./welfare-category-section"; -import { WelfareCategoryTabs } from "./welfare-tabs"; - -export function WelfarePage({ categories }: { categories: WelfareCategory[] }) { - if (!categories.length) { - return ( -
- -

福利应用

-

暂无福利应用

-
- ); - } - - return ( - <> - -

福利应用

-
- - {categories.map((category, index) => ( - - ))} - - - -
- -
-
-
- - {categories.map((category, index) => ( - - ))} -
- - - - - -
-
- - - ); -} diff --git a/src/components/welfare/welfare-tabs.tsx b/src/components/welfare/welfare-tabs.tsx deleted file mode 100644 index 711502a..0000000 --- a/src/components/welfare/welfare-tabs.tsx +++ /dev/null @@ -1,66 +0,0 @@ -"use client"; - -import { useState } from "react"; - -import type { WelfareCategory } from "./data"; - -type Variant = "pc" | "h5"; - -const tabStyles = { - pc: { - tabs: "flex w-full flex-wrap items-center justify-center gap-[18px]", - tab: "min-h-[50px] max-w-full px-5 py-2 text-lg leading-6 whitespace-normal", - tabActive: "bg-[#F12B9E] text-white", - tabIdle: "bg-black/5 text-[#999999] outline outline-black/10 -outline-offset-1 hover:text-white", - }, - h5: { - tabs: "flex w-full gap-1.5 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", - tab: "shrink-0 rounded-[25px] px-3 py-1.5 text-xs leading-[18px]", - tabActive: "bg-[#F12B9E] text-white", - tabIdle: - "bg-white/5 text-white/55 outline outline-1 -outline-offset-1 outline-white/10", - }, -} as const; - -export function WelfareCategoryTabs({ - variant, - categories, -}: { - variant: Variant; - categories: WelfareCategory[]; -}) { - const [activeId, setActiveId] = useState(categories[0]?.id ?? ""); - const styles = tabStyles[variant]; - const validActive = - categories.some((item) => item.id === activeId) ? activeId : (categories[0]?.id ?? ""); - if (validActive !== activeId) { - setActiveId(validActive); - } - - const jumpTo = (id: string) => { - setActiveId(id); - const prefix = variant === "h5" ? "h5-" : ""; - document.getElementById(`${prefix}${id}`)?.scrollIntoView({ behavior: "smooth" }); - }; - - return ( -
- {categories.map((category) => { - const active = category.id === validActive; - return ( - - ); - })} -
- ); -} diff --git a/src/lib/api/applist.ts b/src/lib/api/applist.ts deleted file mode 100644 index b0455de..0000000 --- a/src/lib/api/applist.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { cacheLife, cacheTag } from "next/cache"; - -import { apiRequest } from "./client"; -import { serverFetchJson } from "./server-fetch"; -import type { AppListResp } from "./types"; - -/** categoryId=0 表示默认(返回全部分类列表);浏览器端可用 */ -export function fetchAppList(categoryId = 0, token?: string | null) { - return apiRequest(`/api/app/applist/list?categoryId=${categoryId}`, { - method: "GET", - token, - }); -} - -/** 服务端渲染拉取应用列表(带缓存) */ -export async function getAppListCached(categoryId = 0): Promise { - "use cache"; - cacheLife("hours"); - cacheTag("applist"); - - return serverFetchJson(`/api/app/applist/list?categoryId=${categoryId}`); -} diff --git a/src/lib/api/types.ts b/src/lib/api/types.ts index 4b023d5..c3abeb5 100644 --- a/src/lib/api/types.ts +++ b/src/lib/api/types.ts @@ -48,40 +48,6 @@ export type UpdateProfileReq = { newPassword?: string; }; -export type ImageUploadData = { - id?: string; - sourceNameFs?: string; - url?: string; -}; - -/** GET /api/app/applist/list */ -export type AppInfoData = { - id: number; - categoryId?: number; - name: string; - intro?: string; - description?: string; - logo?: string; - url?: string; - sort?: number; - advitiseId?: string; - location?: number; -}; - -export type AppCategoryInfo = { - id: number; - code?: number; - title: string; - intro?: string; - sort?: number; - appList?: AppInfoData[]; -}; - -export type AppListResp = { - category?: AppCategoryInfo; - categoryList?: AppCategoryInfo[]; -}; - /** GET /api/app/playlet/content/list */ export type PlayletData = { id: number;