族谱节点封面接契约类型,去掉本地宽化
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m31s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m8s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m30s
Project CI / Frontend tests (pull_request) Successful in 2m45s
Project CI / Backend tests (pull_request) Successful in 7m49s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m58s
Project CI / Repository checks (pull_request) Successful in 6m1s
Project CI / Native shell tests (pull_request) Successful in 9m11s

contract now declares coverObjectKey; drop the local widening and use GameDistributionLineageNode directly (client + layout), keep card cover branch.
This commit is contained in:
2026-10-06 14:51:41 +08:00
parent 54e05d5e84
commit 37ad96368c
3 changed files with 15 additions and 25 deletions
@@ -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: '适应窗口' }));
@@ -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;
+4 -17
View File
@@ -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<GameDistributionLineageResponse>(
`/games/${encodeURIComponent(normalizedId)}/lineage`,
{ method: 'GET', cache: 'no-store' },
'读取创作族谱失败',