{ "generatedAt": "2026-09-06T08:34:35.367Z", "overall": "PASS", "featureCount": { "total": 103, "pass": 103, "fail": 0, "pending": 0 }, "browserCount": { "total": 19, "pass": 19, "fail": 0, "pending": 0 }, "finalRun": true, "finalArtifactsVerified": true, "features": [ { "id": "project.new", "group": "工程与数据", "name": "新建工程", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyModelEditorView.vue", "line": 118, "symbol": "createNewProject" }, "implementation": "本轮修复", "boundary": "宿主创建新项目 ID 并跳转;新建不是清空并覆盖当前工程。", "fix": "新建复用正式 create API;创建失败或取消保留原工程。", "acceptance": "新建前后 ID 不同,原工程内容/版本不被覆盖;取消不发创建请求。", "verification": "PASS", "evidence": [ { "ids": [ "project.new" ], "file": "evidence/new-project.json", "status": "PASS", "testTitle": "新建工程取消及失败保留输入;成功创建新ID且不覆盖当前模型", "recordedAt": "2026-09-06T08:22:24.723Z", "kind": "e2e+api", "title": "新建工程取消及失败保留输入;成功创建新ID且不覆盖当前模型", "screenshots": [], "details": "" } ] }, { "id": "project.import-glb", "group": "工程与数据", "name": "导入装备 GLB", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1264, "symbol": "importModelFile" }, "implementation": "已有实现", "boundary": "只支持单文件 GLB;上传→登记→解析→最终保存。", "fix": "延续导入 busy/失败阻断及稳定资产校验。", "acceptance": "选择轻量 GLB,真实模型可见;刷新仍加载同一资产。", "verification": "PASS", "evidence": [ { "ids": [ "project.import-glb", "environment.external", "project.import-json", "project.export-json", "project.save" ], "file": "evidence/30-assets-roundtrip.json", "status": "PASS", "testTitle": "受控模型与外部环境 GLB 上传、JSON 往返和刷新重开", "recordedAt": "2026-09-06T08:20:27.495Z", "kind": "e2e+api", "title": "受控模型与外部环境 GLB 上传、JSON 往返和刷新重开", "screenshots": [], "details": "真实上传、注册、保存、JSON重导入、刷新后的实际网格和原生动画目录;未断言下游场景执行动画。" } ] }, { "id": "project.import-json", "group": "工程与数据", "name": "导入工程 JSON", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2863, "symbol": "importProjectFile" }, "implementation": "本轮修复", "boundary": "导入到当前工程,不创建副本;受控引用必须属于当前可用资产。JSON 不含 GLB 字节。", "fix": "导入前 materialize 稳定资产引用。", "acceptance": "同项目导出后再导入,材质/动画/环境不丢;无效或跨项目缺失引用报错。", "verification": "PASS", "evidence": [ { "ids": [ "project.import-glb", "environment.external", "project.import-json", "project.export-json", "project.save" ], "file": "evidence/30-assets-roundtrip.json", "status": "PASS", "testTitle": "受控模型与外部环境 GLB 上传、JSON 往返和刷新重开", "recordedAt": "2026-09-06T08:20:27.495Z", "kind": "e2e+api", "title": "受控模型与外部环境 GLB 上传、JSON 往返和刷新重开", "screenshots": [], "details": "真实上传、注册、保存、JSON重导入、刷新后的实际网格和原生动画目录;未断言下游场景执行动画。" }, { "ids": [ "project.import-json" ], "file": "evidence/31-invalid-import.json", "status": "PASS", "testTitle": "非法 JSON 与未登记 blob 引用导入失败时保留当前文档", "recordedAt": "2026-09-06T08:20:33.535Z", "kind": "e2e+api", "title": "非法 JSON 与未登记 blob 引用导入失败时保留当前文档", "screenshots": [], "details": "" } ] }, { "id": "project.save", "group": "工程与数据", "name": "手动保存 / Ctrl+S", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2748, "symbol": "saveProject" }, "implementation": "已有实现", "boundary": "同一工程保存内容和当前视口封面;成功以 API 响应为准。", "fix": "保留队列、版本保护和封面失败回退。", "acceptance": "保存真实 PUT 成功,刷新字段不丢;重复点击不交错。", "verification": "PASS", "evidence": [ { "ids": [ "project.import-glb", "environment.external", "project.import-json", "project.export-json", "project.save" ], "file": "evidence/30-assets-roundtrip.json", "status": "PASS", "testTitle": "受控模型与外部环境 GLB 上传、JSON 往返和刷新重开", "recordedAt": "2026-09-06T08:20:27.495Z", "kind": "e2e+api", "title": "受控模型与外部环境 GLB 上传、JSON 往返和刷新重开", "screenshots": [], "details": "真实上传、注册、保存、JSON重导入、刷新后的实际网格和原生动画目录;未断言下游场景执行动画。" }, { "ids": [ "project.conflict", "project.save", "feedback.toast" ], "file": "evidence/32-save-failure-recovery.json", "status": "PASS", "testTitle": "保存请求超时和 409 保留恢复副本,解除故障后可重试", "recordedAt": "2026-09-06T08:20:43.214Z", "kind": "e2e+api", "title": "保存请求超时和 409 保留恢复副本,解除故障后可重试", "screenshots": [], "details": "真实页面保存及真实服务端重试;失败由浏览器定向注入 timedout/409,非宣称自然发生的服务故障。" }, { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" } ] }, { "id": "project.autosave", "group": "工程与数据", "name": "30 秒自动保存", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2122, "symbol": "bindShellEvents" }, "implementation": "已有实现", "boundary": "自动保存工程内容;不反复截图更新封面,恢复稿待确认期间暂停。", "fix": "导入/封面保存期间阻断重入。", "acceptance": "修改后等待自动保存并刷新;忙碌时不提交半成品。", "verification": "PASS", "evidence": [ { "ids": [ "project.name", "project.autosave", "view.orbit", "view.focus", "view.panels", "view.preview-step", "feedback.log", "feedback.stats", "property.identity", "property.binding", "selection.scope", "component.inspect" ], "file": "evidence/46-navigation-panels-autosave.json", "status": "PASS", "testTitle": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "recordedAt": "2026-09-06T08:23:54.656Z", "kind": "e2e+api", "title": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "screenshots": [], "details": "真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。" } ] }, { "id": "project.name", "group": "工程与数据", "name": "修改工程名", "source": { "file": "../../../unreal_tran/src/main.js", "line": 457, "symbol": "projectNameInput" }, "implementation": "已有实现", "boundary": "输入→失焦/Enter→保存;当前模型工程名。", "fix": "无本轮专门改动。", "acceptance": "改名保存后列表和重新打开一致。", "verification": "PASS", "evidence": [ { "ids": [ "project.name", "project.autosave", "view.orbit", "view.focus", "view.panels", "view.preview-step", "feedback.log", "feedback.stats", "property.identity", "property.binding", "selection.scope", "component.inspect" ], "file": "evidence/46-navigation-panels-autosave.json", "status": "PASS", "testTitle": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "recordedAt": "2026-09-06T08:23:54.656Z", "kind": "e2e+api", "title": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "screenshots": [], "details": "真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。" } ] }, { "id": "project.export-json", "group": "工程与数据", "name": "另存项目 JSON", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2883, "symbol": "saveProjectAs" }, "implementation": "已有实现", "boundary": "下载工程 JSON;不是另建服务端项目,也不内嵌 GLB 文件。", "fix": "稳定资产引用保留,临时 blob 地址清除。", "acceptance": "下载文件可解析,包含轨道/蓝图/自定义字段,配套资源可重新解析。", "verification": "PASS", "evidence": [ { "ids": [ "project.import-glb", "environment.external", "project.import-json", "project.export-json", "project.save" ], "file": "evidence/30-assets-roundtrip.json", "status": "PASS", "testTitle": "受控模型与外部环境 GLB 上传、JSON 往返和刷新重开", "recordedAt": "2026-09-06T08:20:27.495Z", "kind": "e2e+api", "title": "受控模型与外部环境 GLB 上传、JSON 往返和刷新重开", "screenshots": [], "details": "真实上传、注册、保存、JSON重导入、刷新后的实际网格和原生动画目录;未断言下游场景执行动画。" } ] }, { "id": "project.undo", "group": "工程与数据", "name": "撤销 / Ctrl+Z", "source": { "file": "../../../unreal_tran/src/main.js", "line": 160, "symbol": "class HistoryStack" }, "implementation": "本轮修复", "boundary": "最多 60 个历史快照;不是数据库版本回滚。", "fix": "异步互斥,失败不移动索引,托管 GLB 快照重新解析。", "acceptance": "属性/曲线修改撤销;连续点击不并发;下载失败索引与修改保留。", "verification": "PASS", "evidence": [ { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" }, { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" }, { "ids": [ "project.undo", "project.redo" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "history retains 60 states, truncates redo after edits and ignores duplicate snapshots", "screenshots": [], "details": "60 个快照上限、重复状态忽略及新编辑截断 redo 分支。" }, { "ids": [ "project.undo", "project.redo" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "history serializes async restores and leaves index unchanged when restoration fails", "screenshots": [], "details": "异步互斥、下载失败不移动索引,解除锁后可继续操作。" }, { "ids": [ "project.undo", "project.redo" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "managed-GLB history downloads the snapshot stable reference before restoring, without rewriting it", "screenshots": [], "details": "受控 GLB 快照先物化再恢复,不把临时 URL 写回快照。" } ] }, { "id": "project.redo", "group": "工程与数据", "name": "重做 / Ctrl+Y", "source": { "file": "../../../unreal_tran/src/main.js", "line": 160, "symbol": "class HistoryStack" }, "implementation": "本轮修复", "boundary": "撤销后重做;产生新编辑时清掉旧 redo 分支。", "fix": "与撤销共用异步恢复保护。", "acceptance": "重做恢复相同字段及姿态,不回放无效分支。", "verification": "PASS", "evidence": [ { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" }, { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" }, { "ids": [ "project.undo", "project.redo" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "history retains 60 states, truncates redo after edits and ignores duplicate snapshots", "screenshots": [], "details": "60 个快照上限、重复状态忽略及新编辑截断 redo 分支。" }, { "ids": [ "project.undo", "project.redo" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "history serializes async restores and leaves index unchanged when restoration fails", "screenshots": [], "details": "异步互斥、下载失败不移动索引,解除锁后可继续操作。" }, { "ids": [ "project.undo", "project.redo" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "managed-GLB history downloads the snapshot stable reference before restoring, without rewriting it", "screenshots": [], "details": "受控 GLB 快照先物化再恢复,不把临时 URL 写回快照。" } ] }, { "id": "project.recovery", "group": "工程与数据", "name": "异常恢复副本", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2554, "symbol": "reconcileApiRecovery" }, "implementation": "本轮修复", "boundary": "同版本恢复稿需用户确认;旧版本隔离/下载,不静默覆盖服务器。", "fix": "恢复稿重新解析受控资源;指纹忽略对象键顺序,避免已保存页面刷新误生恢复稿。", "acceptance": "同版本恢复可见;旧版本保留/下载;主动保存前不自动 PUT。", "verification": "PASS", "evidence": [ { "ids": [ "project.recovery" ], "file": "evidence/60-project-recovery.json", "status": "PASS", "testTitle": "真实未保存稿同版本恢复并显式保存;旧版本恢复稿下载隔离保留", "recordedAt": "2026-09-06T08:24:07.184Z", "kind": "e2e+api", "title": "真实未保存稿同版本恢复并显式保存;旧版本恢复稿下载隔离保留", "screenshots": [], "details": "通过真实beforeunload创建未保存稿;同版恢复后仅显式保存落库;旧版只下载隔离,不自动覆盖服务端。丢弃/跨标签竞态另由既有恢复合同测试覆盖。" }, { "ids": [ "project.recovery" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "beforeunload recovery does not create a draft for key order alone, but preserves real unsaved changes", "screenshots": [], "details": "同语义对象键顺序不产生假恢复稿;真正修改仍保留。不包含全部恢复/隔离/下载 UI 分支。" } ] }, { "id": "project.conflict", "group": "工程与数据", "name": "保存/发布版本冲突", "source": { "file": "../../../unreal_tran/src/model-content-api.js", "line": 1025, "symbol": "queueSave" }, "implementation": "已有实现", "boundary": "409 阻断;恢复稿保留。", "fix": "发布封面队列同样遵守冲突保护。", "acceptance": "定向模拟 409,无后续发布请求,不误报成功。", "verification": "PASS", "evidence": [ { "ids": [ "project.conflict", "project.save", "feedback.toast" ], "file": "evidence/32-save-failure-recovery.json", "status": "PASS", "testTitle": "保存请求超时和 409 保留恢复副本,解除故障后可重试", "recordedAt": "2026-09-06T08:20:43.214Z", "kind": "e2e+api", "title": "保存请求超时和 409 保留恢复副本,解除故障后可重试", "screenshots": [], "details": "真实页面保存及真实服务端重试;失败由浏览器定向注入 timedout/409,非宣称自然发生的服务故障。" }, { "ids": [ "project.conflict", "publish.api", "publish.cover" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "API conflict leaves publication failed and releases the cover guard", "screenshots": [], "details": "409 分支不误报已发布并释放 busy 标记;模拟 API。" } ] }, { "id": "project.permissions", "group": "工程与数据", "name": "查看/编辑/新增/发布权限", "source": { "file": "../../../unreal_tran_web/src/features/editors/model/legacy-iframe-permissions.ts", "line": 32, "symbol": "installLegacyModelPermissions" }, "implementation": "本轮修复", "boundary": "按 content.model.* 权限,不按 teacher/admin 身份推断写权限;只读仍能搜索、选择原生动画和键盘激活预览。", "fix": "新建接真实 create 能力;只发布可发布已保存版本;修复查看控件键盘事件误拦截。", "acceptance": "只读查看操作可用且无内容写请求;不可编辑/保存;publisher 不上传/PUT,正常发布已有版本。", "verification": "PASS", "evidence": [ { "ids": [ "project.permissions" ], "file": "evidence/permissions.json", "status": "PASS", "testTitle": "管理员只读权限:编辑、新建、导入、保存发布均不可越权", "recordedAt": "2026-09-06T08:22:29.489Z", "kind": "e2e+api", "title": "管理员只读权限:编辑、新建、导入、保存发布均不可越权", "screenshots": [], "details": "管理员只读UI、真实键盘搜索、原生片段切换与Enter播放/暂停、预览无内容写请求及创建403;纯publisher无需update的发布流程由独立权限合同回归补充。" } ] }, { "id": "project.leave", "group": "工程与数据", "name": "离开/切角色未保存确认", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyModelEditorView.vue", "line": 147, "symbol": "confirmLeave" }, "implementation": "已有实现", "boundary": "取消留在当前页;保存失败不得离开;上传/保存/发布忙时保留状态。", "fix": "新建也复用现有未保存确认。", "acceptance": "取消、放弃、保存失败与 busy 分别验证,没有误清 dirty。", "verification": "PASS", "evidence": [ { "ids": [ "environment.builtin", "tree.browse", "tree.expand", "project.leave" ], "file": "evidence/43-builtin-environments-tree.json", "status": "PASS", "testTitle": "补充环境与树:内置三环境保存重开、节点计数与折叠展开", "recordedAt": "2026-09-06T08:23:19.759Z", "kind": "e2e+api", "title": "补充环境与树:内置三环境保存重开、节点计数与折叠展开", "screenshots": [], "details": "真实切换、保存和刷新三种环境;树计数与展开折叠;装备车库保存后返回列表不误报未保存。树220行上限仍属已说明边界。" }, { "ids": [ "project.leave" ], "file": "evidence/52-leave.json", "status": "PASS", "testTitle": "未保存离开:关闭确认保留工作、保存失败留在原页、重试保存后离开", "recordedAt": "2026-09-06T08:22:48.155Z", "kind": "e2e+api", "title": "未保存离开:关闭确认保留工作、保存失败留在原页、重试保存后离开", "screenshots": [], "details": "" } ] }, { "id": "resource.catalog", "group": "资源库与场景树", "name": "资源库浏览 / 选择装备", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1383, "symbol": "renderAssets" }, "implementation": "本轮修复", "boundary": "切换装备可能应用其基准时间轴/蓝图;不是把第二个主装备追加进当前模型。", "fix": "修复单节点选择器误用导致启动报错;忙碌期间禁止切换资源。", "acceptance": "完整资源库 DOM 初始化、卡片事件、空目录启动正常;切换实际几何可见,保存后资源一致。", "verification": "PASS", "evidence": [ { "ids": [ "resource.search", "resource.catalog" ], "file": "evidence/41-resource-search-switch.json", "status": "PASS", "testTitle": "补充资源库:搜索清空、真实卡片切换与刷新保留", "recordedAt": "2026-09-06T08:22:56.960Z", "kind": "e2e+api", "title": "补充资源库:搜索清空、真实卡片切换与刷新保留", "screenshots": [], "details": "真实搜索与资源卡片点击、服务端保存、刷新后原模型网格;副本独立登记待清理。" }, { "ids": [ "resource.catalog" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "resource catalog startup renders all model/environment cards and binds every action with a real DOM", "screenshots": [], "details": "真实 DOM 渲染、卡片绑定、空目录及 busy 保护;不包含 GLB 网络加载。" } ] }, { "id": "resource.search", "group": "资源库与场景树", "name": "资源搜索", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1467, "symbol": "applyAssetSearch" }, "implementation": "已有实现", "boundary": "按资源名称/关键词过滤现有目录,不搜索服务器全站。", "fix": "无本轮专门改动。", "acceptance": "关键字命中、空结果与清空恢复。", "verification": "PASS", "evidence": [ { "ids": [ "resource.search", "resource.catalog" ], "file": "evidence/41-resource-search-switch.json", "status": "PASS", "testTitle": "补充资源库:搜索清空、真实卡片切换与刷新保留", "recordedAt": "2026-09-06T08:22:56.960Z", "kind": "e2e+api", "title": "补充资源库:搜索清空、真实卡片切换与刷新保留", "screenshots": [], "details": "真实搜索与资源卡片点击、服务端保存、刷新后原模型网格;副本独立登记待清理。" } ] }, { "id": "environment.builtin", "group": "资源库与场景树", "name": "内置车间 / 野外 / 装备车库", "source": { "file": "../../../unreal_tran/src/main.js", "line": 789, "symbol": "resetEnvironmentPlaceholder" }, "implementation": "已有实现", "boundary": "车间/野外为程序化环境;车库为随包 GLB。", "fix": "保留真实 GLB 和占位环境来源区别。", "acceptance": "三种切换与重新打开可重现相应环境。", "verification": "PASS", "evidence": [ { "ids": [ "environment.builtin", "tree.browse", "tree.expand", "project.leave" ], "file": "evidence/43-builtin-environments-tree.json", "status": "PASS", "testTitle": "补充环境与树:内置三环境保存重开、节点计数与折叠展开", "recordedAt": "2026-09-06T08:23:19.759Z", "kind": "e2e+api", "title": "补充环境与树:内置三环境保存重开、节点计数与折叠展开", "screenshots": [], "details": "真实切换、保存和刷新三种环境;树计数与展开折叠;装备车库保存后返回列表不误报未保存。树220行上限仍属已说明边界。" } ] }, { "id": "environment.external", "group": "资源库与场景树", "name": "导入外部车间 / 野外 GLB", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1028, "symbol": "loadEnvironmentSceneFile" }, "implementation": "本轮修复", "boundary": "一个环境根;受控 MODEL_FILE 资产 metadata.usage=environment,与主装备独立。", "fix": "上传、最终保存、JSON 与刷新恢复稳定引用;失败阻断半提交。", "acceptance": "小 GLB 上传后主装备不被替换;环境刷新仍可见且资产引用一致。", "verification": "PASS", "evidence": [ { "ids": [ "project.import-glb", "environment.external", "project.import-json", "project.export-json", "project.save" ], "file": "evidence/30-assets-roundtrip.json", "status": "PASS", "testTitle": "受控模型与外部环境 GLB 上传、JSON 往返和刷新重开", "recordedAt": "2026-09-06T08:20:27.495Z", "kind": "e2e+api", "title": "受控模型与外部环境 GLB 上传、JSON 往返和刷新重开", "screenshots": [], "details": "真实上传、注册、保存、JSON重导入、刷新后的实际网格和原生动画目录;未断言下游场景执行动画。" } ] }, { "id": "resource.loading", "group": "资源库与场景树", "name": "模型加载进度 / 失败 / 重试", "source": { "file": "../../../unreal_tran_web/src/views/content/LegacyModelEditorView.vue", "line": 33, "symbol": "installLegacyModelIframeLoadingScope" }, "implementation": "已有实现", "boundary": "局部加载状态;错误短提示,中性失败状态可重试。", "fix": "复用前轮加载范围与错误消息修复。", "acceptance": "资源失败不无限转圈;重试恢复;无凭据泄露。", "verification": "PASS", "evidence": [ { "ids": [ "resource.loading", "publish.preflight" ], "file": "evidence/63-loading-publish-preflight.json", "status": "PASS", "testTitle": "受控 GLB 下载失败阻止编辑,重试成功;发布预检开关无写入且拒绝错误资源 JSON", "recordedAt": "2026-09-06T08:24:17.025Z", "kind": "e2e+api", "title": "受控 GLB 下载失败阻止编辑,重试成功;发布预检开关无写入且拒绝错误资源 JSON", "screenshots": [], "details": "真实受控下载定向503→中性失败与禁止编辑→解除故障后UI重试成功;预检面板开关不写项目,错误资源JSON在替换当前模型之前被拒绝。服务端发布时code/URI一致性拒绝由ContentProjectServiceTest合同回归覆盖,未把面板绿标代替服务端校验。" } ] }, { "id": "tree.browse", "group": "资源库与场景树", "name": "场景树节点与计数", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1925, "symbol": "buildSceneTree" }, "implementation": "现有边界", "boundary": "树行上限 220;统计节点总数不等于当前展开行数。", "fix": "无本轮容量重构。", "acceptance": "模型/环境节点可达,计数说明不当作全部节点已经逐项验收。", "verification": "PASS", "evidence": [ { "ids": [ "environment.builtin", "tree.browse", "tree.expand", "project.leave" ], "file": "evidence/43-builtin-environments-tree.json", "status": "PASS", "testTitle": "补充环境与树:内置三环境保存重开、节点计数与折叠展开", "recordedAt": "2026-09-06T08:23:19.759Z", "kind": "e2e+api", "title": "补充环境与树:内置三环境保存重开、节点计数与折叠展开", "screenshots": [], "details": "真实切换、保存和刷新三种环境;树计数与展开折叠;装备车库保存后返回列表不误报未保存。树220行上限仍属已说明边界。" } ] }, { "id": "tree.expand", "group": "资源库与场景树", "name": "节点展开 / 折叠", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1925, "symbol": "buildSceneTree" }, "implementation": "已有实现", "boundary": "当前界面浏览状态;无须持久化展开布局。", "fix": "无本轮专门改动。", "acceptance": "展开和折叠子级不修改模型对象。", "verification": "PASS", "evidence": [ { "ids": [ "environment.builtin", "tree.browse", "tree.expand", "project.leave" ], "file": "evidence/43-builtin-environments-tree.json", "status": "PASS", "testTitle": "补充环境与树:内置三环境保存重开、节点计数与折叠展开", "recordedAt": "2026-09-06T08:23:19.759Z", "kind": "e2e+api", "title": "补充环境与树:内置三环境保存重开、节点计数与折叠展开", "screenshots": [], "details": "真实切换、保存和刷新三种环境;树计数与展开折叠;装备车库保存后返回列表不误报未保存。树220行上限仍属已说明边界。" } ] }, { "id": "tree.search", "group": "资源库与场景树", "name": "场景树搜索", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1925, "symbol": "buildSceneTree" }, "implementation": "已有实现", "boundary": "搜索当前载入对象,不新增/删除对象。", "fix": "无本轮专门改动。", "acceptance": "搜索节点后选中正确对象,清空恢复树。", "verification": "PASS", "evidence": [ { "ids": [ "tree.search", "tree.visibility", "selection.sync", "view.grid", "view.pbr", "view.wireframe", "view.outline", "view.perspective", "view.preview", "view.snapshot", "timeline.native" ], "file": "evidence/viewport.json", "status": "PASS", "testTitle": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "recordedAt": "2026-09-06T08:22:14.169Z", "kind": "e2e", "title": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "screenshots": [], "details": "树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。" } ] }, { "id": "tree.visibility", "group": "资源库与场景树", "name": "显示 / 隐藏对象", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1925, "symbol": "buildSceneTree" }, "implementation": "本轮修复", "boundary": "对象 visible 保存;隐藏祖先的子对象不接受射线拾取。", "fix": "拾取与高亮统一检查祖先可见性。", "acceptance": "隐藏父级后不能点到子级,保存重开仍隐藏。", "verification": "PASS", "evidence": [ { "ids": [ "tree.search", "tree.visibility", "selection.sync", "view.grid", "view.pbr", "view.wireframe", "view.outline", "view.perspective", "view.preview", "view.snapshot", "timeline.native" ], "file": "evidence/viewport.json", "status": "PASS", "testTitle": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "recordedAt": "2026-09-06T08:22:14.169Z", "kind": "e2e", "title": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "screenshots": [], "details": "树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。" }, { "ids": [ "tree.visibility", "blueprint.highlight" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "visible child under a hidden ancestor cannot be picked or highlighted; trigger aliases include ancestors", "screenshots": [], "details": "共用可见性和祖先别名函数;鼠标事件另由 E2E 验证。" } ] }, { "id": "selection.scope", "group": "资源库与场景树", "name": "整体 / 部件级选择", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1938, "symbol": "setSelectionScope" }, "implementation": "已有实现", "boundary": "整体作用主装备根;部件作用实际节点。", "fix": "保留当前选择层级合同。", "acceptance": "点击可见 mesh,整体/部件切换选择目标不同。", "verification": "PASS", "evidence": [ { "ids": [ "project.name", "project.autosave", "view.orbit", "view.focus", "view.panels", "view.preview-step", "feedback.log", "feedback.stats", "property.identity", "property.binding", "selection.scope", "component.inspect" ], "file": "evidence/46-navigation-panels-autosave.json", "status": "PASS", "testTitle": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "recordedAt": "2026-09-06T08:23:54.656Z", "kind": "e2e+api", "title": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "screenshots": [], "details": "真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。" } ] }, { "id": "selection.sync", "group": "资源库与场景树", "name": "树 / 视口 / 属性选择联动", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1947, "symbol": "selectObject" }, "implementation": "本轮修复", "boundary": "显示实际对象并更新辅助框;环境也是可编辑对象。", "fix": "关闭轮廓后重选不恢复辅助框。", "acceptance": "三处名称/对象 ID 一致,重选后轮廓状态保持。", "verification": "PASS", "evidence": [ { "ids": [ "tree.search", "tree.visibility", "selection.sync", "view.grid", "view.pbr", "view.wireframe", "view.outline", "view.perspective", "view.preview", "view.snapshot", "timeline.native" ], "file": "evidence/viewport.json", "status": "PASS", "testTitle": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "recordedAt": "2026-09-06T08:22:14.169Z", "kind": "e2e", "title": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "screenshots": [], "details": "树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。" } ] }, { "id": "view.orbit", "group": "三维视图与操作", "name": "鼠标旋转 / 平移 / 缩放视角", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2, "symbol": "OrbitControls" }, "implementation": "已有实现", "boundary": "当前三维相机;不是模型变换。", "fix": "无本轮专门改动。", "acceptance": "鼠标操作改变镜头但不改变对象局部坐标。", "verification": "PASS", "evidence": [ { "ids": [ "project.name", "project.autosave", "view.orbit", "view.focus", "view.panels", "view.preview-step", "feedback.log", "feedback.stats", "property.identity", "property.binding", "selection.scope", "component.inspect" ], "file": "evidence/46-navigation-panels-autosave.json", "status": "PASS", "testTitle": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "recordedAt": "2026-09-06T08:23:54.656Z", "kind": "e2e+api", "title": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "screenshots": [], "details": "真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。" } ] }, { "id": "view.focus", "group": "三维视图与操作", "name": "聚焦选中 / F", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2272, "symbol": "fitCamera" }, "implementation": "已有实现", "boundary": "按当前对象包围盒定位相机。", "fix": "无本轮专门改动。", "acceptance": "小模型/部件聚焦后仍可见,不被裁剪。", "verification": "PASS", "evidence": [ { "ids": [ "project.name", "project.autosave", "view.orbit", "view.focus", "view.panels", "view.preview-step", "feedback.log", "feedback.stats", "property.identity", "property.binding", "selection.scope", "component.inspect" ], "file": "evidence/46-navigation-panels-autosave.json", "status": "PASS", "testTitle": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "recordedAt": "2026-09-06T08:23:54.656Z", "kind": "e2e+api", "title": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "screenshots": [], "details": "真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。" } ] }, { "id": "view.move", "group": "三维视图与操作", "name": "移动工具 / W", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2234, "symbol": "setTransformMode" }, "implementation": "本轮修复", "boundary": "拖拽编辑对象局部位置,保存后重开一致。", "fix": "动画姿态下更新基值只扣实际偏移。", "acceptance": "移动后保存、刷新及动画再播放不漂移。", "verification": "PASS", "evidence": [ { "ids": [ "view.move", "view.rotate", "view.scale" ], "file": "evidence/51-gizmos.json", "status": "PASS", "testTitle": "真实三维移动、旋转、缩放手柄拖动及撤销重做保存重开", "recordedAt": "2026-09-06T08:22:38.815Z", "kind": "e2e+runtime-readback", "title": "真实三维移动、旋转、缩放手柄拖动及撤销重做保存重开", "screenshots": [], "details": "" } ] }, { "id": "view.rotate", "group": "三维视图与操作", "name": "旋转工具 / E", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2234, "symbol": "setTransformMode" }, "implementation": "本轮修复", "boundary": "对象旋转;与当前时间轴旋转叠加需要一致。", "fix": "统一动画基值处理。", "acceptance": "旋转后保存/撤销/重做姿态一致。", "verification": "PASS", "evidence": [ { "ids": [ "view.move", "view.rotate", "view.scale" ], "file": "evidence/51-gizmos.json", "status": "PASS", "testTitle": "真实三维移动、旋转、缩放手柄拖动及撤销重做保存重开", "recordedAt": "2026-09-06T08:22:38.815Z", "kind": "e2e+runtime-readback", "title": "真实三维移动、旋转、缩放手柄拖动及撤销重做保存重开", "screenshots": [], "details": "" } ] }, { "id": "view.scale", "group": "三维视图与操作", "name": "缩放工具 / R", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2234, "symbol": "setTransformMode" }, "implementation": "已有实现", "boundary": "对象缩放持久化;时间轴没有缩放轨道 UI。", "fix": "无新增缩放动画能力。", "acceptance": "对象缩放保存和重开正确。", "verification": "PASS", "evidence": [ { "ids": [ "view.move", "view.rotate", "view.scale" ], "file": "evidence/51-gizmos.json", "status": "PASS", "testTitle": "真实三维移动、旋转、缩放手柄拖动及撤销重做保存重开", "recordedAt": "2026-09-06T08:22:38.815Z", "kind": "e2e+runtime-readback", "title": "真实三维移动、旋转、缩放手柄拖动及撤销重做保存重开", "screenshots": [], "details": "" } ] }, { "id": "view.grid", "group": "三维视图与操作", "name": "网格开关", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2283, "symbol": "toggleGrid" }, "implementation": "已有实现", "boundary": "临时视图选项,不要求写入工程。", "fix": "非持久化不计保存缺陷。", "acceptance": "开关只影响辅助网格,不改变模型。", "verification": "PASS", "evidence": [ { "ids": [ "tree.search", "tree.visibility", "selection.sync", "view.grid", "view.pbr", "view.wireframe", "view.outline", "view.perspective", "view.preview", "view.snapshot", "timeline.native" ], "file": "evidence/viewport.json", "status": "PASS", "testTitle": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "recordedAt": "2026-09-06T08:22:14.169Z", "kind": "e2e", "title": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "screenshots": [], "details": "树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。" } ] }, { "id": "view.pbr", "group": "三维视图与操作", "name": "PBR 光照开关", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2236, "symbol": "handleView" }, "implementation": "已有实现", "boundary": "当前照明预览选项;不是批量材质编辑。", "fix": "非持久化不计保存缺陷。", "acceptance": "开关改变显示,物体材质字段不被覆盖。", "verification": "PASS", "evidence": [ { "ids": [ "tree.search", "tree.visibility", "selection.sync", "view.grid", "view.pbr", "view.wireframe", "view.outline", "view.perspective", "view.preview", "view.snapshot", "timeline.native" ], "file": "evidence/viewport.json", "status": "PASS", "testTitle": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "recordedAt": "2026-09-06T08:22:14.169Z", "kind": "e2e", "title": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "screenshots": [], "details": "树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。" } ] }, { "id": "view.wireframe", "group": "三维视图与操作", "name": "线框开关", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2236, "symbol": "handleView" }, "implementation": "已有实现", "boundary": "当前视图选项;不承诺保存到工程。", "fix": "非持久化不计保存缺陷。", "acceptance": "线框开关恢复正常着色。", "verification": "PASS", "evidence": [ { "ids": [ "tree.search", "tree.visibility", "selection.sync", "view.grid", "view.pbr", "view.wireframe", "view.outline", "view.perspective", "view.preview", "view.snapshot", "timeline.native" ], "file": "evidence/viewport.json", "status": "PASS", "testTitle": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "recordedAt": "2026-09-06T08:22:14.169Z", "kind": "e2e", "title": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "screenshots": [], "details": "树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。" } ] }, { "id": "view.outline", "group": "三维视图与操作", "name": "选中轮廓开关", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2236, "symbol": "handleView" }, "implementation": "本轮修复", "boundary": "当前会话偏好,进出预览后保持;不要求跨重载存储。", "fix": "重选时不重新泄露轮廓。", "acceptance": "关闭→重选→预览→退出,轮廓仍关闭。", "verification": "PASS", "evidence": [ { "ids": [ "tree.search", "tree.visibility", "selection.sync", "view.grid", "view.pbr", "view.wireframe", "view.outline", "view.perspective", "view.preview", "view.snapshot", "timeline.native" ], "file": "evidence/viewport.json", "status": "PASS", "testTitle": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "recordedAt": "2026-09-06T08:22:14.169Z", "kind": "e2e", "title": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "screenshots": [], "details": "树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。" } ] }, { "id": "view.perspective", "group": "三维视图与操作", "name": "透视视角按钮", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2236, "symbol": "handleView" }, "implementation": "现有边界", "boundary": "重置透视观察角度;当前没有正交视角菜单。", "fix": "不把临时视图状态当作保存缺陷。", "acceptance": "点击后相机和聚焦目标正常。", "verification": "PASS", "evidence": [ { "ids": [ "tree.search", "tree.visibility", "selection.sync", "view.grid", "view.pbr", "view.wireframe", "view.outline", "view.perspective", "view.preview", "view.snapshot", "timeline.native" ], "file": "evidence/viewport.json", "status": "PASS", "testTitle": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "recordedAt": "2026-09-06T08:22:14.169Z", "kind": "e2e", "title": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "screenshots": [], "details": "树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。" } ] }, { "id": "view.snapshot", "group": "三维视图与操作", "name": "下载视口截图", "source": { "file": "../../../unreal_tran/src/main.js", "line": 3092, "symbol": "downloadSnapshot" }, "implementation": "已有实现", "boundary": "当前 canvas PNG,可包含编辑辅助元素;保存封面是独立截图流程。", "fix": "无本轮专门改动。", "acceptance": "PNG 下载可打开,内容与当前视口对应。", "verification": "PASS", "evidence": [ { "ids": [ "tree.search", "tree.visibility", "selection.sync", "view.grid", "view.pbr", "view.wireframe", "view.outline", "view.perspective", "view.preview", "view.snapshot", "timeline.native" ], "file": "evidence/viewport.json", "status": "PASS", "testTitle": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "recordedAt": "2026-09-06T08:22:14.169Z", "kind": "e2e", "title": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "screenshots": [], "details": "树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。" } ] }, { "id": "view.panels", "group": "三维视图与操作", "name": "底部页签 / Tab / 面板高度", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2215, "symbol": "switchBottomMode" }, "implementation": "已有实现", "boundary": "时间轴、蓝图、曲线、日志切换;拖分割条只调整当前工作区。", "fix": "曲线入口接真实数据。", "acceptance": "各页签显示正确内容,调整高度后画布正确 resize。", "verification": "PASS", "evidence": [ { "ids": [ "project.name", "project.autosave", "view.orbit", "view.focus", "view.panels", "view.preview-step", "feedback.log", "feedback.stats", "property.identity", "property.binding", "selection.scope", "component.inspect" ], "file": "evidence/46-navigation-panels-autosave.json", "status": "PASS", "testTitle": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "recordedAt": "2026-09-06T08:23:54.656Z", "kind": "e2e+api", "title": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "screenshots": [], "details": "真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。" } ] }, { "id": "view.preview", "group": "三维视图与操作", "name": "进入 / 退出运行预览", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2284, "symbol": "togglePreview" }, "implementation": "本轮修复", "boundary": "真实当前三维场景;隐藏辅助工具,退出不丢编辑状态。", "fix": "进入显式派发 auto;退出停止运行并恢复轮廓策略。", "acceptance": "进入/退出与 Esc,三维仍可见,无辅助框泄漏。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.click", "blueprint.double-click", "blueprint.auto", "view.preview" ], "file": "evidence/truck-blueprint-events.json", "status": "PASS", "testTitle": "已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发", "recordedAt": "2026-09-06T08:21:50.227Z", "kind": "e2e", "title": "已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发", "screenshots": [], "details": "执行使用正式模型编辑器的真实模型/时间轴/高亮/提示 handler;不等同于学员端任务下发。" }, { "ids": [ "tree.search", "tree.visibility", "selection.sync", "view.grid", "view.pbr", "view.wireframe", "view.outline", "view.perspective", "view.preview", "view.snapshot", "timeline.native" ], "file": "evidence/viewport.json", "status": "PASS", "testTitle": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "recordedAt": "2026-09-06T08:22:14.169Z", "kind": "e2e", "title": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "screenshots": [], "details": "树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。" } ] }, { "id": "view.preview-step", "group": "三维视图与操作", "name": "预览播放 / 前后导航", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2122, "symbol": "bindShellEvents" }, "implementation": "现有边界", "boundary": "播放运行蓝图;前后按钮当前按时间轴 ±2 秒,不是按业务工序跳转。", "fix": "无新增业务工序导航。", "acceptance": "播放触发真实流程;前后跳时与时间轴一致。", "verification": "PASS", "evidence": [ { "ids": [ "project.name", "project.autosave", "view.orbit", "view.focus", "view.panels", "view.preview-step", "feedback.log", "feedback.stats", "property.identity", "property.binding", "selection.scope", "component.inspect" ], "file": "evidence/46-navigation-panels-autosave.json", "status": "PASS", "testTitle": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "recordedAt": "2026-09-06T08:23:54.656Z", "kind": "e2e+api", "title": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "screenshots": [], "details": "真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。" } ] }, { "id": "property.name", "group": "对象属性与扩展标记", "name": "对象名称", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1996, "symbol": "bindInspectorEvents" }, "implementation": "已有实现", "boundary": "修改当前节点名,标识保持稳定。", "fix": "无本轮专门改动。", "acceptance": "树和属性同步,保存重开名称保留。", "verification": "PASS", "evidence": [ { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" } ] }, { "id": "property.identity", "group": "对象属性与扩展标记", "name": "对象标识 / 状态说明", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1958, "symbol": "renderInspector" }, "implementation": "本轮修复", "boundary": "标识只读;可见性含祖先状态,阴影取实际castShadow;编辑能力由宿主权限注入。", "fix": "不再固定显示“可见/投射阴影”,补宿主可编辑状态钩子。", "acceptance": "标识不可编辑;选中对象、隐藏状态、阴影及只读权限标签一致。", "verification": "PASS", "evidence": [ { "ids": [ "project.name", "project.autosave", "view.orbit", "view.focus", "view.panels", "view.preview-step", "feedback.log", "feedback.stats", "property.identity", "property.binding", "selection.scope", "component.inspect" ], "file": "evidence/46-navigation-panels-autosave.json", "status": "PASS", "testTitle": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "recordedAt": "2026-09-06T08:23:54.656Z", "kind": "e2e+api", "title": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "screenshots": [], "details": "真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。" }, { "ids": [ "property.identity" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "inspector state tags reflect ancestor visibility and actual shadow flags, with a host editability hook", "screenshots": [], "details": "真实属性 DOM 显示隐藏/阴影状态,并提供宿主权限标签 hook;不伪称此单测完成会话鉴权。" } ] }, { "id": "property.position", "group": "对象属性与扩展标记", "name": "位置 XYZ 数值", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1996, "symbol": "bindInspectorEvents" }, "implementation": "本轮修复", "boundary": "局部坐标;动画当前帧下编辑也能生成正确基值。", "fix": "中性姿态与实际应用偏移统一。", "acceptance": "三个轴编辑、保存重开、动画回放一致。", "verification": "PASS", "evidence": [ { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" } ] }, { "id": "property.rotation", "group": "对象属性与扩展标记", "name": "旋转 XYZ 数值", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1996, "symbol": "bindInspectorEvents" }, "implementation": "本轮修复", "boundary": "输入角度转弧度存储,显示角度。", "fix": "多轨旋转基值不重复扣除。", "acceptance": "度数显示与存储/播放的弧度一致。", "verification": "PASS", "evidence": [ { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" } ] }, { "id": "property.scale", "group": "对象属性与扩展标记", "name": "缩放 XYZ 数值", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1996, "symbol": "bindInspectorEvents" }, "implementation": "已有实现", "boundary": "对象当前缩放,不添加新的缩放轨道。", "fix": "无本轮专门改动。", "acceptance": "非均匀缩放可保存和重开。", "verification": "PASS", "evidence": [ { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" } ] }, { "id": "property.color", "group": "对象属性与扩展标记", "name": "材质基础色", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1996, "symbol": "bindInspectorEvents" }, "implementation": "现有边界", "boundary": "只编辑当前对象第一个材质槽;共享材质可能影响共享该实例的网格。", "fix": "界面明确材质槽 1/N。", "acceptance": "颜色修改、保存重开对应当前槽;不宣称所有槽可编辑。", "verification": "PASS", "evidence": [ { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" } ] }, { "id": "property.metalness", "group": "对象属性与扩展标记", "name": "材质金属度", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1996, "symbol": "bindInspectorEvents" }, "implementation": "现有边界", "boundary": "当前首槽 PBR 参数 0~1。", "fix": "标明材质槽范围。", "acceptance": "拖动参数有效,保存重开值一致。", "verification": "PASS", "evidence": [ { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" } ] }, { "id": "property.roughness", "group": "对象属性与扩展标记", "name": "材质粗糙度", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1996, "symbol": "bindInspectorEvents" }, "implementation": "现有边界", "boundary": "当前首槽 PBR 参数 0~1。", "fix": "标明材质槽范围。", "acceptance": "参数与当前槽持久化一致。", "verification": "PASS", "evidence": [ { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" } ] }, { "id": "property.opacity", "group": "对象属性与扩展标记", "name": "材质透明度", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1996, "symbol": "bindInspectorEvents" }, "implementation": "现有边界", "boundary": "当前首槽;联动 transparent/depthWrite。", "fix": "保留实际透明度保存字段。", "acceptance": "透明效果与重开结果一致。", "verification": "PASS", "evidence": [ { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" } ] }, { "id": "property.code", "group": "对象属性与扩展标记", "name": "自定义资源编码", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1996, "symbol": "bindInspectorEvents" }, "implementation": "本轮修复", "boundary": "对象 customProperties.resourceCode,不是服务器资产主键。", "fix": "输入真正回写、保存、恢复及撤销。", "acceptance": "编辑、清空、重选、JSON 往返、刷新均一致。", "verification": "PASS", "evidence": [ { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" }, { "ids": [ "property.code", "property.status", "component.tag" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "custom properties and extension labels survive actual capture, project JSON and object restore", "screenshots": [], "details": "真实对象 capture、normalization、JSON 与 restore 往返;清空和撤销到未存在字段状态。" } ] }, { "id": "property.status", "group": "对象属性与扩展标记", "name": "自定义运行状态", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1996, "symbol": "bindInspectorEvents" }, "implementation": "本轮修复", "boundary": "NORMAL/SERVICE 标签,不会发起真实设备控制。", "fix": "真正写回对象,首次变更可撤销。", "acceptance": "两状态切换及刷新保留。", "verification": "PASS", "evidence": [ { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" }, { "ids": [ "property.code", "property.status", "component.tag" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "custom properties and extension labels survive actual capture, project JSON and object restore", "screenshots": [], "details": "真实对象 capture、normalization、JSON 与 restore 往返;清空和撤销到未存在字段状态。" } ] }, { "id": "property.binding", "group": "对象属性与扩展标记", "name": "流程绑定说明", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1958, "symbol": "renderInspector" }, "implementation": "现有边界", "boundary": "只读显示模板/时间轴名称,不是教学任务的设备或物联网绑定。", "fix": "无本轮业务绑定改造。", "acceptance": "只读,不将其当作任务绑定操作验收。", "verification": "PASS", "evidence": [ { "ids": [ "project.name", "project.autosave", "view.orbit", "view.focus", "view.panels", "view.preview-step", "feedback.log", "feedback.stats", "property.identity", "property.binding", "selection.scope", "component.inspect" ], "file": "evidence/46-navigation-panels-autosave.json", "status": "PASS", "testTitle": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "recordedAt": "2026-09-06T08:23:54.656Z", "kind": "e2e+api", "title": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "screenshots": [], "details": "真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。" } ] }, { "id": "component.inspect", "group": "对象属性与扩展标记", "name": "组件页内置能力说明", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1975, "symbol": "renderComponents" }, "implementation": "现有边界", "boundary": "变换、场景节点、渲染等派生说明,没有通用组件启停系统。", "fix": "区分内置说明与自定义标记。", "acceptance": "显示与对象类型一致,不虚报可执行组件配置。", "verification": "PASS", "evidence": [ { "ids": [ "project.name", "project.autosave", "view.orbit", "view.focus", "view.panels", "view.preview-step", "feedback.log", "feedback.stats", "property.identity", "property.binding", "selection.scope", "component.inspect" ], "file": "evidence/46-navigation-panels-autosave.json", "status": "PASS", "testTitle": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "recordedAt": "2026-09-06T08:23:54.656Z", "kind": "e2e+api", "title": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "screenshots": [], "details": "真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。" } ] }, { "id": "component.tag", "group": "对象属性与扩展标记", "name": "添加扩展标记", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1975, "symbol": "renderComponents" }, "implementation": "本轮澄清", "boundary": "只保存名称标签;没有脚本、启停、运行时行为。", "fix": "从“添加组件”改为“扩展标记,仅保存标签”。", "acceptance": "新增标签保存/重开;不得声称已启用新行为。", "verification": "PASS", "evidence": [ { "ids": [ "property.name", "property.position", "property.rotation", "property.scale", "property.color", "property.metalness", "property.roughness", "property.opacity", "property.code", "property.status", "component.tag", "project.undo", "project.redo", "project.save" ], "file": "evidence/properties.json", "status": "PASS", "testTitle": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "recordedAt": "2026-09-06T08:21:58.738Z", "kind": "e2e+api", "title": "对象名称、变换、PBR、属性与扩展标记保存重开;撤销重做", "screenshots": [], "details": "真实属性输入/材质参数/标签、保存与刷新;变换只验数值输入,不代替三维 gizmo 拖动。" }, { "ids": [ "property.code", "property.status", "component.tag" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "custom properties and extension labels survive actual capture, project JSON and object restore", "screenshots": [], "details": "真实对象 capture、normalization、JSON 与 restore 往返;清空和撤销到未存在字段状态。" } ] }, { "id": "timeline.play", "group": "时间轴与曲线", "name": "播放 / 暂停 / Space", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 393, "symbol": "play()" }, "implementation": "已有实现", "boundary": "播放当前编辑时间轴;原生 GLB 动画单独控制。", "fix": "停止清理与时间同步由本轮补强。", "acceptance": "播放进度/真实对象姿态变化,暂停停在当前帧。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" } ] }, { "id": "timeline.stop", "group": "时间轴与曲线", "name": "停止", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 429, "symbol": "stop()" }, "implementation": "已有实现", "boundary": "停止并回到起始时间,按起始帧应用动画。", "fix": "无本轮语义扩展。", "acceptance": "按钮停止后不再自行前进。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" } ] }, { "id": "timeline.loop", "group": "时间轴与曲线", "name": "循环开关", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 198, "symbol": "#bindEvents" }, "implementation": "本轮修复", "boundary": "循环设置随 timeline 保存;不循环到达终点只完成一次。", "fix": "循环变更进入历史/dirty。", "acceptance": "开关、保存重开、到末尾/循环边界正确。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" } ] }, { "id": "timeline.key-nav", "group": "时间轴与曲线", "name": "上一 / 下一关键帧", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 232, "symbol": "#onClick" }, "implementation": "已有实现", "boundary": "按时间轴关键帧时间定位。", "fix": "无本轮专门改动。", "acceptance": "多个轨道时跳到正确相邻关键时刻。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" } ] }, { "id": "timeline.seek", "group": "时间轴与曲线", "name": "拖播放头 / 点击刻度", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 263, "symbol": "#onPointerDown" }, "implementation": "本轮修复", "boundary": "实时求值当前帧;键盘左右移动一帧,Shift 移动一秒。", "fix": "拖动补发 timechange,解绑指针监听。", "acceptance": "时间码、曲线和视口姿态同步。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.seek", "timeline.record", "timeline.baseline", "curve.axis", "blueprint.palette", "blueprint.add", "blueprint.canvas" ], "file": "evidence/truck-blueprint-tool-supplement.json", "status": "PASS", "testTitle": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "recordedAt": "2026-09-06T08:21:23.003Z", "kind": "e2e+runtime-read", "title": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "screenshots": [], "details": "真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。" } ] }, { "id": "timeline.add-track", "group": "时间轴与曲线", "name": "添加轨道", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 471, "symbol": "addTrack(" }, "implementation": "已有实现", "boundary": "当前主装备节点的变换轨道。", "fix": "无新增轨道类型。", "acceptance": "增加后可绑定节点并保存。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" } ] }, { "id": "timeline.remove-track", "group": "时间轴与曲线", "name": "删除轨道", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 495, "symbol": "removeTrack(" }, "implementation": "本轮修复", "boundary": "删除后不再保留该轨道施加的旧姿态。", "fix": "宿主恢复此前作用的属性基值。", "acceptance": "删除即时复原,再保存/重开一致。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" } ] }, { "id": "timeline.target", "group": "时间轴与曲线", "name": "修改轨道目标节点", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 506, "symbol": "updateSelectedTrack" }, "implementation": "本轮修复", "boundary": "用稳定对象标识绑定,不靠同名第一项。", "fix": "重绑清理旧目标的应用姿态。", "acceptance": "从 A 改为 B,A 复原,B 播放。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" }, { "ids": [ "timeline.multitrack", "timeline.target" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "changing a binding resets the former target and keeps manual transform edits as base", "screenshots": [], "details": "轨道重绑后旧目标复位,手动变换成为新基值;不假称鼠标拖动已验。" } ] }, { "id": "timeline.property", "group": "时间轴与曲线", "name": "选择位置 / 旋转属性", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 506, "symbol": "updateSelectedTrack" }, "implementation": "已有实现", "boundary": "位置 XYZ 向量、旋转 X/Y/Z;没有缩放轨道 UI。", "fix": "保留当前实际能力。", "acceptance": "属性切换后关键帧类型和播放一致。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" } ] }, { "id": "timeline.mute", "group": "时间轴与曲线", "name": "轨道静音", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 232, "symbol": "#onClick" }, "implementation": "本轮修复", "boundary": "仅忽略当前轨道,不关闭其他轨道。", "fix": "不再停留旧姿态或保存时重复扣值。", "acceptance": "静音/恢复后实际姿态与重开一致。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" } ] }, { "id": "timeline.record", "group": "时间轴与曲线", "name": "记录当前姿态关键帧", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2276, "symbol": "addKeyframeFromSelection" }, "implementation": "本轮修复", "boundary": "记录相对对象基值的当前值。", "fix": "与多轨实际应用/保存采用同一基值。", "acceptance": "操作对象后记录,回放命中相同姿态。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.seek", "timeline.record", "timeline.baseline", "curve.axis", "blueprint.palette", "blueprint.add", "blueprint.canvas" ], "file": "evidence/truck-blueprint-tool-supplement.json", "status": "PASS", "testTitle": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "recordedAt": "2026-09-06T08:21:23.003Z", "kind": "e2e+runtime-read", "title": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "screenshots": [], "details": "真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。" } ] }, { "id": "timeline.add-key", "group": "时间轴与曲线", "name": "双击新增关键帧", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 255, "symbol": "#onDoubleClick" }, "implementation": "已有实现", "boundary": "在轨道对应时刻创建/更新关键帧。", "fix": "时间碰撞保留当前编辑帧。", "acceptance": "新增后节点可选,时间/数值可保存。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" } ] }, { "id": "timeline.delete-key", "group": "时间轴与曲线", "name": "删除关键帧 / Delete", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 584, "symbol": "deleteSelected" }, "implementation": "已有实现", "boundary": "删除选中的关键帧;输入框编辑时不截获删除键。", "fix": "删除和监听清理回归。", "acceptance": "删除后曲线和播放变化,撤销可恢复。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" } ] }, { "id": "timeline.drag-key", "group": "时间轴与曲线", "name": "关键帧拖动 / Alt 取消吸附", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 263, "symbol": "#onPointerDown" }, "implementation": "本轮修复", "boundary": "默认 1/30 秒吸附;重叠时合并,保留正在编辑帧。", "fix": "拖动时间同步,碰撞处理确定。", "acceptance": "拖动/重叠/Alt 行为和保存后时间正确。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" } ] }, { "id": "timeline.zoom", "group": "时间轴与曲线", "name": "时间轴缩放", "source": { "file": "../../../unreal_tran/src/timeline.js", "line": 451, "symbol": "setZoom" }, "implementation": "已有实现", "boundary": "显示缩放随 timeline 记录;不改变时长/关键帧数值。", "fix": "无本轮专门改动。", "acceptance": "缩放不改变动作内容。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" } ] }, { "id": "timeline.multitrack", "group": "时间轴与曲线", "name": "多轨组合与保存基值", "source": { "file": "../../../unreal_tran/src/model-editor-state.js", "line": 12, "symbol": "applyEditorTimelinePose" }, "implementation": "本轮修复", "boundary": "不同属性组合;同对象同属性最后有效轨道生效。", "fix": "求值、手工编辑基值、序列化一致。", "acceptance": "两轨同属性、静音、删轨、重绑、重开无漂移。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.multitrack" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "multiple tracks on one property use last effective value; capture subtracts it only once", "screenshots": [], "details": "调用实际 apply/capture/update 基值方法;验证同属性最后有效轨道、静音/删除后中性姿态,偏移只扣一次。" }, { "ids": [ "timeline.multitrack", "timeline.target" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "changing a binding resets the former target and keeps manual transform edits as base", "screenshots": [], "details": "轨道重绑后旧目标复位,手动变换成为新基值;不假称鼠标拖动已验。" } ] }, { "id": "timeline.baseline", "group": "时间轴与曲线", "name": "载入装备基准动作", "source": { "file": "../../../unreal_tran/src/main.js", "line": 3058, "symbol": "loadBaselineAnimation" }, "implementation": "现有边界", "boundary": "匹配部件后直接替换当前时间轴/蓝图;当前没有单独确认框,不是任意维修流程自动生成。", "fix": "保持当前装备限定;无匹配只提示,不改工程。", "acceptance": "匹配时载入/保存/重开;无匹配时保留原数据并给出提示。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.seek", "timeline.record", "timeline.baseline", "curve.axis", "blueprint.palette", "blueprint.add", "blueprint.canvas" ], "file": "evidence/truck-blueprint-tool-supplement.json", "status": "PASS", "testTitle": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "recordedAt": "2026-09-06T08:21:23.003Z", "kind": "e2e+runtime-read", "title": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "screenshots": [], "details": "真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。" } ] }, { "id": "timeline.native", "group": "时间轴与曲线", "name": "GLB 原生动画选择 / 播放", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1715, "symbol": "selectNativeAnimation" }, "implementation": "已有实现", "boundary": "真实 GLB 自带片段;保存选择索引,不是蓝图 clip 字段。", "fix": "无片段时禁用。", "acceptance": "狐狸实际动画播放/暂停,选择保存后重开一致。", "verification": "PASS", "evidence": [ { "ids": [ "tree.search", "tree.visibility", "selection.sync", "view.grid", "view.pbr", "view.wireframe", "view.outline", "view.perspective", "view.preview", "view.snapshot", "timeline.native" ], "file": "evidence/viewport.json", "status": "PASS", "testTitle": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "recordedAt": "2026-09-06T08:22:14.169Z", "kind": "e2e", "title": "场景树搜索、可见性、选择、视口工具、原生动画与截图", "screenshots": [], "details": "树搜索、对象隐藏刷新、轮廓及视图开关、原生动画和PNG下载。移动/旋转/缩放只切换模式,真实gizmo拖动仍需补验;聚焦未做几何断言。" } ] }, { "id": "timeline.curve", "group": "时间轴与曲线", "name": "曲线选择轨道 / 关键帧", "source": { "file": "../../../unreal_tran/src/model-curve-editor.js", "line": 5, "symbol": "class ModelCurveEditor" }, "implementation": "本轮修复", "boundary": "曲线与时间轴共用一份轨道和关键帧数据。", "fix": "移除固定 SVG 假数据。", "acceptance": "实际轨道名和点数匹配时间轴,点击同步当前帧。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" }, { "ids": [ "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "curve edits real scalar key values, time and interpolation, persisting through document reload", "screenshots": [], "details": "真实曲线控件与 TimelineEditor,标量编辑与文档往返。" } ] }, { "id": "curve.time", "group": "时间轴与曲线", "name": "曲线编辑时间", "source": { "file": "../../../unreal_tran/src/model-curve-editor.js", "line": 30, "symbol": "change(input)" }, "implementation": "本轮修复", "boundary": "更新实际 key.time,约束在时间轴时长内。", "fix": "调用 timeline.updateKeyframe 并保持编辑目标。", "acceptance": "改时间后重排、撤销、JSON 往返一致。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" }, { "ids": [ "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "curve edits real scalar key values, time and interpolation, persisting through document reload", "screenshots": [], "details": "真实曲线控件与 TimelineEditor,标量编辑与文档往返。" } ] }, { "id": "curve.value", "group": "时间轴与曲线", "name": "曲线编辑标量 / XYZ 数值", "source": { "file": "../../../unreal_tran/src/model-curve-editor.js", "line": 30, "symbol": "change(input)" }, "implementation": "本轮修复", "boundary": "修改实际关键帧值,向量编辑保留其他分量。", "fix": "无效空值拒绝提交。", "acceptance": "标量和向量都驱动真实姿态并保存。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" }, { "ids": [ "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "curve edits real scalar key values, time and interpolation, persisting through document reload", "screenshots": [], "details": "真实曲线控件与 TimelineEditor,标量编辑与文档往返。" }, { "ids": [ "curve.value" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "curve vector editing preserves other components and selected track; invalid values do not mutate", "screenshots": [], "details": "向量其它分量保留,当前轨道、非法值保护;不包含显示轴切换或可视像素验收。" } ] }, { "id": "curve.easing", "group": "时间轴与曲线", "name": "曲线插值", "source": { "file": "../../../unreal_tran/src/model-curve-editor.js", "line": 30, "symbol": "change(input)" }, "implementation": "本轮修复", "boundary": "支持线性、平滑、缓入、缓出、阶梯;没有 Bezier 切线 UI。", "fix": "阶梯插值与已有下游时间轴求值一致。", "acceptance": "修改插值改变中间帧,重开保留。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.play", "timeline.stop", "timeline.loop", "timeline.key-nav", "timeline.add-track", "timeline.remove-track", "timeline.target", "timeline.property", "timeline.mute", "timeline.add-key", "timeline.delete-key", "timeline.drag-key", "timeline.zoom", "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "evidence/truck-timeline-curve.json", "status": "PASS", "testTitle": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "recordedAt": "2026-09-06T08:21:02.898Z", "kind": "e2e+runtime-read", "title": "时间轴真实控件:轨道/关键帧/播放与曲线 5 插值,保存后重新加载一致", "screenshots": [], "details": "真实控件和指针操作,运行时读取数值校验;curve.axis仅切换无数值独立断言,timeline.seek播放头与Alt吸附未在本例覆盖。" }, { "ids": [ "timeline.curve", "curve.time", "curve.value", "curve.easing" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "curve edits real scalar key values, time and interpolation, persisting through document reload", "screenshots": [], "details": "真实曲线控件与 TimelineEditor,标量编辑与文档往返。" } ] }, { "id": "curve.axis", "group": "时间轴与曲线", "name": "曲线 XYZ 分量显示", "source": { "file": "../../../unreal_tran/src/model-curve-editor.js", "line": 30, "symbol": "change(input)" }, "implementation": "已有实现", "boundary": "改变图上显示分量,不改另外两轴的数据。", "fix": "当前实际向量轨道才出现。", "acceptance": "切分量显示不同曲线,数值不被改写。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.seek", "timeline.record", "timeline.baseline", "curve.axis", "blueprint.palette", "blueprint.add", "blueprint.canvas" ], "file": "evidence/truck-blueprint-tool-supplement.json", "status": "PASS", "testTitle": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "recordedAt": "2026-09-06T08:21:23.003Z", "kind": "e2e+runtime-read", "title": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "screenshots": [], "details": "真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。" } ] }, { "id": "blueprint.palette", "group": "蓝图与交互反馈", "name": "节点库搜索 / 拖入节点", "source": { "file": "../../../unreal_tran/src/blueprint.js", "line": 287, "symbol": "_renderPalette" }, "implementation": "已有实现", "boundary": "当前七类节点:触发、时间轴、等待、高亮、提示、条件、完成。", "fix": "不新增任意脚本节点。", "acceptance": "搜索、拖入后节点类型和参数可保存。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.seek", "timeline.record", "timeline.baseline", "curve.axis", "blueprint.palette", "blueprint.add", "blueprint.canvas" ], "file": "evidence/truck-blueprint-tool-supplement.json", "status": "PASS", "testTitle": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "recordedAt": "2026-09-06T08:21:23.003Z", "kind": "e2e+runtime-read", "title": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "screenshots": [], "details": "真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。" } ] }, { "id": "blueprint.add", "group": "蓝图与交互反馈", "name": "添加节点按钮", "source": { "file": "../../../unreal_tran/src/blueprint.js", "line": 257, "symbol": "_bind()" }, "implementation": "已有实现", "boundary": "工具栏默认添加提示节点;其他类型来自节点库。", "fix": "无本轮专门改动。", "acceptance": "新增进入画布、历史与 JSON。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.seek", "timeline.record", "timeline.baseline", "curve.axis", "blueprint.palette", "blueprint.add", "blueprint.canvas" ], "file": "evidence/truck-blueprint-tool-supplement.json", "status": "PASS", "testTitle": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "recordedAt": "2026-09-06T08:21:23.003Z", "kind": "e2e+runtime-read", "title": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "screenshots": [], "details": "真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。" } ] }, { "id": "blueprint.parameters", "group": "蓝图与交互反馈", "name": "节点标题 / 参数编辑", "source": { "file": "../../../unreal_tran/src/blueprint.js", "line": 431, "symbol": "_renderInspector" }, "implementation": "已有实现", "boundary": "按节点类型提供数字、文字、枚举、复选框。", "fix": "保留数值边界与真实数据写回。", "acceptance": "改参数与标题后重开保留。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" } ] }, { "id": "blueprint.move", "group": "蓝图与交互反馈", "name": "节点拖动 / 多选框选", "source": { "file": "../../../unreal_tran/src/blueprint.js", "line": 566, "symbol": "_onViewportPointerDown" }, "implementation": "已有实现", "boundary": "节点布局随蓝图 JSON 保存。", "fix": "无本轮专门改动。", "acceptance": "多选移动位置正确,撤销恢复。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" } ] }, { "id": "blueprint.connect", "group": "蓝图与交互反馈", "name": "连接 / 替换 / 删除执行线", "source": { "file": "../../../unreal_tran/src/blueprint.js", "line": 767, "symbol": "connect(" }, "implementation": "本轮修复", "boundary": "单输出重连替换;允许多个来源汇聚同一输入。", "fix": "不再清掉同一输入的其他合法来源。", "acceptance": "true/false 汇聚保留两条,替换输出只影响该线。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" } ] }, { "id": "blueprint.delete", "group": "蓝图与交互反馈", "name": "删除节点 / 连线", "source": { "file": "../../../unreal_tran/src/blueprint.js", "line": 786, "symbol": "deleteSelection" }, "implementation": "已有实现", "boundary": "删除节点一并清理相关边。", "fix": "复制/剪切使用同一图数据合同。", "acceptance": "无悬空边;撤销恢复完整图。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" } ] }, { "id": "blueprint.copy", "group": "蓝图与交互反馈", "name": "复制 / 剪切 / 粘贴", "source": { "file": "../../../unreal_tran/src/blueprint.js", "line": 714, "symbol": "copySelection" }, "implementation": "本轮修复", "boundary": "Ctrl/Cmd+C/X/V;复制选区内部边,不复制指向外部的边。", "fix": "新节点 ID 和内部连线同步生成,阻止快捷键冒泡。", "acceptance": "副本内部连接正确,不误删外部对象。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" } ] }, { "id": "blueprint.canvas", "group": "蓝图与交互反馈", "name": "平移 / 缩放 / 适应画布", "source": { "file": "../../../unreal_tran/src/blueprint.js", "line": 801, "symbol": "frameAll" }, "implementation": "已有实现", "boundary": "蓝图画布浏览;不是三维相机。", "fix": "无本轮专门改动。", "acceptance": "滚轮、Space 拖动和适应可见全部节点。", "verification": "PASS", "evidence": [ { "ids": [ "timeline.seek", "timeline.record", "timeline.baseline", "curve.axis", "blueprint.palette", "blueprint.add", "blueprint.canvas" ], "file": "evidence/truck-blueprint-tool-supplement.json", "status": "PASS", "testTitle": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "recordedAt": "2026-09-06T08:21:23.003Z", "kind": "e2e+runtime-read", "title": "补充时间轴与蓝图工具:刻度/按键/记录/分量、基准匹配、节点搜索拖入与画布平移", "screenshots": [], "details": "真实模型编辑参数/基准轨道生成验收,不构成装备操作或维修指导。桥梁原工程94仅GET和copy。" } ] }, { "id": "blueprint.run", "group": "蓝图与交互反馈", "name": "运行 / F5", "source": { "file": "../../../unreal_tran/src/blueprint.js", "line": 871, "symbol": "async run(" }, "implementation": "本轮修复", "boundary": "在当前模型编辑器驱动真实场景;有 200 步循环上限。", "fix": "条件与等待流程回归。", "acceptance": "执行高亮、动作、反馈;循环图可终止不挂死。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" } ] }, { "id": "blueprint.stop", "group": "蓝图与交互反馈", "name": "停止运行", "source": { "file": "../../../unreal_tran/src/blueprint.js", "line": 997, "symbol": "stop(" }, "implementation": "本轮修复", "boundary": "停止中断挂起等待,旧运行不覆盖新运行状态。", "fix": "AbortSignal 贯穿宿主动作与高亮清理。", "acceptance": "等待/动画中停止及时退出,无迟到完成提示。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" } ] }, { "id": "blueprint.reset", "group": "蓝图与交互反馈", "name": "重置执行", "source": { "file": "../../../unreal_tran/src/blueprint.js", "line": 1014, "symbol": "resetExecution" }, "implementation": "已有实现", "boundary": "复位执行状态、时间轴和高亮;保留蓝图编辑内容。", "fix": "高亮恢复原材质。", "acceptance": "运行后重置,进度归零且节点图不丢。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" } ] }, { "id": "blueprint.click", "group": "蓝图与交互反馈", "name": "点击触发目标", "source": { "file": "../../../unreal_tran/src/blueprint.js", "line": 933, "symbol": "async trigger(" }, "implementation": "本轮修复", "boundary": "匹配事件和目标 ID/名称/祖先别名;空目标可通配。", "fix": "不再点击 B 触发 A。", "acceptance": "两个不同目标分别点击,只运行匹配分支。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.click", "blueprint.double-click", "blueprint.auto", "view.preview" ], "file": "evidence/truck-blueprint-events.json", "status": "PASS", "testTitle": "已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发", "recordedAt": "2026-09-06T08:21:50.227Z", "kind": "e2e", "title": "已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发", "screenshots": [], "details": "执行使用正式模型编辑器的真实模型/时间轴/高亮/提示 handler;不等同于学员端任务下发。" } ] }, { "id": "blueprint.double-click", "group": "蓝图与交互反馈", "name": "双击触发目标", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2256, "symbol": "pickObject" }, "implementation": "本轮修复", "boundary": "双击独立派发 double-click,与单击区分。", "fix": "增加真实视口双击派发并抑制待发单击。", "acceptance": "双击只触发目标的双击分支。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.click", "blueprint.double-click", "blueprint.auto", "view.preview" ], "file": "evidence/truck-blueprint-events.json", "status": "PASS", "testTitle": "已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发", "recordedAt": "2026-09-06T08:21:50.227Z", "kind": "e2e", "title": "已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发", "screenshots": [], "details": "执行使用正式模型编辑器的真实模型/时间轴/高亮/提示 handler;不等同于学员端任务下发。" } ] }, { "id": "blueprint.auto", "group": "蓝图与交互反馈", "name": "自动触发", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2284, "symbol": "togglePreview" }, "implementation": "本轮修复", "boundary": "仅进入预览显式派发 auto,不把每次单击当 auto。", "fix": "事件匹配与进入预览联动。", "acceptance": "进入预览触发一次,普通点击不重放自动分支。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.click", "blueprint.double-click", "blueprint.auto", "view.preview" ], "file": "evidence/truck-blueprint-events.json", "status": "PASS", "testTitle": "已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发", "recordedAt": "2026-09-06T08:21:50.227Z", "kind": "e2e", "title": "已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发", "screenshots": [], "details": "执行使用正式模型编辑器的真实模型/时间轴/高亮/提示 handler;不等同于学员端任务下发。" } ] }, { "id": "blueprint.timeline", "group": "蓝图与交互反馈", "name": "播放时间轴节点", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2024, "symbol": "playTimelineForBlueprint" }, "implementation": "本轮修复", "boundary": "播放当前唯一时间轴,动作名称仅描述;支持起始时间、速度、等待。", "fix": "起始末帧不反跳零,终止可取消。", "acceptance": "起始时间和速度实际生效,等待与不等待分支正确。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" } ] }, { "id": "blueprint.wait", "group": "蓝图与交互反馈", "name": "等待节点", "source": { "file": "../../../unreal_tran/src/blueprint.js", "line": 1, "symbol": "NODE_TYPES" }, "implementation": "已有实现", "boundary": "指定毫秒等待;可被停止取消。", "fix": "异步停止及时结束。", "acceptance": "等待期间停止不会继续执行后续节点。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" } ] }, { "id": "blueprint.highlight", "group": "蓝图与交互反馈", "name": "高亮节点 / 呼吸效果", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2052, "symbol": "highlightPart" }, "implementation": "本轮修复", "boundary": "准确命中可见对象;同名歧义要求标识;没有可高亮材质则报错。", "fix": "拒绝错误整机回退,真实呼吸效果,保存忽略临时高亮。", "acceptance": "目标正确、找不到报错、结束恢复、保存不永久染色。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" }, { "ids": [ "tree.visibility", "blueprint.highlight" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "visible child under a hidden ancestor cannot be picked or highlighted; trigger aliases include ancestors", "screenshots": [], "details": "共用可见性和祖先别名函数;鼠标事件另由 E2E 验证。" }, { "ids": [ "blueprint.highlight" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "highlight rejects missing/hidden targets and transient colors never enter a saved project", "screenshots": [], "details": "目标不存在/隐藏报错,执行时暂态发光颜色不进入保存文档,结束恢复原材质。" } ] }, { "id": "blueprint.message", "group": "蓝图与交互反馈", "name": "操作提示节点", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2099, "symbol": "showTeachingMessage" }, "implementation": "现有边界", "boundary": "显示标题/文本,可点击关闭或约 1.8/2.5 秒自动继续;不是强制人员确认。", "fix": "不将其当作业务审核/签名。", "acceptance": "文本出现、关闭和停止不留弹层。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" } ] }, { "id": "blueprint.condition", "group": "蓝图与交互反馈", "name": "条件判断节点", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2095, "symbol": "evaluateBlueprintCondition" }, "implementation": "本轮修复", "boundary": "当前运行变量、嵌套字段、truthy/布尔/数字比较。", "fix": "false 没有出口不走 true,字符串布尔正确解析。", "acceptance": "真假分支分别验证,缺出口不误执行。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.condition" ], "file": "evidence/truck-blueprint-events.json", "status": "PASS", "testTitle": "已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发", "recordedAt": "2026-09-06T08:21:50.227Z", "kind": "runtime", "title": "已保存蓝图:条件出口与目标匹配 runtime 检查,真实模型单击/双击/进入预览自动触发", "screenshots": [], "details": "" }, { "ids": [ "blueprint.condition" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "boolean, numeric and nested blueprint conditions select their true/false branches", "screenshots": [], "details": "布尔、数字、truthy 与嵌套路径值解析。" } ] }, { "id": "blueprint.finish", "group": "蓝图与交互反馈", "name": "流程完成节点", "source": { "file": "../../../unreal_tran/src/main.js", "line": 1075, "symbol": "initEditors" }, "implementation": "已有实现", "boundary": "本次预览结果/进度提示,不写教学任务成绩。", "fix": "保持本次运行反馈边界。", "acceptance": "成功/完成提示与实际执行结果一致。", "verification": "PASS", "evidence": [ { "ids": [ "blueprint.parameters", "blueprint.move", "blueprint.connect", "blueprint.delete", "blueprint.copy", "blueprint.run", "blueprint.stop", "blueprint.reset", "blueprint.timeline", "blueprint.wait", "blueprint.highlight", "blueprint.message", "blueprint.finish", "project.undo", "project.redo" ], "file": "evidence/truck-blueprint-ui.json", "status": "PASS", "testTitle": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "recordedAt": "2026-09-06T08:21:37.144Z", "kind": "e2e+runtime-read", "title": "蓝图真实节点库与端口拖线:七种参数、内部边复制/剪切、撤销重做及保存", "screenshots": [], "details": "节点库点选添加及实际拖动/接线/执行;不把点选当成节点拖入,不把适应画布当成鼠标平移缩放全部已验。" } ] }, { "id": "publish.preflight", "group": "发布、交付与运行信息", "name": "生成 / 发布检查面板", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2978, "symbol": "togglePublishPanel" }, "implementation": "现有边界", "boundary": "本地主要检查资源引用存在及轨道/节点数量,不执行完整蓝图可达性、终止性或业务校验;服务端发布校验最终生效。", "fix": "发布目标与离线交付能力明确。", "acceptance": "可打开关闭;不完整工程不能凭界面绿标绕过服务端。", "verification": "PASS", "evidence": [ { "ids": [ "resource.loading", "publish.preflight" ], "file": "evidence/63-loading-publish-preflight.json", "status": "PASS", "testTitle": "受控 GLB 下载失败阻止编辑,重试成功;发布预检开关无写入且拒绝错误资源 JSON", "recordedAt": "2026-09-06T08:24:17.025Z", "kind": "e2e+api", "title": "受控 GLB 下载失败阻止编辑,重试成功;发布预检开关无写入且拒绝错误资源 JSON", "screenshots": [], "details": "真实受控下载定向503→中性失败与禁止编辑→解除故障后UI重试成功;预检面板开关不写项目,错误资源JSON在替换当前模型之前被拒绝。服务端发布时code/URI一致性拒绝由ContentProjectServiceTest合同回归覆盖,未把面板绿标代替服务端校验。" } ] }, { "id": "publish.api", "group": "发布、交付与运行信息", "name": "发布到场景资源库", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2897, "symbol": "publishToSceneLibrary" }, "implementation": "本轮修复", "boundary": "先保存当前工程再发布固定版本;纯 publisher 发布已保存版本。", "fix": "整个保存/发布队列互斥,失败不发后续发布。", "acceptance": "发布版本/模型资产/列表状态一致,保存失败不发布。", "verification": "PASS", "evidence": [ { "ids": [ "publish.api", "publish.cover", "publish.html", "publish.downstream" ], "file": "evidence/34-publish-fixed-version.json", "status": "PASS", "testTitle": "直接发布同步截图封面、固定版本目录与真实离线步骤 HTML", "recordedAt": "2026-09-06T08:20:51.047Z", "kind": "e2e+api", "title": "直接发布同步截图封面、固定版本目录与真实离线步骤 HTML", "screenshots": [], "details": "目录可取得固定发布版本及同版封面;HTML已真实打开并确认演示边界,未声明下游动画执行或完整离线三维运行。" }, { "ids": [ "publish.api", "publish.cover", "feedback.toast" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "API publish supplies current cover, blocks duplicate publication and reports preserved old cover once", "screenshots": [], "details": "实际发布宿主方法传封面、重入阻断和上传失败只提示一次;模拟 API,不是实际服务响应。" }, { "ids": [ "project.conflict", "publish.api", "publish.cover" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "API conflict leaves publication failed and releases the cover guard", "screenshots": [], "details": "409 分支不误报已发布并释放 busy 标记;模拟 API。" } ] }, { "id": "publish.cover", "group": "发布、交付与运行信息", "name": "发布时当前三维截图封面", "source": { "file": "../../../unreal_tran/src/main.js", "line": 2897, "symbol": "publishToSceneLibrary" }, "implementation": "本轮修复", "boundary": "JPEG 当前视口;截图或上传失败可保留旧封面并明确提示。", "fix": "与手动保存共用封面链路,409 阻断。", "acceptance": "真实封面图对应当前模型;上传失败提示一次,旧图保留。", "verification": "PASS", "evidence": [ { "ids": [ "publish.api", "publish.cover", "publish.html", "publish.downstream" ], "file": "evidence/34-publish-fixed-version.json", "status": "PASS", "testTitle": "直接发布同步截图封面、固定版本目录与真实离线步骤 HTML", "recordedAt": "2026-09-06T08:20:51.047Z", "kind": "e2e+api", "title": "直接发布同步截图封面、固定版本目录与真实离线步骤 HTML", "screenshots": [], "details": "目录可取得固定发布版本及同版封面;HTML已真实打开并确认演示边界,未声明下游动画执行或完整离线三维运行。" }, { "ids": [ "publish.api", "publish.cover", "feedback.toast" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "API publish supplies current cover, blocks duplicate publication and reports preserved old cover once", "screenshots": [], "details": "实际发布宿主方法传封面、重入阻断和上传失败只提示一次;模拟 API,不是实际服务响应。" }, { "ids": [ "project.conflict", "publish.api", "publish.cover" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "API conflict leaves publication failed and releases the cover guard", "screenshots": [], "details": "409 分支不误报已发布并释放 busy 标记;模拟 API。" } ] }, { "id": "publish.html", "group": "发布、交付与运行信息", "name": "导出步骤演示 HTML", "source": { "file": "../../../unreal_tran/src/main.js", "line": 3011, "symbol": "exportStandaloneHtml" }, "implementation": "本轮澄清", "boundary": "工程数据、静态截图和步骤演示;不包含 GLB、不执行真实三维交互。", "fix": "生成面板和按钮移除完整离线运行的误导。", "acceptance": "下载可打开、步骤导航可用,醒目标明不含三维模型。", "verification": "PASS", "evidence": [ { "ids": [ "publish.api", "publish.cover", "publish.html", "publish.downstream" ], "file": "evidence/34-publish-fixed-version.json", "status": "PASS", "testTitle": "直接发布同步截图封面、固定版本目录与真实离线步骤 HTML", "recordedAt": "2026-09-06T08:20:51.047Z", "kind": "e2e+api", "title": "直接发布同步截图封面、固定版本目录与真实离线步骤 HTML", "screenshots": [], "details": "目录可取得固定发布版本及同版封面;HTML已真实打开并确认演示边界,未声明下游动画执行或完整离线三维运行。" } ] }, { "id": "publish.downstream", "group": "发布、交付与运行信息", "name": "下游读取模型固定版本", "source": { "file": "../../../unreal_tran/src/model-content-api.js", "line": 1159, "symbol": "publishDocument" }, "implementation": "现有边界", "boundary": "已发布模型作为资源引用;下游自动执行模型 timeline/blueprint 尚无统一运行桥接。", "fix": "本轮不将资源加载成功等同动作自动运行成功。", "acceptance": "固定引用/可见性单独测;自动动作执行列为待确认范围。", "verification": "PASS", "evidence": [ { "ids": [ "publish.api", "publish.cover", "publish.html", "publish.downstream" ], "file": "evidence/34-publish-fixed-version.json", "status": "PASS", "testTitle": "直接发布同步截图封面、固定版本目录与真实离线步骤 HTML", "recordedAt": "2026-09-06T08:20:51.047Z", "kind": "e2e+api", "title": "直接发布同步截图封面、固定版本目录与真实离线步骤 HTML", "screenshots": [], "details": "目录可取得固定发布版本及同版封面;HTML已真实打开并确认演示边界,未声明下游动画执行或完整离线三维运行。" } ] }, { "id": "feedback.log", "group": "发布、交付与运行信息", "name": "运行日志", "source": { "file": "../../../unreal_tran/src/main.js", "line": 3116, "symbol": "log(message" }, "implementation": "已有实现", "boundary": "本次内存运行日志,最多 160 条;不是服务端审计日志。", "fix": "错误信息转义,保留清晰运行结果。", "acceptance": "选中/运行/保存产生相应日志,不要求刷新保留。", "verification": "PASS", "evidence": [ { "ids": [ "project.name", "project.autosave", "view.orbit", "view.focus", "view.panels", "view.preview-step", "feedback.log", "feedback.stats", "property.identity", "property.binding", "selection.scope", "component.inspect" ], "file": "evidence/46-navigation-panels-autosave.json", "status": "PASS", "testTitle": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "recordedAt": "2026-09-06T08:23:54.656Z", "kind": "e2e+api", "title": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "screenshots": [], "details": "真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。" } ] }, { "id": "feedback.stats", "group": "发布、交付与运行信息", "name": "节点 / 三角面 / FPS 等状态", "source": { "file": "../../../unreal_tran/src/main.js", "line": 3094, "symbol": "updateStats" }, "implementation": "现有边界", "boundary": "节点与三角面统计、当前 FPS;内存为估算值,不是精确浏览器内存剖析。", "fix": "无本轮性能仪表重构。", "acceptance": "载入模型后统计有合理变化,状态不作为性能验收数据。", "verification": "PASS", "evidence": [ { "ids": [ "project.name", "project.autosave", "view.orbit", "view.focus", "view.panels", "view.preview-step", "feedback.log", "feedback.stats", "property.identity", "property.binding", "selection.scope", "component.inspect" ], "file": "evidence/46-navigation-panels-autosave.json", "status": "PASS", "testTitle": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "recordedAt": "2026-09-06T08:23:54.656Z", "kind": "e2e+api", "title": "补充视口与工作区:鼠标镜头、聚焦、面板、预览导航、日志与自动保存", "screenshots": [], "details": "真实鼠标镜头操作;聚焦只验证相机目标;真实30秒自动保存,无测试加速定时器;统计内存估值未当作性能结论。" } ] }, { "id": "feedback.toast", "group": "发布、交付与运行信息", "name": "成功 / 错误短消息", "source": { "file": "../../../unreal_tran/src/main.js", "line": 3117, "symbol": "toast(message" }, "implementation": "已有实现", "boundary": "瞬时反馈;保存/发布成功以接口完成为准。", "fix": "封面失败不伪报全部成功。", "acceptance": "失败出现一次明确提示,自动消失,不形成重复常驻错误条。", "verification": "PASS", "evidence": [ { "ids": [ "project.conflict", "project.save", "feedback.toast" ], "file": "evidence/32-save-failure-recovery.json", "status": "PASS", "testTitle": "保存请求超时和 409 保留恢复副本,解除故障后可重试", "recordedAt": "2026-09-06T08:20:43.214Z", "kind": "e2e+api", "title": "保存请求超时和 409 保留恢复副本,解除故障后可重试", "screenshots": [], "details": "真实页面保存及真实服务端重试;失败由浏览器定向注入 timedout/409,非宣称自然发生的服务故障。" }, { "ids": [ "publish.api", "publish.cover", "feedback.toast" ], "file": "validation.json", "status": "PASS", "kind": "unit", "title": "API publish supplies current cover, blocks duplicate publication and reports preserved old cover once", "screenshots": [], "details": "实际发布宿主方法传封面、重入阻断和上传失败只提示一次;模拟 API,不是实际服务响应。" } ] } ] }