diff --git a/src/components/game-distribution/GameDistributionPages.test.tsx b/src/components/game-distribution/GameDistributionPages.test.tsx index 29d311a5f..6c793673b 100644 --- a/src/components/game-distribution/GameDistributionPages.test.tsx +++ b/src/components/game-distribution/GameDistributionPages.test.tsx @@ -2094,10 +2094,13 @@ describe('GameLineagePage 可视化与交互', () => { ); await screen.findByRole('button', { name: /《母版》/u }); - // jsdom 没有布局尺寸:min(scaleX, scaleY) 会远小于 1,因此触发可读下限 60%。 - expect( - document.querySelector('.game-lineage-zoom')?.textContent, - ).toBe('60%'); + // 初次适配在 effect 里落地:先等它生效(jsdom 无布局尺寸 → min(scaleX, scaleY) 远小于 1, + // 因此命中可读下限 60%)。 + await waitFor(() => + expect(document.querySelector('.game-lineage-zoom')?.textContent).toBe( + '60%', + ), + ); expect(screen.getByText(/已按可读缩放显示/u)).toBeTruthy(); // 主动点「适应窗口」才做真正的全览(不受可读下限约束),提示随之收起。 fireEvent.click(screen.getByRole('button', { name: '适应窗口' })); diff --git a/src/components/game-distribution/lineageTreeLayout.ts b/src/components/game-distribution/lineageTreeLayout.ts index f246da077..99aeedb6e 100644 --- a/src/components/game-distribution/lineageTreeLayout.ts +++ b/src/components/game-distribution/lineageTreeLayout.ts @@ -1,4 +1,4 @@ -import type { GameDistributionLineageNodeWithCover } from '../../services/gameDistributionClient'; +import type { GameDistributionLineageNode } from '../../services/gameDistributionClient'; /** * 创作族谱树的可视化布局(tidy tree 的轻量实现)。 @@ -32,7 +32,7 @@ export type LineageLayoutNode = { /** 真实作品是 gameId;占位父节点是那个不可用的父作品 ID。 */ gameId: string; /** 占位节点没有原始节点(服务端不会把不可用父作品当成节点发出来)。 */ - node: GameDistributionLineageNodeWithCover | null; + node: GameDistributionLineageNode | null; /** 占位节点为 null:不展示被删作品的标题。 */ title: string | null; /** 占位节点为 null:不展示被删作品的作者名。 */ @@ -94,7 +94,7 @@ function roundTo(value: number, digits = 2) { } export function buildLineageTreeLayout( - nodes: readonly GameDistributionLineageNodeWithCover[], + nodes: readonly GameDistributionLineageNode[], options: LineageLayoutOptions = {}, ): LineageTreeLayout { const nodeWidth = options.nodeWidth ?? LINEAGE_NODE_WIDTH; @@ -106,7 +106,7 @@ export function buildLineageTreeLayout( const visibleIds = new Set(nodes.map((node) => node.gameId)); const entries: { gameId: string; - node: GameDistributionLineageNodeWithCover | null; + node: GameDistributionLineageNode | null; title: string | null; authorName: string | null; coverObjectKey: string | null; diff --git a/src/services/gameDistributionClient.ts b/src/services/gameDistributionClient.ts index 56fc888b3..fdfd0d7e6 100644 --- a/src/services/gameDistributionClient.ts +++ b/src/services/gameDistributionClient.ts @@ -545,30 +545,17 @@ export async function listMyCollections() { }; } -/** - * 族谱节点 + 服务端正在补的封面字段。 - * - * `coverObjectKey` 目前由服务端在族谱节点上补齐(TS 契约在 `packages/shared`,本轮不改), - * 因此这里做**本地宽化**:字段可能存在、也可能还不存在,渲染侧两种都要能跑。 - */ -export type GameDistributionLineageNodeWithCover = - GameDistributionLineageResponse['nodes'][number] & { - coverObjectKey?: string | null; - }; - /** * 读取以某作品的根为顶的创作族谱树。公开只读接口,匿名可读; * 作品不存在或已软删除时服务端返回 404,这里按失败抛出,由页面走既有错误态。 + * + * 节点形状直接复用契约类型:`coverObjectKey` 已在 `GameDistributionLineageNode` 里 + * (可选字段,无封面时为 null),因此这里不再需要本地宽化。 */ export async function getGameLineage(gameId: string) { const normalizedId = gameId.trim(); if (!normalizedId) return null; - return requestJson<{ - rootGameId: string; - root?: GameDistributionLineageNodeWithCover | null; - nodes: GameDistributionLineageNodeWithCover[]; - truncated: boolean; - }>( + return requestJson( `/games/${encodeURIComponent(normalizedId)}/lineage`, { method: 'GET', cache: 'no-store' }, '读取创作族谱失败',