From 055a6d665866b76ea72cd558285d454c7396a9a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 30 Sep 2026 11:44:05 +0800 Subject: [PATCH 01/12] =?UTF-8?q?=E9=A6=96=E5=B1=8F=E3=80=8C=E7=AB=8B?= =?UTF-8?q?=E5=8D=B3=E8=AF=95=E7=8E=A9=E3=80=8D=E6=94=B9=E4=B8=BA=E6=BB=9A?= =?UTF-8?q?=E5=8A=A8=E5=88=B0=E5=88=97=E8=A1=A8=E8=80=8C=E4=B8=8D=E6=98=AF?= =?UTF-8?q?=E6=89=93=E5=BC=80=E7=AC=AC=E4=B8=80=E6=AC=BE=E6=B8=B8=E6=88=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GameGalleryPage 的 hero 按钮不再取 visibleGames[0] 直接开详情,改为把游戏列表滚进视野 - 滚动按平台页签面板(.platform-tab-panel)算相对偏移并走 scrollTo({behavior:'smooth'}),缺少 scrollTo 时退化成直接赋值 - 新增 gameListRef 锚在列表工具栏,退役只服务该按钮的 featuredGame 派生 - GameDistributionPages.test.tsx 新增「首屏「立即试玩」滚动到列表,而不是打开第一款游戏详情」,断言未触发 onOpenDetail 且滚动落在页签面板上 - 里程碑文档新增「网页侧交互修正(2026-09-30)」记录该修正与验证命令 --- ...‹碑】游戏分发目录详情与在线游玩-2026-09-18.md | 5 +++ .../GameDistributionPages.test.tsx | 44 +++++++++++++++++++ .../game-distribution/GameGalleryPage.tsx | 31 +++++++++++-- 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md b/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md index d9b9fa6a4..ca79346e7 100644 --- a/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md +++ b/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md @@ -320,3 +320,8 @@ | 同上路径但带 `Cookie` | **403 Forbidden** + `application/json`(`x-api-version: 2026-06-16`、`x-response-time-ms`)——发行读取**拒绝携带账号凭证**的请求,与本地用例断言一致 | **生产环境**(`genarrative.world`)同一时刻 `/api/game-distribution/games` 返回 **0 条**:说明当前生产还没有任何已发布的游戏,条目 ④「真实环境整链路」需要在生产发布一个作品后再跑。也就是说此刻能拿到的真实环境证据是「dev 部署的公开读路径 + 发行隔离」,写路径与审核链路的真实环境验收仍需生产账号。 + +## 网页侧交互修正(2026-09-30) + +- **广场首屏「立即试玩」不再替用户选中作品**:`src/components/game-distribution/GameGalleryPage.tsx` 原来是 `onClick={() => featuredGame && openDetail(featuredGame.id)}`,点一下等于直接打开第一款游戏的详情。现在按钮只把列表滚进视野(按平台页签面板计算相对偏移,`scrollTo({ behavior: 'smooth' })`,无 `scrollTo` 时退化成直接赋值),不再触发 `onOpenDetail`;`featuredGame` 派生随之退役。回归用例 `GameDistributionPages.test.tsx`「首屏「立即试玩」滚动到列表,而不是打开第一款游戏详情」断言 `onOpenDetail` 未被调用且滚动落在页签面板上。 +- 验证:`npx vitest run src/components/game-distribution/GameDistributionPages.test.tsx`(21 passed)、`npm run typecheck`、`npx eslint`、`npm run check:encoding`、`git diff --check` 通过。视觉与真机走查仍需人工/截图评审(沿用阶段 C 第 8 条未取证口径)。 diff --git a/src/components/game-distribution/GameDistributionPages.test.tsx b/src/components/game-distribution/GameDistributionPages.test.tsx index 15b3b8720..f550408c7 100644 --- a/src/components/game-distribution/GameDistributionPages.test.tsx +++ b/src/components/game-distribution/GameDistributionPages.test.tsx @@ -375,6 +375,50 @@ describe('GameGalleryPage 筛选与返回上下文', () => { ).toBe('180'), ); }); + + it('首屏「立即试玩」滚动到列表,而不是打开第一款游戏详情', async () => { + resetGalleryContext(); + const onOpenDetail = vi.fn(); + listGamesMock.mockResolvedValue([ + createGame({ id: 'game-1', title: '星轨防线' }), + createGame({ id: 'game-2', title: '第二款' }), + ]); + const scrollTo = vi.fn(); + const originalScrollTo = Object.getOwnPropertyDescriptor( + Element.prototype, + 'scrollTo', + ); + Object.defineProperty(Element.prototype, 'scrollTo', { + configurable: true, + writable: true, + value: scrollTo, + }); + try { + const { container } = render( +
+ +
, + ); + expect(await screen.findByText('星轨防线')).toBeTruthy(); + + fireEvent.click(screen.getByRole('button', { name: /立即试玩/u })); + + expect(onOpenDetail).not.toHaveBeenCalled(); + expect(scrollTo).toHaveBeenCalledTimes(1); + expect(scrollTo.mock.instances[0]).toBe( + container.querySelector('.platform-tab-panel'), + ); + expect(scrollTo.mock.calls[0]?.[0]).toMatchObject({ + behavior: 'smooth', + }); + } finally { + if (originalScrollTo) { + Object.defineProperty(Element.prototype, 'scrollTo', originalScrollTo); + } else { + delete (Element.prototype as { scrollTo?: unknown }).scrollTo; + } + } + }); }); describe('GamePlayPage 启动超时与旋转提示', () => { diff --git a/src/components/game-distribution/GameGalleryPage.tsx b/src/components/game-distribution/GameGalleryPage.tsx index 11636ccfc..36d888e05 100644 --- a/src/components/game-distribution/GameGalleryPage.tsx +++ b/src/components/game-distribution/GameGalleryPage.tsx @@ -145,6 +145,7 @@ export function GameGalleryPage({ const [error, setError] = useState(''); const requestSerialRef = useRef(0); const scrollContainerRef = useRef(null); + const gameListRef = useRef(null); const hasRestoredScrollRef = useRef(false); /** * 顶栏搜索词只在「真的变了」时覆盖目录筛选。 @@ -167,6 +168,31 @@ export function GameGalleryPage({ container instanceof HTMLElement ? container : null; }, []); + /** + * 首屏「立即试玩」只把列表滚进视野,不替用户选定某一款游戏。 + * + * 滚动容器是平台页签面板而不是窗口,所以按容器相对位置计算,缺少 + * `scrollTo` 时退化成直接赋值,保证无动画环境也能生效。 + */ + const scrollToGameList = useCallback(() => { + const anchor = gameListRef.current; + if (!anchor) return; + const container = scrollContainerRef.current; + if (!container) { + anchor.scrollIntoView?.({ behavior: 'smooth', block: 'start' }); + return; + } + const nextScrollTop = + container.scrollTop + + anchor.getBoundingClientRect().top - + container.getBoundingClientRect().top; + if (typeof container.scrollTo === 'function') { + container.scrollTo({ top: nextScrollTop, behavior: 'smooth' }); + return; + } + container.scrollTop = nextScrollTop; + }, []); + const loadGames = useCallback(() => { const requestSerial = ++requestSerialRef.current; setIsLoading(true); @@ -231,7 +257,6 @@ export function GameGalleryPage({ () => games.filter((game) => matchesGameDeviceFilter(game, device)), [device, games], ); - const featuredGame = useMemo(() => visibleGames[0] ?? null, [visibleGames]); const hasActiveFilters = activeKeyword.trim().length > 0 || category !== '全部' || device !== '全部'; const resetFilters = () => { @@ -270,7 +295,7 @@ export function GameGalleryPage({ featuredGame && openDetail(featuredGame.id)} + onClick={scrollToGameList} > @@ -282,7 +307,7 @@ export function GameGalleryPage({ -
+
发现好玩的

游戏广场

From 301f66da2d40de368c26276b00275be99fe7a9a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 30 Sep 2026 11:44:35 +0800 Subject: [PATCH 02/12] =?UTF-8?q?=E6=B8=B8=E7=8E=A9=E9=A1=B5=E6=94=B9?= =?UTF-8?q?=E6=88=90=E5=9B=BA=E5=AE=9A=E7=94=BB=E5=B8=83=EF=BC=8C=E7=A7=BB?= =?UTF-8?q?=E5=8A=A8=E7=AB=AF=E4=B8=8D=E5=86=8D=E6=95=B4=E9=A1=B5=E5=8F=AF?= =?UTF-8?q?=E6=BB=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PlatformEntryActiveFlowShell 在 game-play 舞台给页签容器加 platform-tab-panel--game-play,不再挂 overflow-auto,也不再叠加双份内边距 - index.css 新增 .platform-tab-panel--game-play,面板 overflow:hidden 并去掉右侧留白 - gameDistribution.css 让 .game-play-page 改 height:100%/min-height:0、播放器 flex:1 1 auto/min-height:0,删除移动端把页面撑高的 calc(100dvh - 8rem) - 矮视口(max-height:26rem,横屏手机)压扁启动面板并隐藏封面,保证「开始游戏」可达,页面与面板本身仍不滚动 - PlatformEntryActiveFlowShell.test.tsx 双向锁定游玩舞台页签容器不滚动、游戏广场等舞台仍保持滚动 - check-game-distribution-web-e2e.mjs 移动端段新增「移动端游玩页不可滚动(面板与整页都不溢出)」断言 - 里程碑文档补上该修复的根因、口径与验证记录 --- ...‹碑】游戏分发目录详情与在线游玩-2026-09-18.md | 6 ++- scripts/check-game-distribution-web-e2e.mjs | 17 ++++++++ .../game-distribution/gameDistribution.css | 41 +++++++++++++++---- .../PlatformEntryActiveFlowShell.test.tsx | 29 +++++++++++++ .../PlatformEntryActiveFlowShell.tsx | 7 +++- src/index.css | 6 +++ 6 files changed, 96 insertions(+), 10 deletions(-) diff --git a/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md b/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md index ca79346e7..015600f54 100644 --- a/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md +++ b/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md @@ -321,7 +321,9 @@ **生产环境**(`genarrative.world`)同一时刻 `/api/game-distribution/games` 返回 **0 条**:说明当前生产还没有任何已发布的游戏,条目 ④「真实环境整链路」需要在生产发布一个作品后再跑。也就是说此刻能拿到的真实环境证据是「dev 部署的公开读路径 + 发行隔离」,写路径与审核链路的真实环境验收仍需生产账号。 -## 网页侧交互修正(2026-09-30) +## 网页侧两处交互修正(2026-09-30) - **广场首屏「立即试玩」不再替用户选中作品**:`src/components/game-distribution/GameGalleryPage.tsx` 原来是 `onClick={() => featuredGame && openDetail(featuredGame.id)}`,点一下等于直接打开第一款游戏的详情。现在按钮只把列表滚进视野(按平台页签面板计算相对偏移,`scrollTo({ behavior: 'smooth' })`,无 `scrollTo` 时退化成直接赋值),不再触发 `onOpenDetail`;`featuredGame` 派生随之退役。回归用例 `GameDistributionPages.test.tsx`「首屏「立即试玩」滚动到列表,而不是打开第一款游戏详情」断言 `onOpenDetail` 未被调用且滚动落在页签面板上。 -- 验证:`npx vitest run src/components/game-distribution/GameDistributionPages.test.tsx`(21 passed)、`npm run typecheck`、`npx eslint`、`npm run check:encoding`、`git diff --check` 通过。视觉与真机走查仍需人工/截图评审(沿用阶段 C 第 8 条未取证口径)。 +- **游玩页不再可滚**:移动端此前是整页可滚的半屏播放器。原因是面板仍是 `overflow-auto`,而播放器用 `min-height: calc(100dvh - 8rem)` 撑高页面。现在 `PlatformEntryActiveFlowShell.tsx` 在 `game-play` 舞台给页签容器加 `platform-tab-panel--game-play`(不滚动、去掉多余内边距),`.game-play-page` 改为 `height: 100%; min-height: 0`,播放器 `flex: 1; min-height: 0` 撑满剩余高度;矮视口(`max-height: 26rem`,横屏手机)另给启动面板一套压扁布局,兜底只在极端高度下发生在启动卡片内部,页面与面板本身始终不滚动。回归用例 `PlatformEntryActiveFlowShell.test.tsx`「游玩舞台的页签容器不滚动」「游戏广场等舞台仍保持页签容器滚动」双向锁定。 +- 真机口径的守卫补在现役 E2E 里:`scripts/check-game-distribution-web-e2e.mjs` 的移动端段新增「移动端游玩页不可滚动(面板与整页都不溢出)」,断言 `.platform-tab-panel` 的 `overflow-y` 为 `hidden` 且面板与整页都不溢出(该脚本需真实本地栈,未在本轮执行)。 +- 验证:`npx vitest run src/components/game-distribution/GameDistributionPages.test.tsx`(21 passed)、`npx vitest run src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx`(20 passed)、`npm run typecheck`、`npx eslint`(四个改动文件)、`npm run check:encoding`、`git diff --check` 全部通过。视觉与真机横竖屏走查仍需人工/截图评审(沿用阶段 C 第 8 条未取证口径)。 diff --git a/scripts/check-game-distribution-web-e2e.mjs b/scripts/check-game-distribution-web-e2e.mjs index 60ba8f326..73a322544 100644 --- a/scripts/check-game-distribution-web-e2e.mjs +++ b/scripts/check-game-distribution-web-e2e.mjs @@ -522,6 +522,23 @@ async function main() { mobileStartDisabled && rotationHint, `startDisabled=${mobileStartDisabled} rotationHint=${rotationHint}`, ); + // 游玩页是固定画布:面板必须是 overflow:hidden 且不溢出,整页也不能出现滚动条。 + const playScroll = await mobilePage.evaluate(() => { + const panel = document.querySelector('.platform-tab-panel'); + return { + documentOverflow: + document.documentElement.scrollHeight - window.innerHeight, + panelOverflow: panel ? panel.scrollHeight - panel.clientHeight : 0, + panelOverflowY: panel ? getComputedStyle(panel).overflowY : 'missing', + }; + }); + check( + '移动端游玩页不可滚动(面板与整页都不溢出)', + playScroll.documentOverflow <= 1 && + playScroll.panelOverflow <= 1 && + playScroll.panelOverflowY === 'hidden', + JSON.stringify(playScroll), + ); await mobile.close(); await context.close(); } finally { diff --git a/src/components/game-distribution/gameDistribution.css b/src/components/game-distribution/gameDistribution.css index c24e193f5..dad457374 100644 --- a/src/components/game-distribution/gameDistribution.css +++ b/src/components/game-distribution/gameDistribution.css @@ -494,7 +494,9 @@ display: flex; flex-direction: column; gap: 0.9rem; - min-height: 100%; + /* 游玩页是固定画布:撑满面板而不是撑高面板,页面本身永不滚动。 */ + height: 100%; + min-height: 0; padding: clamp(0.7rem, 1.8vw, 1.5rem); } .game-play-toolbar { @@ -536,8 +538,8 @@ } .game-player-shell { position: relative; - flex: 1; - min-height: min(72vh, 48rem); + flex: 1 1 auto; + min-height: 0; overflow: hidden; border: 1px solid rgba(199, 101, 61, 0.24); border-radius: 1.5rem; @@ -581,7 +583,7 @@ display: block; width: 100%; height: 100%; - min-height: min(72vh, 48rem); + min-height: 0; border: 0; opacity: 0; transition: opacity 220ms ease; @@ -716,9 +718,34 @@ .game-play-toolbar__actions .game-play-device-hint { display: none; } - .game-player-shell, - .game-player-frame { - min-height: calc(100dvh - 8rem); +} + +/* + * 矮视口(横屏手机)里播放器只剩很小的高度,压扁启动面板保证 + * 「开始游戏」可达;页面依旧不滚动,只有这一块在极端情况下内部兜底。 + */ +@media (max-height: 26rem) { + .game-player-launch { + grid-template-columns: minmax(0, 1fr); + align-content: center; + gap: 0.5rem; + padding: 0.75rem 1rem; + text-align: center; + overflow-y: auto; + } + .game-player-launch .game-cover { + display: none; + } + .game-player-launch__copy p { + display: -webkit-box; + margin: 0.25rem 0 0.6rem; + overflow: hidden; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + } + .game-player-loading, + .game-player-timeout { + font-size: 0.8rem; } } diff --git a/src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx b/src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx index 92c5cbd8f..68cec511c 100644 --- a/src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx +++ b/src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx @@ -898,6 +898,35 @@ describe('游戏发布灰度入口', () => { ); }); + it('游玩舞台的页签容器不滚动', async () => { + gameDistributionMock.getGame.mockResolvedValue(null); + + const { container } = render( + , + ); + + const panel = container.querySelector('.platform-tab-panel'); + expect(panel?.classList.contains('platform-tab-panel--game-play')).toBe( + true, + ); + expect(panel?.className).not.toContain('overflow-auto'); + }); + + it('游戏广场等舞台仍保持页签容器滚动', async () => { + gameDistributionMock.listGames.mockResolvedValue([]); + + const { container } = render( + , + ); + expect(await screen.findByText('游戏广场')).toBeTruthy(); + + const panel = container.querySelector('.platform-tab-panel'); + expect(panel?.classList.contains('platform-tab-panel--game-play')).toBe( + false, + ); + expect(panel?.className).toContain('overflow-auto'); + }); + it('灰度命中时广场展示发布入口', async () => { loginAsAuthor(); gameDistributionMock.listGames.mockResolvedValue([]); diff --git a/src/components/platform-entry/PlatformEntryActiveFlowShell.tsx b/src/components/platform-entry/PlatformEntryActiveFlowShell.tsx index c5029d2e0..6a4f6ba7d 100644 --- a/src/components/platform-entry/PlatformEntryActiveFlowShell.tsx +++ b/src/components/platform-entry/PlatformEntryActiveFlowShell.tsx @@ -540,6 +540,11 @@ export function PlatformEntryFlowShellImpl({ selectionStage === 'game-play' || selectionStage === 'game-mine' || selectionStage === 'game-publish'; + /** + * 游玩页自己撑满剩余高度,面板不能再滚动,否则移动端会出现整页可滚的 + * 半屏播放器;其余舞台继续沿用面板滚动。 + */ + const isGamePlayStage = selectionStage === 'game-play'; const gameSearchParams = new URLSearchParams( typeof window === 'undefined' ? '' : window.location.search, ); @@ -717,7 +722,7 @@ export function PlatformEntryFlowShellImpl({
{isProfileStage ? ( Date: Wed, 30 Sep 2026 12:04:26 +0800 Subject: [PATCH 03/12] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=B8=B8=E7=8E=A9?= =?UTF-8?q?=E9=A1=B5=E5=90=AF=E5=8A=A8=E9=9D=A2=E6=9D=BF=E5=B1=85=E4=B8=AD?= =?UTF-8?q?=E6=BA=A2=E5=87=BA=E5=AF=BC=E8=87=B4=E9=A1=B6=E9=83=A8=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E6=BB=9A=E4=B8=8D=E5=88=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .game-player-launch 纵向对齐由 center 改为 safe center(保留 center 作旧浏览器回退),溢出时退回 start,不再把标题裁到 scrollTop=0 也够不到的位置 - 同步修正 ≤820px 与 max-height:26rem 两处媒体查询里的 align-content - 把 overflow-y: auto 提到 .game-player-launch 基础规则,启动卡片成为唯一的内部滚动兜底,页面与 .platform-tab-panel 仍保持 overflow:hidden - pitfalls 记录该故障模式(居中溢出叠加内部滚动会让负方向那半不可达)与真实 Chromium 夹具判据 - 里程碑文档补上 safe center 口径与取证说明 --- ...€�里程碑】游戏分发目录详情与在线游玩-2026-09-18.md | 4 ++-- docs/project-memory/shared-memory/pitfalls.md | 8 ++++++++ .../game-distribution/gameDistribution.css | 12 +++++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md b/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md index 015600f54..67d08a2f3 100644 --- a/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md +++ b/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md @@ -324,6 +324,6 @@ ## 网页侧两处交互修正(2026-09-30) - **广场首屏「立即试玩」不再替用户选中作品**:`src/components/game-distribution/GameGalleryPage.tsx` 原来是 `onClick={() => featuredGame && openDetail(featuredGame.id)}`,点一下等于直接打开第一款游戏的详情。现在按钮只把列表滚进视野(按平台页签面板计算相对偏移,`scrollTo({ behavior: 'smooth' })`,无 `scrollTo` 时退化成直接赋值),不再触发 `onOpenDetail`;`featuredGame` 派生随之退役。回归用例 `GameDistributionPages.test.tsx`「首屏「立即试玩」滚动到列表,而不是打开第一款游戏详情」断言 `onOpenDetail` 未被调用且滚动落在页签面板上。 -- **游玩页不再可滚**:移动端此前是整页可滚的半屏播放器。原因是面板仍是 `overflow-auto`,而播放器用 `min-height: calc(100dvh - 8rem)` 撑高页面。现在 `PlatformEntryActiveFlowShell.tsx` 在 `game-play` 舞台给页签容器加 `platform-tab-panel--game-play`(不滚动、去掉多余内边距),`.game-play-page` 改为 `height: 100%; min-height: 0`,播放器 `flex: 1; min-height: 0` 撑满剩余高度;矮视口(`max-height: 26rem`,横屏手机)另给启动面板一套压扁布局,兜底只在极端高度下发生在启动卡片内部,页面与面板本身始终不滚动。回归用例 `PlatformEntryActiveFlowShell.test.tsx`「游玩舞台的页签容器不滚动」「游戏广场等舞台仍保持页签容器滚动」双向锁定。 +- **游玩页不再可滚**:移动端此前是整页可滚的半屏播放器。原因是面板仍是 `overflow-auto`,而播放器用 `min-height: calc(100dvh - 8rem)` 撑高页面。现在 `PlatformEntryActiveFlowShell.tsx` 在 `game-play` 舞台给页签容器加 `platform-tab-panel--game-play`(不滚动、去掉多余内边距),`.game-play-page` 改为 `height: 100%; min-height: 0`,播放器 `flex: 1; min-height: 0` 撑满剩余高度;矮视口(`max-height: 26rem`,横屏手机)另给启动面板一套压扁布局,兜底只在极端高度下发生在启动卡片内部,页面与面板本身始终不滚动。启动面板纵向对齐用 `safe center`(`center` 作旧浏览器回退)并把 `overflow-y: auto` 放在基础规则:居中溢出会把顶部标题裁到 `scrollTop = 0` 也够不到的位置,只加内部滚动并不能修掉。回归用例 `PlatformEntryActiveFlowShell.test.tsx`「游玩舞台的页签容器不滚动」「游戏广场等舞台仍保持页签容器滚动」双向锁定。 - 真机口径的守卫补在现役 E2E 里:`scripts/check-game-distribution-web-e2e.mjs` 的移动端段新增「移动端游玩页不可滚动(面板与整页都不溢出)」,断言 `.platform-tab-panel` 的 `overflow-y` 为 `hidden` 且面板与整页都不溢出(该脚本需真实本地栈,未在本轮执行)。 -- 验证:`npx vitest run src/components/game-distribution/GameDistributionPages.test.tsx`(21 passed)、`npx vitest run src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx`(20 passed)、`npm run typecheck`、`npx eslint`(四个改动文件)、`npm run check:encoding`、`git diff --check` 全部通过。视觉与真机横竖屏走查仍需人工/截图评审(沿用阶段 C 第 8 条未取证口径)。 +- 验证:`npx vitest run src/components/game-distribution/GameDistributionPages.test.tsx`(21 passed)、`npx vitest run src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx`(20 passed)、`npm run typecheck`、`npx eslint`(四个改动文件)、`npm run check:encoding`、`git diff --check` 全部通过;启动面板的溢出可达性用真实 Chromium 静态夹具量过(见 pitfalls 同日条目)。视觉与真机横竖屏走查仍需人工/截图评审(沿用阶段 C 第 8 条未取证口径)。 diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index c9e9f00c3..c7eafc70e 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -6100,3 +6100,11 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/` - **做法**:`npm run check:agc-direct-execution-fixture -- --agc-exe "<已安装渠道包的主程序绝对路径>" --cases completed`。夹具在临时项目和 loopback Provider 上跑 CLI 执行链路,可直接核对已安装包的随包 Codex。 - **判读**:若报 `Codex app-server JSON-RPC 失败`,sidecar 已启动并返回协议错误,应先检查请求体及已安装包的源码版本;若根本无法启动,再查安装目录 `coding-agent/win-x64/manifest.json` 的组件哈希、`codex-package.json` 的版本和缺失组件。夹具模式中的 `remote_control disabled reason=provider-proxy-auth` 是预期诊断行。 + +## 2026-09-30 居中溢出叠加内部滚动,会把顶部内容裁到滚不到的地方(游玩页启动面板) + +- **现象**:极矮视口(横屏手机,游玩页 `max-height: 26rem` 那一档)里启动面板内容比容器高时,标题与简介被裁在容器上方,`scrollTop` 已经到 0 也够不到,看起来像「标题凭空消失」。 +- **根因**:`.game-player-launch` 同时有 `align-content: center`(两列布局下是 `align-items: center`)与 `overflow-y: auto`。溢出量被居中分配成上下各一半,负方向那一半落在滚动范围之外——滚动只能从 0 开始,所以顶部永远不可达。只给 `overflow-y: auto` 或只改居中都修不掉。 +- **处理**:纵向对齐改用 `safe center`(先写 `align-content: center` / `align-items: center` 作旧浏览器回退,再写 `safe center`),溢出时退回 start;并把 `overflow-y: auto` 提到基础规则,让启动卡片成为唯一的内部滚动兜底,页面与 `.platform-tab-panel` 本身仍保持 `overflow: hidden`。 +- **判据**:真实 Chromium 夹具(`tmp/launch-overflow-check`,把 `gameDistribution.css` 直接链进静态页面)在 `844x200` 下量:改回 `center` 时 `titleTopAtScrollZero = -20`(不可达),改成 `safe center` 后为 `+12`(可达),滚到底按钮同样可达;`390x844` 与 `1280x800` 正常视口不溢出,位置与改前一致。 +- **关联**:`src/components/game-distribution/gameDistribution.css` 的 `.game-player-launch` 与两处媒体查询、`src/components/game-distribution/GamePlayPage.tsx`。 diff --git a/src/components/game-distribution/gameDistribution.css b/src/components/game-distribution/gameDistribution.css index dad457374..227c4a3e6 100644 --- a/src/components/game-distribution/gameDistribution.css +++ b/src/components/game-distribution/gameDistribution.css @@ -551,7 +551,14 @@ inset: 0; display: grid; grid-template-columns: minmax(8rem, 15rem) minmax(0, 1fr); + /* + * safe:内容比容器高时退回 start,配合下面的兜底滚动,顶部标题与「开始游戏」 + * 都不会像居中溢出那样被裁到滚不到的位置。 + * 启动卡片是唯一允许内部滚动的兜底,页面与面板本身始终不滚动。 + */ align-items: center; + align-items: safe center; + overflow-y: auto; gap: clamp(1.1rem, 4vw, 2.8rem); padding: clamp(1rem, 4vw, 2.5rem); background: linear-gradient(135deg, #1e1820, #302329); @@ -700,7 +707,9 @@ } .game-player-launch { grid-template-columns: 1fr; + /* safe:内容比容器高时退回 start,避免居中把顶部标题裁到滚不到的地方。 */ align-content: center; + align-content: safe center; gap: 1rem; text-align: center; } @@ -727,11 +736,12 @@ @media (max-height: 26rem) { .game-player-launch { grid-template-columns: minmax(0, 1fr); + /* 单行布局下由 align-content 决定纵向位置,同样必须用 safe。 */ align-content: center; + align-content: safe center; gap: 0.5rem; padding: 0.75rem 1rem; text-align: center; - overflow-y: auto; } .game-player-launch .game-cover { display: none; From 3bb1c28a51d5b1f511ea1a51c5617cebe476b6db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 30 Sep 2026 12:43:25 +0800 Subject: [PATCH 04/12] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=88=91=E7=9A=84?= =?UTF-8?q?=E6=B8=B8=E6=88=8F=E9=A1=B5=E7=A7=BB=E5=8A=A8=E7=AB=AF=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=A8=AA=E5=90=91=E6=BA=A2=E5=87=BA=E5=B9=B6=E4=B8=8E?= =?UTF-8?q?=E7=94=BB=E5=BB=8A=E7=BB=9F=E4=B8=80=E5=B8=83=E5=B1=80=E5=8F=A3?= =?UTF-8?q?=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .my-game-list 改 grid-template-columns: minmax(0, 1fr),单列 auto 轨道不再被内容最小宽度撑开 - .my-game-card 加 min-width: 0 与 overflow: hidden,卡片可被压缩并裁掉溢出,与 .game-card 同口径 - .my-game-card__title 加 min-width: 0 与 overflow-wrap: anywhere,长标题不撑宽标题行 - pitfalls 记录该机制(auto 轨道最小尺寸 = 内容最小宽度 + nowrap 简介 + 缺 min-width:0)与 Chromium 夹具判据 - 里程碑文档补上该修复与 390/360 视口的测量结论 --- .../【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md | 3 ++- docs/project-memory/shared-memory/pitfalls.md | 9 +++++++++ src/components/game-distribution/gameDistribution.css | 8 ++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md b/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md index 67d08a2f3..ebe8da069 100644 --- a/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md +++ b/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md @@ -325,5 +325,6 @@ - **广场首屏「立即试玩」不再替用户选中作品**:`src/components/game-distribution/GameGalleryPage.tsx` 原来是 `onClick={() => featuredGame && openDetail(featuredGame.id)}`,点一下等于直接打开第一款游戏的详情。现在按钮只把列表滚进视野(按平台页签面板计算相对偏移,`scrollTo({ behavior: 'smooth' })`,无 `scrollTo` 时退化成直接赋值),不再触发 `onOpenDetail`;`featuredGame` 派生随之退役。回归用例 `GameDistributionPages.test.tsx`「首屏「立即试玩」滚动到列表,而不是打开第一款游戏详情」断言 `onOpenDetail` 未被调用且滚动落在页签面板上。 - **游玩页不再可滚**:移动端此前是整页可滚的半屏播放器。原因是面板仍是 `overflow-auto`,而播放器用 `min-height: calc(100dvh - 8rem)` 撑高页面。现在 `PlatformEntryActiveFlowShell.tsx` 在 `game-play` 舞台给页签容器加 `platform-tab-panel--game-play`(不滚动、去掉多余内边距),`.game-play-page` 改为 `height: 100%; min-height: 0`,播放器 `flex: 1; min-height: 0` 撑满剩余高度;矮视口(`max-height: 26rem`,横屏手机)另给启动面板一套压扁布局,兜底只在极端高度下发生在启动卡片内部,页面与面板本身始终不滚动。启动面板纵向对齐用 `safe center`(`center` 作旧浏览器回退)并把 `overflow-y: auto` 放在基础规则:居中溢出会把顶部标题裁到 `scrollTop = 0` 也够不到的位置,只加内部滚动并不能修掉。回归用例 `PlatformEntryActiveFlowShell.test.tsx`「游玩舞台的页签容器不滚动」「游戏广场等舞台仍保持页签容器滚动」双向锁定。 +- **「我的游戏」列表与画廊同口径**:`.my-game-list` 的单列 `auto` 轨道会被 `white-space: nowrap` 的简介顶到内容最小宽度(实测 592px),加上 `.my-game-card` 缺 `min-width: 0`,390/360 宽的手机上卡片超出视口、面板横向可滚且看着不居中。现在改成画廊那套口径(`grid-template-columns: minmax(0, 1fr)`、`.my-game-card { min-width: 0; overflow: hidden }`、标题 `min-width: 0; overflow-wrap: anywhere`);真实 Chromium 夹具在 390/360 下量到轨道=卡片=可视宽、面板横向溢出 0(改前 592px 卡片、横向可滚 230/260px)。 - 真机口径的守卫补在现役 E2E 里:`scripts/check-game-distribution-web-e2e.mjs` 的移动端段新增「移动端游玩页不可滚动(面板与整页都不溢出)」,断言 `.platform-tab-panel` 的 `overflow-y` 为 `hidden` 且面板与整页都不溢出(该脚本需真实本地栈,未在本轮执行)。 -- 验证:`npx vitest run src/components/game-distribution/GameDistributionPages.test.tsx`(21 passed)、`npx vitest run src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx`(20 passed)、`npm run typecheck`、`npx eslint`(四个改动文件)、`npm run check:encoding`、`git diff --check` 全部通过;启动面板的溢出可达性用真实 Chromium 静态夹具量过(见 pitfalls 同日条目)。视觉与真机横竖屏走查仍需人工/截图评审(沿用阶段 C 第 8 条未取证口径)。 +- 验证:`npx vitest run src/components/game-distribution/GameDistributionPages.test.tsx`(21 passed)、`npx vitest run src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx`(20 passed)、`npm run typecheck`、`npx eslint`(四个改动文件)、`npm run check:encoding`、`git diff --check` 全部通过;启动面板的溢出可达性、「我的游戏」移动端横向溢出都用真实 Chromium 静态夹具量过(见 pitfalls 同日条目)。视觉与真机横竖屏走查仍需人工/截图评审(沿用阶段 C 第 8 条未取证口径)。 diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index c7eafc70e..c4c2e9e32 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -6108,3 +6108,12 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/` - **处理**:纵向对齐改用 `safe center`(先写 `align-content: center` / `align-items: center` 作旧浏览器回退,再写 `safe center`),溢出时退回 start;并把 `overflow-y: auto` 提到基础规则,让启动卡片成为唯一的内部滚动兜底,页面与 `.platform-tab-panel` 本身仍保持 `overflow: hidden`。 - **判据**:真实 Chromium 夹具(`tmp/launch-overflow-check`,把 `gameDistribution.css` 直接链进静态页面)在 `844x200` 下量:改回 `center` 时 `titleTopAtScrollZero = -20`(不可达),改成 `safe center` 后为 `+12`(可达),滚到底按钮同样可达;`390x844` 与 `1280x800` 正常视口不溢出,位置与改前一致。 - **关联**:`src/components/game-distribution/gameDistribution.css` 的 `.game-player-launch` 与两处媒体查询、`src/components/game-distribution/GamePlayPage.tsx`。 + +## 2026-09-30 单列 grid 的 auto 轨道被 nowrap 文本撑开,移动端面板只剩横向滚动 + +- **现象**:「我的游戏」页在 390/360 宽的手机上可以横向滑动,卡片比可视区宽(实测卡片 592px、可视 390px、面板横向可滚 230px),看起来也不居中;同一页面的画廊卡片却正常。 +- **根因**:三件事叠在一起。① `.my-game-list` 是单列 grid,隐式列宽是 `auto`,而 auto 轨道的最小尺寸是**内容最小宽度**,不会被容器宽度压回去;② `.my-game-card`(flex 行)没有 `min-width: 0`,其最小内容宽度 = 封面 `8.5rem` + gap + 正文最小内容;③ 正文里的 `.my-game-card__summary` 是 `white-space: nowrap`,一条长且不可断行的简介就把最小内容宽度顶到 592px。画廊之所以正常,是因为 `.game-grid` 用 `repeat(n, minmax(0, 1fr))`,`.game-card` 又有 `min-width: 0; overflow: hidden`。 +- **处理**:让「我的游戏」与画廊同口径——`.my-game-list` 改 `grid-template-columns: minmax(0, 1fr)`,`.my-game-card` 加 `min-width: 0; overflow: hidden`,标题加 `min-width: 0; overflow-wrap: anywhere`。改后 390/360 实测轨道 = 列表 = 卡片 = 可视宽,面板横向溢出 0。 +- **顺带观察(本次未改)**:移动端面板左右留白本就不对称——左 12/24px(Tailwind `px-3`/`px-6`),右 0(`.platform-desktop-shell--workbench .platform-tab-panel` 与 `@media (max-width: 639px)` 都把 `padding-right` 压成 0),所以列表左右留白是 28/16。画廊完全相同,属既有全局口径,不在本次范围内。 +- **判据**:真实 Chromium 静态夹具 `tmp/mygames-check/`(`tmp/` 已 gitignore,由 dev 栈 `http://127.0.0.1:10000/tmp/mygames-check/` 托管)同时渲染「我的游戏」和画廊两组卡片,量面板横向溢出与网格轨道宽度:改前 390/360 下轨道 591.891px、卡片 592px、面板横向可滚 230/260px;改后轨道 = 列表 = 卡片 = 可视宽、溢出 0。布局问题 jsdom 量不出来,这类回归目前靠这个夹具手工复核。 +- **关联**:`src/components/game-distribution/gameDistribution.css`、`src/components/game-distribution/MyGamesPage.tsx`、`.game-grid`/`.game-card`。 diff --git a/src/components/game-distribution/gameDistribution.css b/src/components/game-distribution/gameDistribution.css index 227c4a3e6..03ff3d527 100644 --- a/src/components/game-distribution/gameDistribution.css +++ b/src/components/game-distribution/gameDistribution.css @@ -761,10 +761,16 @@ .my-game-list { display: grid; + /* 与画廊 .game-grid 同口径:轨道可以被压到内容最小宽度以下,否则长标题/长简介 + 会把单列轨道撑到视口之外,移动端面板出现横向滚动。 */ + grid-template-columns: minmax(0, 1fr); gap: 0.9rem; } .my-game-card { display: flex; + /* 与 .game-card 同口径:卡片自身可被压缩并裁掉溢出,保证卡片永不宽于所在轨道。 */ + min-width: 0; + overflow: hidden; gap: 1rem; align-items: stretch; border: 1px solid var(--platform-surface-border); @@ -793,6 +799,8 @@ gap: 0.6rem; } .my-game-card__title { + min-width: 0; + overflow-wrap: anywhere; border: 0; padding: 0; background: transparent; From baca440c5d1d901e026f83450332b9bc30da0d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 30 Sep 2026 12:52:15 +0800 Subject: [PATCH 05/12] =?UTF-8?q?=E5=AF=B9=E9=BD=90=E9=A1=B5=E7=AD=BE?= =?UTF-8?q?=E9=9D=A2=E6=9D=BF=E5=B7=A6=E5=8F=B3=E7=95=99=E7=99=BD=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E6=8E=89=E7=A7=BB=E5=8A=A8=E7=AB=AF=E5=8F=B3=E4=BE=A7?= =?UTF-8?q?=E8=B4=B4=E8=BE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 .platform-tab-panel 里写死的 padding-right: 0.25rem:它是不分层规则,优先级高于 Tailwind @layer utilities 的 padding-inline,把右内边距钉成 0 - 同时删除 @media (max-width: 639px) .platform-tab-panel 与 .platform-desktop-shell--workbench .platform-tab-panel 里各自重复的 padding-right: 0 - 左右留白统一交给使用处的 px-3 / sm:px-6;没有 px 类的舞台(创作主页、我的、游玩页)保持 0/0 - 实测:390 下面板 12/12、列表左右留白 28/28;820 下 24/24、44/44(改前为 12/0、28/16 与 24/0、44/20),横向溢出仍为 0 - pitfalls 与里程碑文档同步该根因(unlayered 覆盖 Tailwind 层级)与测量结论 --- ...€�里程碑】游戏分发目录详情与在线游玩-2026-09-18.md | 1 + docs/project-memory/shared-memory/pitfalls.md | 2 +- src/index.css | 12 +++++------- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md b/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md index ebe8da069..a4098dda0 100644 --- a/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md +++ b/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md @@ -326,5 +326,6 @@ - **广场首屏「立即试玩」不再替用户选中作品**:`src/components/game-distribution/GameGalleryPage.tsx` 原来是 `onClick={() => featuredGame && openDetail(featuredGame.id)}`,点一下等于直接打开第一款游戏的详情。现在按钮只把列表滚进视野(按平台页签面板计算相对偏移,`scrollTo({ behavior: 'smooth' })`,无 `scrollTo` 时退化成直接赋值),不再触发 `onOpenDetail`;`featuredGame` 派生随之退役。回归用例 `GameDistributionPages.test.tsx`「首屏「立即试玩」滚动到列表,而不是打开第一款游戏详情」断言 `onOpenDetail` 未被调用且滚动落在页签面板上。 - **游玩页不再可滚**:移动端此前是整页可滚的半屏播放器。原因是面板仍是 `overflow-auto`,而播放器用 `min-height: calc(100dvh - 8rem)` 撑高页面。现在 `PlatformEntryActiveFlowShell.tsx` 在 `game-play` 舞台给页签容器加 `platform-tab-panel--game-play`(不滚动、去掉多余内边距),`.game-play-page` 改为 `height: 100%; min-height: 0`,播放器 `flex: 1; min-height: 0` 撑满剩余高度;矮视口(`max-height: 26rem`,横屏手机)另给启动面板一套压扁布局,兜底只在极端高度下发生在启动卡片内部,页面与面板本身始终不滚动。启动面板纵向对齐用 `safe center`(`center` 作旧浏览器回退)并把 `overflow-y: auto` 放在基础规则:居中溢出会把顶部标题裁到 `scrollTop = 0` 也够不到的位置,只加内部滚动并不能修掉。回归用例 `PlatformEntryActiveFlowShell.test.tsx`「游玩舞台的页签容器不滚动」「游戏广场等舞台仍保持页签容器滚动」双向锁定。 - **「我的游戏」列表与画廊同口径**:`.my-game-list` 的单列 `auto` 轨道会被 `white-space: nowrap` 的简介顶到内容最小宽度(实测 592px),加上 `.my-game-card` 缺 `min-width: 0`,390/360 宽的手机上卡片超出视口、面板横向可滚且看着不居中。现在改成画廊那套口径(`grid-template-columns: minmax(0, 1fr)`、`.my-game-card { min-width: 0; overflow: hidden }`、标题 `min-width: 0; overflow-wrap: anywhere`);真实 Chromium 夹具在 390/360 下量到轨道=卡片=可视宽、面板横向溢出 0(改前 592px 卡片、横向可滚 230/260px)。 +- **页签面板左右留白对齐**:`.platform-tab-panel` 原本写死 `padding-right`(并在移动端与 workbench 两处再压成 0),而它是不分层规则、优先级高于 Tailwind 的 `padding-inline`,导致面板左 12/24px、右 0,列表左右留白 28/16。三处 `padding-right` 删除后左右统一由使用处的 `px-3`/`sm:px-6` 决定:390 下 12/12(列表 28/28)、820 下 24/24(列表 44/44),无 px 类的舞台仍是 0/0;Chromium 夹具实测「我的游戏」与画廊两张页面同时恢复对称,且横向溢出保持 0。 - 真机口径的守卫补在现役 E2E 里:`scripts/check-game-distribution-web-e2e.mjs` 的移动端段新增「移动端游玩页不可滚动(面板与整页都不溢出)」,断言 `.platform-tab-panel` 的 `overflow-y` 为 `hidden` 且面板与整页都不溢出(该脚本需真实本地栈,未在本轮执行)。 - 验证:`npx vitest run src/components/game-distribution/GameDistributionPages.test.tsx`(21 passed)、`npx vitest run src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx`(20 passed)、`npm run typecheck`、`npx eslint`(四个改动文件)、`npm run check:encoding`、`git diff --check` 全部通过;启动面板的溢出可达性、「我的游戏」移动端横向溢出都用真实 Chromium 静态夹具量过(见 pitfalls 同日条目)。视觉与真机横竖屏走查仍需人工/截图评审(沿用阶段 C 第 8 条未取证口径)。 diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index c4c2e9e32..29c47e11a 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -6114,6 +6114,6 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/` - **现象**:「我的游戏」页在 390/360 宽的手机上可以横向滑动,卡片比可视区宽(实测卡片 592px、可视 390px、面板横向可滚 230px),看起来也不居中;同一页面的画廊卡片却正常。 - **根因**:三件事叠在一起。① `.my-game-list` 是单列 grid,隐式列宽是 `auto`,而 auto 轨道的最小尺寸是**内容最小宽度**,不会被容器宽度压回去;② `.my-game-card`(flex 行)没有 `min-width: 0`,其最小内容宽度 = 封面 `8.5rem` + gap + 正文最小内容;③ 正文里的 `.my-game-card__summary` 是 `white-space: nowrap`,一条长且不可断行的简介就把最小内容宽度顶到 592px。画廊之所以正常,是因为 `.game-grid` 用 `repeat(n, minmax(0, 1fr))`,`.game-card` 又有 `min-width: 0; overflow: hidden`。 - **处理**:让「我的游戏」与画廊同口径——`.my-game-list` 改 `grid-template-columns: minmax(0, 1fr)`,`.my-game-card` 加 `min-width: 0; overflow: hidden`,标题加 `min-width: 0; overflow-wrap: anywhere`。改后 390/360 实测轨道 = 列表 = 卡片 = 可视宽,面板横向溢出 0。 -- **顺带观察(本次未改)**:移动端面板左右留白本就不对称——左 12/24px(Tailwind `px-3`/`px-6`),右 0(`.platform-desktop-shell--workbench .platform-tab-panel` 与 `@media (max-width: 639px)` 都把 `padding-right` 压成 0),所以列表左右留白是 28/16。画廊完全相同,属既有全局口径,不在本次范围内。 +- **同批修掉左右留白不对称**:面板左留白 12/24px(Tailwind `px-3`/`sm:px-6`),右留白却是 0,列表左右留白因此是 28/16。根因是 `src/index.css` 里 `.platform-tab-panel` 写死了 `padding-right: 0.25rem`——它属于**不分层(unlayered)规则,优先级高于 Tailwind 放进 `@layer utilities` 的 `padding-inline`**,把右内边距钉死在 0;`@media (max-width: 639px) .platform-tab-panel` 与 `.platform-desktop-shell--workbench .platform-tab-panel` 又各写了一次 `padding-right: 0`。三处声明全部删掉,左右统一交给使用处的 `padding-inline`;没有 px 类的舞台(创作主页 / 我的 / 游玩页)维持 0/0。改后实测 390 下 12/12、820 下 24/24,列表左右留白 28/28 与 44/44。 - **判据**:真实 Chromium 静态夹具 `tmp/mygames-check/`(`tmp/` 已 gitignore,由 dev 栈 `http://127.0.0.1:10000/tmp/mygames-check/` 托管)同时渲染「我的游戏」和画廊两组卡片,量面板横向溢出与网格轨道宽度:改前 390/360 下轨道 591.891px、卡片 592px、面板横向可滚 230/260px;改后轨道 = 列表 = 卡片 = 可视宽、溢出 0。布局问题 jsdom 量不出来,这类回归目前靠这个夹具手工复核。 - **关联**:`src/components/game-distribution/gameDistribution.css`、`src/components/game-distribution/MyGamesPage.tsx`、`.game-grid`/`.game-card`。 diff --git a/src/index.css b/src/index.css index 6c37aff9f..cb2a68469 100644 --- a/src/index.css +++ b/src/index.css @@ -1173,13 +1173,16 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock { height: 100%; overflow-y: auto; overflow-x: hidden; - padding-right: 0.25rem; + /* + * 左右留白只由使用处的 `px-3` / `sm:px-6`(Tailwind 的 padding-inline)决定。 + * 这里以前写死 `padding-right`,会压过 Tailwind 的 inline padding,让右边永远 + * 比左边小一截(移动端实测左 12/右 0),页面看着不居中。 + */ } /* 游玩页是固定画布:面板不滚动,播放器撑满剩余高度,移动端也不会整页可滚。 */ .platform-tab-panel--game-play { overflow: hidden; - padding-right: 0; } .platform-tab-panel--hidden { @@ -11447,10 +11450,6 @@ button.image-canvas-editor__reference-chip:disabled { font-size: 0.62rem; } - .platform-tab-panel { - padding-right: 0; - } - .platform-mobile-topbar { min-height: 2.75rem; } @@ -12727,7 +12726,6 @@ button.image-canvas-editor__reference-chip:disabled { .platform-desktop-shell--workbench .platform-tab-panel { height: 100%; overflow: auto; - padding-right: 0; } .platform-desktop-shell--workbench .creation-landing { From 0d3937b7eb3f341e751b24ffcac62618e39c0490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 30 Sep 2026 12:57:00 +0800 Subject: [PATCH 06/12] =?UTF-8?q?=E5=B9=BF=E5=9C=BA=E7=9A=84=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E6=B8=B8=E6=88=8F=E4=B8=8E=E6=88=91=E7=9A=84=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=E5=85=A5=E5=8F=A3=E4=B8=8A=E7=A7=BB=E5=88=B0=20hero?= =?UTF-8?q?=20=E5=8F=B3=E4=B8=8A=E8=A7=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GameGalleryPage 新增 .game-page-actions 一行,把「发布游戏」「我的游戏」从 hero 下方的工具栏动作区移出,右对齐放在 hero 上方 - 工具栏只保留「游戏广场」标题与搜索框,不再和入口按钮挤在一行 - .game-page-actions 设 flex-end、0.6rem 间距与 1.1rem 底部间距;两个入口都不可用时整行不渲染,hero 位置不变 - 实测 390/360/820 三档:按钮在 hero 之上、与 hero 间距 18px、右边缘与 hero 对齐 - 里程碑文档记录该布局调整 --- ...‹碑】游戏分发目录详情与在线游玩-2026-09-18.md | 1 + .../game-distribution/GameGalleryPage.tsx | 44 +++++++++++-------- .../game-distribution/gameDistribution.css | 7 +++ 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md b/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md index a4098dda0..11051faee 100644 --- a/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md +++ b/docs/project-memory/plans/【里程碑】游戏分发目录详情与在线游玩-2026-09-18.md @@ -327,5 +327,6 @@ - **游玩页不再可滚**:移动端此前是整页可滚的半屏播放器。原因是面板仍是 `overflow-auto`,而播放器用 `min-height: calc(100dvh - 8rem)` 撑高页面。现在 `PlatformEntryActiveFlowShell.tsx` 在 `game-play` 舞台给页签容器加 `platform-tab-panel--game-play`(不滚动、去掉多余内边距),`.game-play-page` 改为 `height: 100%; min-height: 0`,播放器 `flex: 1; min-height: 0` 撑满剩余高度;矮视口(`max-height: 26rem`,横屏手机)另给启动面板一套压扁布局,兜底只在极端高度下发生在启动卡片内部,页面与面板本身始终不滚动。启动面板纵向对齐用 `safe center`(`center` 作旧浏览器回退)并把 `overflow-y: auto` 放在基础规则:居中溢出会把顶部标题裁到 `scrollTop = 0` 也够不到的位置,只加内部滚动并不能修掉。回归用例 `PlatformEntryActiveFlowShell.test.tsx`「游玩舞台的页签容器不滚动」「游戏广场等舞台仍保持页签容器滚动」双向锁定。 - **「我的游戏」列表与画廊同口径**:`.my-game-list` 的单列 `auto` 轨道会被 `white-space: nowrap` 的简介顶到内容最小宽度(实测 592px),加上 `.my-game-card` 缺 `min-width: 0`,390/360 宽的手机上卡片超出视口、面板横向可滚且看着不居中。现在改成画廊那套口径(`grid-template-columns: minmax(0, 1fr)`、`.my-game-card { min-width: 0; overflow: hidden }`、标题 `min-width: 0; overflow-wrap: anywhere`);真实 Chromium 夹具在 390/360 下量到轨道=卡片=可视宽、面板横向溢出 0(改前 592px 卡片、横向可滚 230/260px)。 - **页签面板左右留白对齐**:`.platform-tab-panel` 原本写死 `padding-right`(并在移动端与 workbench 两处再压成 0),而它是不分层规则、优先级高于 Tailwind 的 `padding-inline`,导致面板左 12/24px、右 0,列表左右留白 28/16。三处 `padding-right` 删除后左右统一由使用处的 `px-3`/`sm:px-6` 决定:390 下 12/12(列表 28/28)、820 下 24/24(列表 44/44),无 px 类的舞台仍是 0/0;Chromium 夹具实测「我的游戏」与画廊两张页面同时恢复对称,且横向溢出保持 0。 +- **广场的「发布游戏」「我的游戏」上移到 hero 右上角**:两个入口原本混在 hero 下方的 `.game-toolbar` 动作区(和搜索框同排),现在提到 `.game-page-actions` 一行、右对齐放在 hero 上方,并留 1.1rem 底部间距;工具栏只保留标题与搜索。未登录或灰度未命中时两者都不渲染,该行随之消失。Chromium 夹具实测 390/360/820 三档都是 `aboveHero=true`、与 hero 间距 18px、右边缘与 hero 对齐。 - 真机口径的守卫补在现役 E2E 里:`scripts/check-game-distribution-web-e2e.mjs` 的移动端段新增「移动端游玩页不可滚动(面板与整页都不溢出)」,断言 `.platform-tab-panel` 的 `overflow-y` 为 `hidden` 且面板与整页都不溢出(该脚本需真实本地栈,未在本轮执行)。 - 验证:`npx vitest run src/components/game-distribution/GameDistributionPages.test.tsx`(21 passed)、`npx vitest run src/components/platform-entry/PlatformEntryActiveFlowShell.test.tsx`(20 passed)、`npm run typecheck`、`npx eslint`(四个改动文件)、`npm run check:encoding`、`git diff --check` 全部通过;启动面板的溢出可达性、「我的游戏」移动端横向溢出都用真实 Chromium 静态夹具量过(见 pitfalls 同日条目)。视觉与真机横竖屏走查仍需人工/截图评审(沿用阶段 C 第 8 条未取证口径)。 diff --git a/src/components/game-distribution/GameGalleryPage.tsx b/src/components/game-distribution/GameGalleryPage.tsx index 36d888e05..6df822fc1 100644 --- a/src/components/game-distribution/GameGalleryPage.tsx +++ b/src/components/game-distribution/GameGalleryPage.tsx @@ -275,8 +275,33 @@ export function GameGalleryPage({ [onOpenDetail, scrollKey], ); + const hasPageActions = Boolean(onOpenPublish || onOpenMyGames); + return (
+ {hasPageActions ? ( +
+ {onOpenPublish ? ( + + 发布游戏 + + ) : null} + {onOpenMyGames ? ( + + 我的游戏 + + ) : null} +
+ ) : null}
@@ -313,25 +338,6 @@ export function GameGalleryPage({

游戏广场

- {onOpenPublish ? ( - - 发布游戏 - - ) : null} - {onOpenMyGames ? ( - - 我的游戏 - - ) : null}