From 04ab7ee0da35a54b19b99f42d484b1e8eb0d4923 Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Wed, 9 Sep 2026 16:41:03 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B5=84=E6=BA=90=E7=94=BB=E5=B8=83=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E6=80=81=E6=94=B9=E4=B8=BA=E6=AF=8F=E6=A0=8F=E7=9B=AE?= =?UTF-8?q?=E6=A1=86=E5=86=85=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 撤销全屏遮罩,改为每个功能画布框内各自转圈:该栏目一张预览都没落地时显示,本栏目加载完立即消失 版本、占位与音频卡片本来不请求卡片预览,不计入等待,避免一直转圈 加载态 pointer-events 为 none,不拦截点击,未加载完也能直接进入栏目 判定函数改为按栏目,补充 resourceBookModel 用例 --- apps/ai-game-creator-shell/src/styles.css | 12 +-- .../src/view/project-development/index.tsx | 78 +++++++++++-------- .../project-development/resourceBookModel.ts | 13 +--- .../tests/resourceBookModel.test.ts | 31 +++----- 4 files changed, 63 insertions(+), 71 deletions(-) diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css index 7ea1cc2ab..9103e5587 100644 --- a/apps/ai-game-creator-shell/src/styles.css +++ b/apps/ai-game-creator-shell/src/styles.css @@ -5634,18 +5634,14 @@ iframe.preview-frame { pointer-events: none; } -.game-resource-book-loading { +.game-resource-book-thumbnail-loading { position: absolute; - inset: 0; - z-index: 40; + inset: 42px 0 0; + z-index: 3; display: flex; - flex-direction: column; align-items: center; justify-content: center; - gap: 10px; - background: rgb(255 253 250 / 78%); - color: #a08073; - font-size: 13px; + color: #c46a40; pointer-events: none; } diff --git a/apps/ai-game-creator-shell/src/view/project-development/index.tsx b/apps/ai-game-creator-shell/src/view/project-development/index.tsx index d24b0fc2b..c470d3fba 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/index.tsx +++ b/apps/ai-game-creator-shell/src/view/project-development/index.tsx @@ -912,6 +912,7 @@ function ResourceBookThumbnail({ resources, onOpen, registerElement, + loading = false, }: { category: ResourceCategory; resources: ProjectResource[]; @@ -920,6 +921,7 @@ function ResourceBookThumbnail({ category: ResourceCategory, element: HTMLElement | null, ) => void; + loading?: boolean; }) { const stacks = Array.from( resources.reduce((groups, resource) => { @@ -978,6 +980,15 @@ function ResourceBookThumbnail({ 打开{categoryLabels[category]} + {loading ? ( + + + ) : null} ); } @@ -2191,36 +2202,40 @@ export default function ProjectDevelopmentView({ sortMode, visibleCategoryOrder, ]); - const resourceBookOverviewReady = useMemo( - () => - visibleCategoryOrder.every((category) => { - const rect = resourceBookOverviewRects.get(category); - return Boolean(rect && rect.width > 0 && rect.height > 0); - }), - [resourceBookOverviewRects, visibleCategoryOrder], - ); - const resourceBookSettledPreviewCount = useMemo(() => { - let settled = 0; + // 每个功能画布框内各自判断加载态:该栏目一张预览都没落地时转圈。 + const resourceBookCategorySettledPreviews = useMemo(() => { + const counts = new Map< + ResourceCategory, + { previewable: number; settled: number } + >(); for (const resource of visibleResources) { + const kind = projectResourceCardPreviewKind(resource); + // 版本、占位和音频卡片本来就不请求卡片预览,不能计入加载等待。 + if (kind === 'version' || kind === 'placeholder' || kind === 'audio') { + continue; + } + const entry = counts.get(resource.category) ?? { + previewable: 0, + settled: 0, + }; + entry.previewable += 1; const identity = resourceCardPreviews.identityByResourceId.get( resource.id, ); - if (!identity) continue; - const status = resourceCardPreviews.previews.get(identity)?.status; - if (status === 'loaded' || status === 'failed') settled += 1; + const status = identity + ? resourceCardPreviews.previews.get(identity)?.status + : undefined; + if (status === 'loaded' || status === 'failed') { + entry.settled += 1; + } + counts.set(resource.category, entry); } - return settled; + return counts; }, [ resourceCardPreviews.identityByResourceId, resourceCardPreviews.previews, visibleResources, ]); - const resourceBookLoading = resourceBookIsLoading({ - resourceCount: visibleResources.length, - overviewReady: resourceBookOverviewReady, - cardsReady: sortMode !== 'dependency' || resourceGraphInitializationReady, - settledPreviewCount: resourceBookSettledPreviewCount, - }); const setResourceCanvasViewport = useCallback( ( category: ResourceCategory, @@ -5622,20 +5637,6 @@ export default function ProjectDevelopmentView({ onWheel={handleResourceBookWheel} /> - {resourceBookLoading ? ( -
-
- ) : null}
diff --git a/apps/ai-game-creator-shell/src/view/project-development/resourceBookModel.ts b/apps/ai-game-creator-shell/src/view/project-development/resourceBookModel.ts index ed5af0d13..626e1edaa 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/resourceBookModel.ts +++ b/apps/ai-game-creator-shell/src/view/project-development/resourceBookModel.ts @@ -80,22 +80,15 @@ export function resourceBookShowsChildCards(state: ResourceBookState) { } /** - * 进入资源画布时,卡片布局与首屏预览都要等一会儿,这段时间画布是空的。 - * 只要还有资源、但布局未就绪或首屏预览一个都没落地,就显示加载态。 - * 预览失败也算落地,避免一直转圈。 + * 每个功能画布框内各自显示加载态:该栏目还有资源、但一张预览都没落地时转圈。 + * 预览失败同样算落地,避免一直转圈;加载态不拦截点击,没加载出来也能进栏目。 */ export function resourceBookIsLoading({ resourceCount, - overviewReady, - cardsReady, settledPreviewCount, }: { resourceCount: number; - overviewReady: boolean; - cardsReady: boolean; settledPreviewCount: number; }) { - if (resourceCount <= 0) return false; - if (!cardsReady || !overviewReady) return true; - return settledPreviewCount <= 0; + return resourceCount > 0 && settledPreviewCount <= 0; } diff --git a/apps/ai-game-creator-shell/tests/resourceBookModel.test.ts b/apps/ai-game-creator-shell/tests/resourceBookModel.test.ts index d688ff60d..76563aa0e 100644 --- a/apps/ai-game-creator-shell/tests/resourceBookModel.test.ts +++ b/apps/ai-game-creator-shell/tests/resourceBookModel.test.ts @@ -106,28 +106,21 @@ describe('resource book state machine', () => { }); describe('resource book loading state', () => { - const ready = { - resourceCount: 3, - overviewReady: true, - cardsReady: true, - settledPreviewCount: 1, - }; - - it('never blocks an empty project', () => { - expect(resourceBookIsLoading({ ...ready, resourceCount: 0 })).toBe(false); + it('never spins for an empty section', () => { + expect( + resourceBookIsLoading({ resourceCount: 0, settledPreviewCount: 0 }), + ).toBe(false); }); - it('loads while the layout or first preview is still pending', () => { - expect(resourceBookIsLoading({ ...ready, cardsReady: false })).toBe(true); - expect(resourceBookIsLoading({ ...ready, overviewReady: false })).toBe( - true, - ); - expect(resourceBookIsLoading({ ...ready, settledPreviewCount: 0 })).toBe( - true, - ); + it('spins while a section still has no settled preview', () => { + expect( + resourceBookIsLoading({ resourceCount: 3, settledPreviewCount: 0 }), + ).toBe(true); }); - it('stops once a first preview settles', () => { - expect(resourceBookIsLoading(ready)).toBe(false); + it('stops as soon as one preview in that section settles', () => { + expect( + resourceBookIsLoading({ resourceCount: 3, settledPreviewCount: 1 }), + ).toBe(false); }); });