完善官网 SEO 地基与精确路由
补充 robots、sitemap、SEO 元信息、结构化数据与首页语义内容 统一三套 Nginx 与 Pingora 的 62 条 SPA 路由和真实 404 行为 新增路由一致性检查、网关测试并同步技术文档与项目记忆
This commit is contained in:
+6
-2
@@ -161,7 +161,9 @@ describe('App title sync', () => {
|
||||
test('主站阶段变化会同步浏览器与宿主标题', () => {
|
||||
renderApp();
|
||||
|
||||
expect(appTitleMock.syncAppTitle).toHaveBeenLastCalledWith('陶泥儿');
|
||||
expect(appTitleMock.syncAppTitle).toHaveBeenLastCalledWith(
|
||||
'陶泥儿 Genarrative|游戏美术AI创作工具与美术Agent工作台',
|
||||
);
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(screen.getByRole('button', { name: '打开拼图创作' }));
|
||||
@@ -187,7 +189,9 @@ describe('App title sync', () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: '退出 RPG' }));
|
||||
});
|
||||
|
||||
expect(appTitleMock.syncAppTitle).toHaveBeenLastCalledWith('陶泥儿');
|
||||
expect(appTitleMock.syncAppTitle).toHaveBeenLastCalledWith(
|
||||
'陶泥儿 Genarrative|游戏美术AI创作工具与美术Agent工作台',
|
||||
);
|
||||
});
|
||||
|
||||
test('启动时回读宿主 runtime 后刷新壳能力 UI', async () => {
|
||||
|
||||
@@ -158,11 +158,11 @@ describe('CreationLandingView', () => {
|
||||
expect(screen.getByRole('main', { name: '陶泥儿创作主页' })).toBeTruthy();
|
||||
expect(
|
||||
screen.getByRole('heading', {
|
||||
name: '陶泥儿 - 开启全民精品游戏创作',
|
||||
name: '游戏美术AI工作台,让个人创作者更快做出角色、场景与宣发素材',
|
||||
}),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
screen.getByText('登录即送100泥点,可以免费制作50个素材'),
|
||||
screen.getByText(/陶泥儿 Genarrative 以美术 Agent 与无限画布/u),
|
||||
).toBeTruthy();
|
||||
expect(screen.getByRole('heading', { name: '创作工具' })).toBeTruthy();
|
||||
expect(screen.getByText('游戏视觉规范')).toBeTruthy();
|
||||
|
||||
@@ -859,8 +859,13 @@ export function CreationLandingView({
|
||||
<section className="creation-landing__hero">
|
||||
<div className="creation-landing__hero-copy">
|
||||
<span className="creation-landing__eyebrow">陶泥儿创作工具</span>
|
||||
<h1>陶泥儿 - 开启全民精品游戏创作</h1>
|
||||
<p>登录即送100泥点,可以免费制作50个素材</p>
|
||||
<h1>
|
||||
游戏美术AI工作台,让个人创作者更快做出角色、场景与宣发素材
|
||||
</h1>
|
||||
<p>
|
||||
陶泥儿 Genarrative 以美术 Agent
|
||||
与无限画布帮助个人创作者准备角色、场景与宣发素材;登录即送100泥点,可以免费制作50个素材。
|
||||
</p>
|
||||
<div className="creation-landing__hero-actions">
|
||||
<PlatformActionButton
|
||||
size="md"
|
||||
|
||||
@@ -95,7 +95,10 @@ test('renders mobile home welcome copy and closes with the acknowledge button',
|
||||
);
|
||||
|
||||
expect(screen.getByRole('dialog', { name: '欢迎' })).toBeTruthy();
|
||||
expect(screen.getByRole('heading', { name: '欢迎' })).toBeTruthy();
|
||||
expect(
|
||||
screen.getByRole('heading', { level: 2, name: '欢迎' }),
|
||||
).toBeTruthy();
|
||||
expect(screen.queryByRole('heading', { level: 1 })).toBeNull();
|
||||
const copy = document.querySelector(
|
||||
'.platform-mobile-home-welcome-dialog__copy',
|
||||
);
|
||||
|
||||
@@ -68,7 +68,7 @@ export function PlatformMobileHomeWelcomeDialog({
|
||||
alt=""
|
||||
className="platform-mobile-home-welcome-dialog__icon"
|
||||
/>
|
||||
<h1 className="platform-mobile-home-welcome-dialog__title">欢迎</h1>
|
||||
<h2 className="platform-mobile-home-welcome-dialog__title">欢迎</h2>
|
||||
<p className="platform-mobile-home-welcome-dialog__copy">
|
||||
尼嚎,新陶泥er!
|
||||
<br />
|
||||
|
||||
@@ -378,6 +378,9 @@ const {
|
||||
};
|
||||
});
|
||||
|
||||
const HOME_SEO_HEADING =
|
||||
'游戏美术AI工作台,让个人创作者更快做出角色、场景与宣发素材';
|
||||
|
||||
const {
|
||||
mockGetPublicAuthUserByCode,
|
||||
mockGetPublicAuthUserById,
|
||||
@@ -4280,6 +4283,39 @@ test('desktop home shows empty states when public shelves are unavailable', () =
|
||||
expect(screen.getByText('暂时还没有推荐作品。')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('desktop home exposes one product h1 and section h2 headings', () => {
|
||||
renderLoggedOutHomeView(vi.fn(), {}, 'home', true);
|
||||
|
||||
const productHeading = screen.getByRole('heading', {
|
||||
level: 1,
|
||||
name: HOME_SEO_HEADING,
|
||||
});
|
||||
|
||||
expect(screen.getAllByRole('heading', { level: 1 })).toHaveLength(1);
|
||||
expect(productHeading.closest('button')).toBeNull();
|
||||
expect(
|
||||
screen.getByRole('heading', { level: 2, name: '今日游戏' }),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
screen.getByRole('heading', { level: 2, name: '推荐' }),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
screen.getByRole('heading', { level: 2, name: '作品分类' }),
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test('mobile shell exposes one product h1 outside interactive content', () => {
|
||||
renderLoggedOutHomeView(vi.fn(), {}, 'profile');
|
||||
|
||||
const productHeading = screen.getByRole('heading', {
|
||||
level: 1,
|
||||
name: HOME_SEO_HEADING,
|
||||
});
|
||||
|
||||
expect(screen.getAllByRole('heading', { level: 1 })).toHaveLength(1);
|
||||
expect(productHeading.closest('button')).toBeNull();
|
||||
});
|
||||
|
||||
test('logged in create tab shows real wallet balance beside the brand', () => {
|
||||
mockNarrowMobileLayout();
|
||||
|
||||
|
||||
@@ -1020,13 +1020,27 @@ function SectionHeader({ title, detail }: { title: string; detail: string }) {
|
||||
<div className="text-[10px] font-semibold tracking-[0.26em] text-[var(--platform-text-soft)]">
|
||||
{detail}
|
||||
</div>
|
||||
<div className="mt-1 text-base font-semibold text-[var(--platform-text-strong)]">
|
||||
<h2 className="mt-1 text-base font-semibold text-[var(--platform-text-strong)]">
|
||||
{title}
|
||||
</div>
|
||||
</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function HomeSeoIntro() {
|
||||
return (
|
||||
<header className="mb-2 min-w-0 shrink-0 px-1">
|
||||
<h1 className="text-sm font-semibold leading-5 text-[var(--platform-text-strong)]">
|
||||
游戏美术AI工作台,让个人创作者更快做出角色、场景与宣发素材
|
||||
</h1>
|
||||
<p className="mt-0.5 text-xs leading-5 text-[var(--platform-text-soft)]">
|
||||
在陶泥儿 Genarrative,用美术 Agent
|
||||
快速生成与迭代素材,再在无限画布中整理角色、场景和宣发灵感。
|
||||
</p>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
function PublicCodeSearchBar({
|
||||
value,
|
||||
onChange,
|
||||
@@ -4578,6 +4592,8 @@ export function RpgEntryHomeView({
|
||||
|
||||
const desktopHomeContent: ReactNode = (
|
||||
<div className={DESKTOP_PAGE_STAGE_CLASS}>
|
||||
<HomeSeoIntro />
|
||||
|
||||
{platformError ? (
|
||||
<PlatformStatusMessage
|
||||
tone="error"
|
||||
@@ -5160,6 +5176,8 @@ export function RpgEntryHomeView({
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<HomeSeoIntro />
|
||||
|
||||
<div className="platform-tab-panel-stack min-w-0 flex-1">
|
||||
{tabPanels}
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
APP_HOME_TITLE,
|
||||
resolveAppTitleForSelectionStage,
|
||||
syncAppTitle,
|
||||
} from './appTitle';
|
||||
@@ -22,7 +23,10 @@ afterEach(() => {
|
||||
|
||||
describe('appTitle', () => {
|
||||
test('按平台阶段生成可读页面标题', () => {
|
||||
expect(resolveAppTitleForSelectionStage('platform')).toBe('陶泥儿');
|
||||
expect(resolveAppTitleForSelectionStage('platform')).toBe(APP_HOME_TITLE);
|
||||
expect(resolveAppTitleForSelectionStage('creation-home')).toBe(
|
||||
APP_HOME_TITLE,
|
||||
);
|
||||
expect(resolveAppTitleForSelectionStage('work-detail')).toBe(
|
||||
'作品详情 - 陶泥儿',
|
||||
);
|
||||
|
||||
@@ -2,6 +2,8 @@ import type { SelectionStage } from '../components/platform-entry';
|
||||
import { setHostAppTitle } from './host-bridge/hostBridge';
|
||||
|
||||
const APP_TITLE_BRAND = '陶泥儿';
|
||||
export const APP_HOME_TITLE =
|
||||
'陶泥儿 Genarrative|游戏美术AI创作工具与美术Agent工作台';
|
||||
|
||||
const APP_TITLE_BY_SELECTION_STAGE: Partial<Record<SelectionStage, string>> = {
|
||||
'agent-workspace': 'RPG 创作',
|
||||
@@ -21,6 +23,7 @@ const APP_TITLE_BY_SELECTION_STAGE: Partial<Record<SelectionStage, string>> = {
|
||||
'creative-agent-workspace': '创意 Agent',
|
||||
'custom-world-generating': 'RPG 生成中',
|
||||
'custom-world-result': 'RPG 结果',
|
||||
'creation-home': APP_HOME_TITLE,
|
||||
detail: '世界详情',
|
||||
'jump-hop-gallery-detail': '跳一跳详情',
|
||||
'jump-hop-generating': '跳一跳生成中',
|
||||
@@ -31,7 +34,7 @@ const APP_TITLE_BY_SELECTION_STAGE: Partial<Record<SelectionStage, string>> = {
|
||||
'match3d-generating': '抓大鹅生成中',
|
||||
'match3d-result': '抓大鹅结果',
|
||||
'match3d-runtime': '抓大鹅',
|
||||
platform: APP_TITLE_BRAND,
|
||||
platform: APP_HOME_TITLE,
|
||||
'profile-feedback': '反馈与投诉',
|
||||
'puzzle-agent-workspace': '拼图创作',
|
||||
'puzzle-clear-generating': '拼消消生成中',
|
||||
@@ -61,7 +64,7 @@ const APP_TITLE_BY_SELECTION_STAGE: Partial<Record<SelectionStage, string>> = {
|
||||
|
||||
export function resolveAppTitleForSelectionStage(stage: SelectionStage) {
|
||||
const title = APP_TITLE_BY_SELECTION_STAGE[stage] ?? APP_TITLE_BRAND;
|
||||
if (title === APP_TITLE_BRAND) {
|
||||
if (title === APP_TITLE_BRAND || title === APP_HOME_TITLE) {
|
||||
return title;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user