From 1cc9a2381589cfe7c15a0560662b550ddba69b8c Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Fri, 11 Sep 2026 11:21:03 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B5=84=E6=BA=90=E6=80=BB=E8=A7=88=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=8C=E6=89=80=E6=9C=89=E8=B5=84=E6=BA=90=E3=80=8D?= =?UTF-8?q?=E6=B1=87=E6=80=BB=E5=8D=A1=E4=B8=8E=E5=85=A8=E9=83=A8=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 渲染层新增 `RESOURCE_BOOK_ALL_TARGET = 'all'` 特殊项:跨端契约 `ProjectResourceCanvasCategory` 不加 `'all'`,sidecar schema 不升版,落盘布局的 `section` 也不会出现它 - 资源总览第 0 张卡即「所有资源」:排在全部栏目卡之前,复用同一套缩略图类名/圆角/底色/阴影与栏目标题栏组件,不新增卡片样式 - 该卡的计数徽标取全量资源数,口径等于各栏目计数之和(同一份画布投影,不另算一套) - 画本场景的栏目顺序与资源分组各多一个渲染层入口;`buildResourceBookScenePlan` 对特殊项只出计数与标题栏,不铺卡片,避免同一张资源卡在总览里挂载两份(`data-resource-card-id` 查询与 FLIP 的 `card:*` 键都会撞车) - 新增「所有资源」页:栏目大纲 + 收起资源按钮 + 按栏目分组的滚动卡片网格;卡片复用栏目页同一颗渲染器与卡宽高,容器与网格样式补齐在 styles.css - 该页的卡片显式关掉拖拽(卡片坐标不属于任何栏目画布,写回会污染布局 sidecar),保留选中/播放/菜单交互,并把光标与 `touch-action: pan-y` 交给滚动容器 - 栏目大纲抽成 `ResourceBookOutlineNav`,栏目页与全部资源页共用同一条入口带;渲染出的 DOM 与抽取前一致 - `renderResourceBookCard` 增加可选 `draggable` 参数,`ResourceCard` 的三个移动端指针回调改为可选 - 断言:总览存在「所有资源」卡且为第 0 张、计数等于全量资源数且等于各栏目计数之和、点它进入的页面按栏目分组且每组都挂真实资源卡、场景计划里特殊项不出卡片且整份计划无重复卡片键、全部资源页分组只保留非空栏目且保持总览顺序 - 既有「N 项」徽标断言从全文档文本查找收窄到 character 栏自己的标题栏(新增第 0 张卡后同文案会撞车,收窄而非放宽) --- apps/ai-game-creator-shell/src/styles.css | 59 +++ .../src/view/project-development/index.tsx | 375 ++++++++++++++---- .../project-development/resourceBookLayout.ts | 48 ++- .../project-development/resourceBookModel.ts | 32 +- .../appSurface/project-development.suite.ts | 208 +++++++++- .../tests/resourceBookLayout.test.ts | 99 ++++- .../tests/resourceBookModel.test.ts | 53 +++ 7 files changed, 791 insertions(+), 83 deletions(-) diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css index 8bc7b0bf7..18b025651 100644 --- a/apps/ai-game-creator-shell/src/styles.css +++ b/apps/ai-game-creator-shell/src/styles.css @@ -6650,6 +6650,65 @@ iframe.preview-frame { font-size: 12px; } +/* + * 「所有资源」页。 + * + * 资源总览第 0 张卡打开的汇总页:左侧沿用栏目大纲,正文按栏目分组的滚动卡片网格。 + * 整页自成一层(`z-index: 21`):栏目态的画本场景自带一层不透明底纹(`z-index: 20`), + * 只在 `z-index: auto` 上铺会被它盖住;同时它不落在 `.game-resource-canvas` 里, + * 那层挂着画布平移/框选的指针与滚轮处理器,会把这里的滚动吃掉。 + * + * 卡片本体仍由栏目页同一颗卡片渲染器产出(尺寸取画布默认卡宽高), + * 因此这一页只补齐容器与网格,不新增卡片样式。 + */ +.game-resource-all-resources-host { + position: absolute; + z-index: 21; + inset: 0; + padding: 12px; + overflow: hidden; + background-color: #fffdfa; + background-image: radial-gradient(#eaded8 0.8px, transparent 0.8px); + background-size: 18px 18px; +} + +.game-resource-all-scroll { + display: grid; + gap: 14px; + align-content: start; + min-height: 0; + padding: 2px; + overflow: auto; + overscroll-behavior: contain; +} + +.game-resource-all-section { + display: grid; + gap: 10px; + padding: 12px; + border: 1px solid #ecdfd8; + border-radius: 12px; + background: rgb(255 253 250 / 82%); +} + +.game-resource-all-grid { + display: grid; + grid-template-columns: repeat(auto-fill, 180px); + gap: 14px; +} + +.game-resource-all-card { + position: relative; + display: block; +} + +/* 只浏览的页面不接拖拽:光标如实反映「点选 / 播放」,并且把纵向触摸手势交还给滚动容器 + (`.game-resource-card` 常态是 `touch-action: none`,那是画布拖卡需要的)。 */ +.game-resource-all-grid .game-resource-card { + cursor: pointer; + touch-action: pan-y; +} + .game-resource-section-stack { display: contents; } 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 d5c7766a5..e5ea3cdca 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 @@ -22,6 +22,7 @@ import { Gamepad2, Image, Info, + Layers, LayoutGrid, ListFilter, Maximize2, @@ -149,7 +150,10 @@ import { type ResourceBookTransitionController, } from './resourceBookController'; import { + buildResourceBookAllResourcesSections, buildResourceBookScenePlan, + RESOURCE_BOOK_OVERVIEW_STACK_LIMIT, + type ResourceBookAllResourcesSection, type ResourceBookOverviewRect, resourceBookSceneCardKey, resourceBookSceneCardLayoutSignature, @@ -159,8 +163,12 @@ import { } from './resourceBookLayout'; import { initialResourceBookState, + isResourceBookAllTarget, + RESOURCE_BOOK_ALL_TARGET, + type ResourceBookCategory, resourceBookReducer, type ResourceBookState, + type ResourceBookTarget, } from './resourceBookModel'; import { DEFAULT_RESOURCE_BOOK_VIEWPORT, @@ -456,8 +464,11 @@ const categoryOrder = RESOURCE_CANVAS_SECTION_ORDER; /** * 栏目标签复用资源筛选的唯一中文口径;「项目版本」不是资源资产、单独成栏, * 不在筛选口径内,因此只有它在这里给出文案。 + * + * `all` 是渲染层的「所有资源」入口(见 `RESOURCE_BOOK_ALL_TARGET`),不是真实栏目, + * 因此只在这里补文案与图标,不进入跨端契约的分区枚举。 */ -const categoryLabels: Record = { +const categoryLabels: Record = { 'ui-interaction': resourceReferenceCategoryLabel('ui-interaction'), character: resourceReferenceCategoryLabel('character'), scene: resourceReferenceCategoryLabel('scene'), @@ -465,9 +476,10 @@ const categoryLabels: Record = { document: resourceReferenceCategoryLabel('document'), unclassified: resourceReferenceCategoryLabel('unclassified'), version: '项目版本', + [RESOURCE_BOOK_ALL_TARGET]: '所有资源', }; -const categoryIcons: Record = { +const categoryIcons: Record = { 'ui-interaction': LayoutGrid, character: Users, scene: Image, @@ -475,6 +487,7 @@ const categoryIcons: Record = { document: FileText, unclassified: PackageOpen, version: Gamepad2, + [RESOURCE_BOOK_ALL_TARGET]: Layers, }; const approvalOptions: Array<{ @@ -591,9 +604,10 @@ const ResourceCard = memo(function ResourceCard({ x: number, y: number, ) => void; - onPointerMove: (event: ReactPointerEvent) => void; - onPointerUp: (event: ReactPointerEvent) => void; - onPointerCancel: (event: ReactPointerEvent) => void; + /** 只浏览的页面(「所有资源」)不接拖拽,三个移动端回调可以整体缺省。 */ + onPointerMove?: (event: ReactPointerEvent) => void; + onPointerUp?: (event: ReactPointerEvent) => void; + onPointerCancel?: (event: ReactPointerEvent) => void; onObservePreview: ( element: HTMLElement, resource: ProjectResource, @@ -909,11 +923,11 @@ function ResourceBookThumbnail({ onOpen, registerElement, }: { - category: ResourceCategory; + category: ResourceBookTarget; resources: ProjectResource[]; - onOpen: (category: ResourceCategory) => void; + onOpen: (category: ResourceBookTarget) => void; registerElement?: ( - category: ResourceCategory, + category: ResourceBookTarget, element: HTMLElement | null, ) => void; }) { @@ -926,6 +940,11 @@ function ResourceBookThumbnail({ return groups; }, new Map()), ); + // 「所有资源」卡的正文只保留同一套占位摞结构:全量资源的张数由标题栏计数徽标表达, + // 卡片本体不进这一格(同一张资源卡已经在它所属栏目的摞里挂载过)。 + const bodyStacks = isResourceBookAllTarget(category) + ? stacks.slice(0, RESOURCE_BOOK_OVERVIEW_STACK_LIMIT) + : stacks; return (