[Ai] feat: 花魁短剧 Next.js 站点初始提交
- 原生 Next.js App Router + 严格 TypeScript 实现花魁短剧 PC/H5 页面,按 src/features/{catalog,content,playback} 组织,保留完整 SEO(metadata、JSON-LD、canonical、robots、llms、sitemap、IndexNow)。
- 主域名通过 SITE_URL=https://huakuidj.com 强制覆盖后端 foreverUrlAddr(config/*.env、src/server/config.ts、src/server/seo/shared.ts、src/server/services/shared.ts),支持邮箱改为 support@huakuidj.com。
- 移除登录与账户体系:删除 /account 全部页面、登录/注册/充值弹窗、收藏点赞、评论、金币解锁及对应 BFF 接口;旧账户地址 GET/HEAD 统一 301 到首页(src/lib/routes.ts REMOVED_ACCOUNT_PATHS),保留游客会话、反馈、图片上传、观看上报与搜索联想。
- 播放页:评论位改为同类推荐 + 热播榜单,锁定集改为 App 观看遮罩与底部弹层,新增简介标签、分集图例与独立视觉皮肤(src/features/playback、src/styles/pages/playback.css)。
- 卡片:短剧/视频卡状态与时长移至信息行胶囊标签,封面角标移至左上角(src/components/catalog/Cards.tsx、src/styles/foundations/base.css)。
- 同步更新单元测试、浏览器冒烟脚本、路由清单与 README。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { basename, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
// Legacy fixture keys stay inside test infrastructure. Production imports semantic CSS paths.
|
||||
const sourceStyles: Record<string, string> = {
|
||||
"about-mobile": "src/styles/pages/about-responsive.css",
|
||||
"about": "src/styles/pages/about.css",
|
||||
"ads": "src/styles/components/advertising-compat.css",
|
||||
"base": "src/styles/foundations/base.css",
|
||||
"dialogAd": "src/styles/components/advertisement-dialog.css",
|
||||
"downloadPop": "src/styles/components/app-download-dialog.css",
|
||||
"eighteen": "src/styles/components/age-gate.css",
|
||||
"feedback": "src/styles/pages/feedback.css",
|
||||
"footer": "src/styles/components/site-footer.css",
|
||||
"header": "src/styles/components/site-header.css",
|
||||
"help": "src/styles/pages/help.css",
|
||||
"index": "src/styles/pages/home.css",
|
||||
"installGuide": "src/styles/components/installation-guide.css",
|
||||
"legal": "src/styles/pages/legal.css",
|
||||
"list": "src/styles/pages/drama-list.css",
|
||||
"mobile-search": "src/styles/pages/mobile-search.css",
|
||||
"mobileTabbar": "src/styles/components/mobile-tabbar.css",
|
||||
"moreList": "src/styles/pages/expanded-list.css",
|
||||
"nav": "src/styles/pages/site-navigation.css",
|
||||
"notFound": "src/styles/pages/not-found.css",
|
||||
"pagination": "src/styles/components/pagination.css",
|
||||
"play": "src/styles/pages/playback.css",
|
||||
"playLinks": "src/styles/components/playback-links.css",
|
||||
"popularRecommend": "src/styles/pages/popular-recommendations.css",
|
||||
"promo-grid": "src/styles/components/promotional-grid.css",
|
||||
"promo": "src/styles/components/promotional-banner.css",
|
||||
"pwa": "src/styles/overrides/pwa-safe-area.css",
|
||||
"rank": "src/styles/pages/rankings.css",
|
||||
"recentlyAdded": "src/styles/pages/recently-added.css",
|
||||
"search": "src/styles/pages/search-results.css",
|
||||
"series": "src/styles/pages/series-list.css",
|
||||
"tags": "src/styles/pages/tag-detail.css",
|
||||
"toast": "src/styles/components/toast.css",
|
||||
"topic": "src/styles/pages/topics.css",
|
||||
"topicDetail": "src/styles/pages/topic-detail.css",
|
||||
"player": "src/styles/components/video-player.css"
|
||||
};
|
||||
|
||||
export async function createStyleFixture(keys: readonly string[]) {
|
||||
const root = fileURLToPath(new URL("..", import.meta.url));
|
||||
const layout = await readFile(join(root, "src/app/layout.tsx"), "utf8");
|
||||
const order = [...layout.matchAll(/^import ["']@\/([^"']+\.css)["'];/gm)].map((match) => "src/" + match[1]);
|
||||
const files = [...new Set(keys.map((key) => {
|
||||
const file = sourceStyles[key];
|
||||
if (!file) throw new Error("Unknown fixture stylesheet: " + key);
|
||||
return file;
|
||||
}))].sort((first, second) => order.indexOf(first) - order.indexOf(second));
|
||||
for (const file of files) {
|
||||
if (!order.includes(file)) throw new Error("Fixture stylesheet is not imported by the root layout: " + file);
|
||||
}
|
||||
const css = (await Promise.all(files.map((file) => readFile(join(root, file), "utf8")))).join("\n");
|
||||
return {
|
||||
css,
|
||||
href: "/fixture-" + createHash("sha256").update(css).digest("hex").slice(0, 12) + ".css",
|
||||
tokens: files.map((file) => basename(file, ".css")).join(" "),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user