You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

13 lines
209 KiB

  1. <!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>场景编辑器 · 全功能闭环审查</title><style>
  2. :root{--ink:#173443;--muted:#607782;--line:#dbe5e9;--accent:#087e83;--paper:#f2f6f7}*{box-sizing:border-box}body{margin:0;background:var(--paper);font:15px/1.65 "Microsoft YaHei","Segoe UI",sans-serif;color:var(--ink)}a{color:#087786;text-underline-offset:3px}header{padding:54px max(5vw,24px);background:linear-gradient(120deg,#102f44,#07594f);color:#fff}header small{letter-spacing:3px;color:#9cced1}h1{font-size:42px;line-height:1.3;margin:17px 0}header p{max-width:850px;color:#d2e5e8}nav{display:flex;flex-wrap:wrap;gap:22px;background:#fff;border-bottom:1px solid var(--line);padding:15px max(5vw,24px);position:sticky;top:0;z-index:5}nav a{text-decoration:none;color:#3c5c69;font-weight:600}main{max-width:1510px;margin:auto;padding:28px 30px 55px}section{scroll-margin-top:85px;margin-bottom:38px}h2{font-size:26px;margin:8px 0 17px}h3{font-size:19px;margin:0 0 13px}.eyebrow{color:#6c8690;font-size:12px;letter-spacing:2px}.panel{background:#fff;border:1px solid var(--line);border-radius:12px;padding:23px;margin:17px 0}.notice{border-left:4px solid #c69220;padding:19px 24px;background:#fff5d9;border-radius:9px}.notice.fail{background:#fff0ef;border-color:#cb574e}.notice.pass{background:#e7f7ed;border-color:#23845a}.notice strong{font-size:22px}.notice p{margin:8px 0 0}.badge{display:inline-block;white-space:nowrap;font:600 12px/1.4 "Microsoft YaHei",sans-serif;border-radius:20px;padding:5px 9px;background:#edf1f3;color:#667883}.badge.pass{background:#ddf3e8;color:#186d4e}.badge.fail{background:#f9e2df;color:#a63832}.badge.warn,.badge.pending{background:#fff1ce;color:#926614}.stats,.flow{display:grid;grid-template-columns:repeat(4,1fr);gap:15px;margin:22px 0}.stats>div{background:#fff;border:1px solid var(--line);padding:20px;border-radius:10px}.stats b{font-size:30px;display:block}.stats small,.muted{color:var(--muted)}.flow{grid-template-columns:repeat(5,1fr)}.flow article{padding:19px;background:#fff;border:1px solid var(--line);border-radius:10px}.flow b{color:var(--accent)}.flow p{font-size:13px}.twocol{display:grid;grid-template-columns:1fr 1fr;gap:18px}.scroll{overflow:auto;border:1px solid var(--line);border-radius:9px}table{border-collapse:collapse;width:100%;font-size:13px;text-align:left}th{background:#eaf0f3;padding:12px;white-space:nowrap}td{border-top:1px solid var(--line);padding:13px;vertical-align:top;min-width:130px}tbody tr:nth-child(even){background:#f9fbfc}.matrix td:first-child{min-width:220px}.matrix td:nth-child(3),.matrix td:nth-child(4){min-width:240px}.matrix code,.matrix small{display:block;font-size:11px;overflow-wrap:anywhere;margin-top:6px;color:var(--muted)}.matrix strong{display:block}.matrix-group{margin-top:25px}.filters{display:flex;gap:12px;flex-wrap:wrap;margin:17px 0}.filters input,.filters select{padding:9px 12px;border:1px solid #cad9df;border-radius:7px;background:white;font:inherit}.filters input{flex:1;min-width:260px}.gallery{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}figure{margin:0;border:1px solid var(--line);border-radius:11px;overflow:hidden;background:white}figure img{width:100%;aspect-ratio:16/9;object-fit:contain;background:#dce7e9;display:block}figcaption{padding:18px;font-size:13px}figcaption p{margin:10px 0}.links{display:flex;flex-wrap:wrap;gap:12px 24px}.decision{border-top:4px solid #c79231}.decision dt{font-weight:bold;margin-top:12px}.decision dd{margin:4px 0;color:var(--muted)}details summary{cursor:pointer;color:#147586}.hidden{display:none!important}footer{border-top:1px solid var(--line);padding-top:20px;font-size:12px;color:var(--muted)}dialog{border:0;border-radius:10px;padding:0;background:#102a3a;color:white;width:min(96vw,1900px);max-width:none}dialog::backdrop{background:#04131de6}dialog img{display:block;width:100%;max-height:87vh;object-fit:contain}.viewer-bar{display:flex;justify-content:space-between;gap:12px;padding:13px 18px}.viewer-bar a{color:#aae0e7}.viewer-bar button{background:transparent;border:1px solid #799ca8;border-radius:6px;color:white;padding:5px 12px;margin-left:14px}@media(max-width:1050px){.flow,.gallery{grid-template-columns:repeat(2,1fr)}.stats{grid-template-columns:repeat(2,1fr)}.twocol{grid-template-columns:1fr}}@media(max-width:620px){main{padding:20px 12px}h1{font-size:30px}.flow,.gallery{grid-template-columns:1fr}nav{gap:10px;font-size:12px}}@media print{nav,.filters{display:none}.scroll{overflow:visible}header{padding:25px}.gallery{grid-template-columns:1fr 1fr}}
  3. </style></head><body><header><small>SCENE AUTHORING · CLOSURE REVIEW</small><h1>场景编辑器<br>全功能闭环审查与验收</h1><p>资料 → 产品原型 → 设计 → 开发 → 测试。先核准实际入口与全部可达操作,再以真实模型、保存版本、错误恢复和截图证明闭环;每项实现与验证分别记录。</p></header><nav><a href="#overview">验收概况</a><a href="#materials">01 资料</a><a href="#prototype">02 原型</a><a href="#design">03 设计</a><a href="#development">04 开发</a><a href="#testing">05 测试</a><a href="#boundaries">待确认范围</a><a href="#screenshots">截图</a></nav><main>
  4. <section id="overview"><div class="notice pass"><strong>当前列明功能已完成记录验证</strong><p><span class="badge pass">已验证</span> · 2026/9/6 18:36:39(北京时间)</p></div><div class="stats"><div><span>默认入口功能</span><b>119</b><small>未挂载 Vue 工作区不纳入分母</small></div><div><span>逐项证据匹配</span><b>119 / 119</b><small>失败 0 · 待验证 0</small></div><div><span>当前真实 E2E</span><b>33 / 33</b><small>构建、API 和单测不计入 E2E</small></div><div><span>原始截图</span><b>52</b><small>需图注与本轮附件哈希一致</small></div></div><p class="muted">112 项功能由本轮浏览器证据验证,7 项由聚焦单元 / 合同证据验证;未验证 0 项。</p><p class="muted">构建与类型检查:1 / 1 项检查;后端构建与测试:373 / 373 项测试;Node 合同与单元:138 / 138 项测试。</p><p class="muted">数据清理已验证;临时项目 167 / 167、账号 28 / 28、角色 34 / 34 已清理;原件创作内容、版本、资产与依赖 4 / 4 保持不变。</p></section>
  5. <section id="materials"><div class="eyebrow">01 / 资料</div><h2>参考资料中的通用交互要求</h2><div class="panel"><p>沿用前一轮已核对的两份 PDF(共 11 页)。资料中的部件选择、操作步骤、交互反馈和结果追踪,在本轮映射为场景对象摆放、部件能力、稳定资源引用、保存发布及后续训练选用。</p><p><a href="../../../../%E6%96%87%E6%A1%A3/%E7%BB%B4%E4%BF%AE%E8%AE%AD%E7%BB%83%E5%86%85%E5%AE%B9%E8%AE%BE%E8%AE%A1.pdf">维修训练内容设计.pdf</a> · <a href="../../../../%E6%96%87%E6%A1%A3/%E7%94%B5%E5%99%A8%E6%8E%A7%E5%88%B6%E7%B3%BB%E7%BB%9F%E6%95%85%E9%9A%9C%E7%BB%B4%E4%BF%AE%E8%84%9A%E6%9C%AC.pdf">电器控制系统故障维修脚本.pdf</a> · <a href="../scene-live-20260905/index.html">前轮场景实测报告(历史参考)</a></p><p>前轮曾把对象树/接口成功误认为狐狸可见,已撤回旧完整通过结论并补视觉验证。本轮要求截图中实际辨认模型,不能仅凭树节点、依赖字段或成功提示判断加载通过。</p></div></section>
  6. <section id="prototype"><div class="eyebrow">02 / 产品原型</div><h2>以当前路由挂载的场景编辑器为准</h2><div class="flow"><article><b>场景列表</b><p>ContentProjectsView 的 SCENE 入口。</p></article><article><b>真实路由</b><p>/content/scenes/:id/edit。</p></article><article><b>Vue 宿主</b><p>LegacyScenePageView 管理权限、项目、恢复和 FIFO。</p></article><article><b>原型运行时</b><p>demo-src/scene-editor.js 真实 DOM + Three.js。</p></article><article><b>内容 API</b><p>项目版本、封面、受控资产与发布依赖。</p></article></div><div class="panel"><p><a href="../../../unreal_tran_web/src/router/index.ts">index.ts:123 · 场景制作:列表走真实内容制作 API</a> · <a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:441 · async function mountLegacy</a> · <a href="../../../unreal_tran_web/src/features/scene-legacy/runtime.ts">runtime.ts:96 · export function legacySceneRuntimeContext</a></p><p>SceneEditorView → SceneEditorWorkspace 的正式实现仍在源码中,但当前场景路由未挂载。它的额外控件列在“待确认范围”中,不能作为本轮默认入口的已实现/已验证功能。</p></div></section>
  7. <section id="design"><div class="eyebrow">03 / 设计</div><h2>先约定保存、权限与实例边界</h2><div class="twocol"><article class="panel"><h3>数据闭环</h3><p>edit3dv4.scene 文档保存对象变换、语义、模型部件覆盖和 rendering;模型以 SCENE_MODEL 固定项目/版本依赖,场景自导入 GLB 使用本项目资产编码。blob 是临时渲染地址。</p><p>手动保存同时生成当前视口 JPEG 封面;截图失败保留旧封面并提示,409 不覆盖。800ms 本地恢复缓冲不等于自动写服务器。</p></article><article class="panel"><h3>权限与生命周期</h3><p>content.scene 控制页面,create/update/delete/publish 控制实际动作。发布只有权限者可以发布已存版本;编辑后发布必须先完成受 update 保护的保存。</p><p>切标签保留编辑状态并暂停渲染;真正离开/关闭/切角色确认未存修改。资源上传、保存和发布均须完整结束或明确阻止离开。</p></article></div><div class="panel"><h3>基线风险记录</h3><p class="muted">以下是本轮修复前源码发现,不能代替实际失败复现。已修复时须关联 fixes.json 与新测试,历史风险描述保留追溯。</p><div class="scroll"><table><thead><tr><th>风险</th><th>基线观察</th><th>受影响功能</th></tr></thead><tbody><tr><td>细分权限与旧 runtime 权限码不一致</td><td>宿主按 content.scene.update,runtime 基线仍含 content.update。</td><td>permission.update、permission.readonly</td></tr><tr><td>新建重置当前工程身份</td><td>基线新建加载当前模板并 reset;需要真实创建 API 与未保存确认。</td><td>project.new</td></tr><tr><td>透视与预览写入守卫不完整</td><td>透视按钮缺实际分支;预览需要同时约束按钮和快捷键。</td><td>view.perspective、preview.lock</td></tr><tr><td>异步导入和历史恢复的失败回滚</td><td>缺失受控资源可能在清空原对象后失败;历史索引需要和真实恢复结果一致。</td><td>json.missing-resource、history.failure</td></tr><tr><td>显式资产编码不应静默回退</td><td>错误 assetCode 回退为首个 READY_MODEL_FILE 可能加载错误模型。</td><td>validation.references、publish.fixed-model</td></tr><tr><td>底部日志和绑定状态存在固定文本</td><td>静态日志不能称实际记录;恒定已绑定不能替代设备字段校验。</td><td>panel.binding、panel.logs</td></tr><tr><td>缓存暂停后键盘监听需实际隔离</td><td>只暂停 RAF 不保证后台编辑器不响应全局键盘。</td><td>lifecycle.cache</td></tr><tr><td>原场景 160 的既有模板与对象组合不一致</td><td>主代理只读核实原件 presetId=workshop-real,objects 仍包含 terrain;这是既往切模板后持久化的旧组合。未推断用户意图改写原件。本轮仅在独立副本通过真实模板替换验证新保存的环境和对象一致。</td><td>project.load、template.switch-cancel、template.workshop-real、template.terrain-1</td></tr><tr><td>来源部件 ID 被多实例共享导致点选串对象</td><td>本轮双实例验收发现第二实例部件可能复用第一实例 sceneId,选择和覆盖误指向第一实例;修复需按所属根对象隔离身份,同时保留旧单实例兼容与稳定来源标识。</td><td>selection.tree、selection.viewport、part.transform、part.soft-delete</td></tr><tr><td>新建选择放弃后创建失败提前丢失 dirty</td><td>最终复核发现放弃旧稿不能在新项目创建前提交,否则POST503会留下旧页面但失去未保存标记和恢复保护。本轮补为创建且导航成功后才提交放弃,并重新加载原缓存内容。</td><td>project.new、recovery.autobuffer、lifecycle.cache</td></tr><tr><td>仅以资产 ID 引用时缓存身份不完整</td><td>最终复核发现省略code和URI、仅提供sourceAssetId/assetId的场景资产可能共用不完整缓存键,导致先前有效结果绕过另一引用校验。补修同时覆盖下载缓存与预检去重。</td><td>validation.references、glb.references</td></tr><tr><td>并行 GLB 恢复按完成先后改变对象顺序</td><td>第二完整轮在保存卡车双实例后重开时复现:各 GLB 加载结束后直接追加根对象,导致同源实例在对象列表中的先后随完成时间变化。修复后等待全部恢复完成,再按原文档顺序提交,拆分数组内部顺序保持;30/1/10ms 延迟合同验证实际完成顺序不同仍能稳定序列化。</td><td>project.load、selection.tree</td></tr></tbody></table></div></div></section>
  8. <section id="development"><div class="eyebrow">04 / 开发</div><h2>默认入口全功能覆盖矩阵</h2><p><a href="feature-catalog.json">稳定功能 ID</a> · <a href="coverage.json">逐项证据</a> · <a href="coverage-plan.md">测试分层约定</a>。每项分别说明实现状态、闭环边界与实际证据;各模板与资源单独列明。</p><div class="filters"><input id="search" type="search" placeholder="搜索功能、ID、边界、验收要求"><select id="state" aria-label="验证状态"><option value="all">全部状态</option><option value="PENDING">待验证</option><option value="PASS">已验证</option><option value="FAIL">失败</option><option value="WARN">需复核</option></select><span id="visible">119 项</span></div><article class="matrix-group" data-group><h3>工程入口与顶部命令</h3><div class="scroll"><table class="matrix"><thead><tr><th>功能 / 执行路径</th><th>实现 / 对应修复</th><th>闭环边界</th><th>证据与验收要求</th></tr></thead><tbody><tr data-state="PASS"><td><strong>按真实项目 ID 打开已存场景</strong><code>project.load</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:547 · async function loadProject</a></small></td><td>本轮修复已完成验证<small>新实例部件 sceneId 包含所属根对象身份,不再直接复用来源模型部件 ID;源 interaction/editor 标识保留。旧单实例 ID 保留,旧文档重复 ID 仅按文档顺序迁移后续冲突实例并同步 override。第二完整轮进一步发现 GLB 并发完成顺序会改变根对象顺序;现等待 allSettled 成功后按原文档顺序排列恢复根对象,拆分模板返回数组内部顺序和既有对象均保留,同源两实例不再随网速互换列表位置。新增延迟 30/1/10ms 乱序加载合同,验证序列化仍按文档顺序;最终浏览器验证独立记录。</small></td><td>服务端 content 与版本是真值;不得以模板种子覆盖有效旧工程。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><small><a href="validation.json">successful parallel restores retain document order after delayed GLBs and split-object batches finish out of order</a> · unit</small><details><summary>验收要求</summary><p>独立新会话打开,真实模型可见、固定版本及对象字段一致。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>项目或挂载加载失败后重新加载</strong><code>project.retry</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:76 · mountError</a></small></td><td>本轮修复已完成验证<small>前端目录和资源解析同时匹配指定项目、版本、code、id、URI、READY MODEL_FILE;错误引用不回退首文件或本地缓存。仅含 sourceAssetId/assetId 的本项目资源也必须将 ID 纳入缓存/去重键,防止已缓存有效引用掩盖另一个错误ID;失败请求缓存可重试。复制场景保留旧伪 URI 但只读取当前副本资产。</small></td><td>错误详情自动消退,保留中性失败状态与重试按钮。</td><td><span class="badge pass">已验证</span><small><a href="evidence/loading-retry.json">加载失败重试与加载互斥:不展示可写半场景、恢复网络后模型完整加载</a> · browser</small><small><a href="validation.json">a failed fixed-version lookup is evicted and the same reference can be retried</a> · unit</small><details><summary>验收要求</summary><p>注入加载失败;无可写半场景;恢复网络后按钮重试成功。</p><p>GET503只留中性失败和重试;延迟受控下载时禁用写入/导出/预览并阻止关闭,释放后真实狐狸加载。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>当前场景名称与项目说明按钮</strong><code>project.title</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:599 · project: () =&gt; this.toast</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>现有标题显示服务端名称;说明按钮不是重命名输入框。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><details><summary>验收要求</summary><p>长名称完整 title 正确;点击说明反映内容 API 管理。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p></details></td></tr><tr data-state="PASS"><td><strong>顶部新建独立工程</strong><code>project.new</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:575 · new: () =&gt;</a></small></td><td>本轮修复已完成验证<small>顶部新建委托宿主命名与创建 API,先确认未保存内容。选择放弃也要等创建及导航成功后才清除原 dirty/恢复稿;创建503仍保留原稿。成功后刷新原缓存页,避免再次打开时将已放弃修改冒称已保存。取消、失败不清原场景,成功导航到新项目 ID,并有创建并发守卫。</small></td><td>基线代码重置当前模板;本轮由宿主接真实创建 API,必须保留原项目。</td><td><span class="badge pass">已验证</span><small><a href="evidence/new-project.json">新建独立场景:取消、失败、保护原工程以及成功新 ID</a> · browser</small><details><summary>验收要求</summary><p>取消/失败不清原工程;成功新 ID;保存重开与列表对应。</p><p>取消命名、创建503保留原工程、创建成功新ID、保存刷新与原工程内容核对。</p></details></td></tr><tr data-state="PASS"><td><strong>返回场景制作列表并保留场景标签</strong><code>project.back</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:598 · back: () =&gt;</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>平台约定返回列表等同切换标签,保留场景缓存和未存稿,不弹关闭确认;真正关闭标签才询问。</td><td><span class="badge pass">已验证</span><small><a href="evidence/publish-fixed-version.json">发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用</a> · browser</small><details><summary>验收要求</summary><p>未存状态返回列表不写服务器、不丢场景;重新点原标签恢复输入和选择。</p><p>实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。</p></details></td></tr><tr data-state="PASS"><td><strong>保存草稿按钮</strong><code>project.save</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1874 · async saveProject()</a></small></td><td>本轮修复已完成验证<small>完整 PUT 回写原有相同 SCENE_MODEL 三元组时沿用场景授权边界;新增或改版依赖仍要求来源模型可见及范围权限,不扩大模型页面访问权。</small></td><td>只有服务器保存完成才显示成功;本地恢复缓冲不是自动入库。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><small><a href="evidence/save-concurrency.json">延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突</a> · browser</small><small><a href="evidence/scene-multi-session.json">双标签各自修改场景:保存和明确丢弃当前页均保留另一页真实恢复稿</a> · browser</small><details><summary>验收要求</summary><p>编辑后真实 PUT,刷新值一致,version 前进一次。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p><p>延迟真实PUT快照A、重复Ctrl+S不重复请求、期间编辑B仍dirty;再次保存刷新无旧恢复稿误报。</p></details></td></tr><tr data-state="PASS"><td><strong>Ctrl/Meta+S 保存快捷键</strong><code>project.save-key</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:469 · handleKeyboard(event)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>键盘与按钮走相同保存/权限/忙碌守卫。</td><td><span class="badge pass">已验证</span><small><a href="evidence/save-concurrency.json">延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突</a> · browser</small><small><a href="evidence/truck-part-history.json">牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致</a> · browser</small><details><summary>验收要求</summary><p>实际按键触发一次保存;表单内与非表单焦点分别核查。</p><p>延迟真实PUT快照A、重复Ctrl+S不重复请求、期间编辑B仍dirty;再次保存刷新无旧恢复稿误报。</p><p>实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。</p></details></td></tr><tr data-state="PASS"><td><strong>保存失败保留修改和恢复稿</strong><code>project.save-failure</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1874 · async saveProject()</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>网络失败或业务拒绝不能清 dirty;不弹虚假保存成功。</td><td><span class="badge pass">已验证</span><small><a href="evidence/save-cover-failure.json">保存失败与封面降级:503 保留草稿,封面上传失败保留旧图,恢复后可重试</a> · browser</small><details><summary>验收要求</summary><p>定向 503 后对象/字段保留,恢复后重试与刷新成功。</p><p>正文PUT503不清草稿;封面上传503保留旧图但正文可保存;恢复服务后保存刷新。截图异常和409另由聚焦合同验证。</p></details></td></tr><tr data-state="PASS"><td><strong>409 乐观锁冲突保护</strong><code>project.conflict</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:432 · function describeSaveError</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>服务器最新版本不覆盖;恢复副本可下载/隔离。</td><td><span class="badge pass">已验证</span><small><a href="evidence/real-recovery-conflict.json">同版本恢复与 409:真实未保存草稿重载、服务器新版本保护与隔离下载</a> · browser</small><small><a href="validation.json">upload conflict propagates without any content PUT</a> · unit</small><details><summary>验收要求</summary><p>独立副本制造版本冲突;验证失败提示、旧版本不覆盖与本地稿保留。</p><p>真实未存输入产生恢复稿,同版本恢复不自动入库;另一版本先保存后409拒绝覆盖,旧稿隔离下载。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>重复保存与保存中继续编辑</strong><code>project.concurrent-save</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1887 · const saveRevision = this.changeRevision</a></small></td><td>本轮修复已完成验证<small>开始保存时取消之前安排的恢复稿定时器,避免它覆盖宿主保存快照 nonce;保存中的后续编辑仍单独保留。</small></td><td>提交快照与后续编辑分开;后续修改不能随早先请求完成清掉。</td><td><span class="badge pass">已验证</span><small><a href="evidence/save-concurrency.json">延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突</a> · browser</small><details><summary>验收要求</summary><p>延迟 PUT、连点保存并继续改值;一次旧提交、新修改仍 dirty。</p><p>延迟真实PUT快照A、重复Ctrl+S不重复请求、期间编辑B仍dirty;再次保存刷新无旧恢复稿误报。</p></details></td></tr></tbody></table></div></article><article class="matrix-group" data-group><h3>工程文件与资源导入</h3><div class="scroll"><table class="matrix"><thead><tr><th>功能 / 执行路径</th><th>实现 / 对应修复</th><th>闭环边界</th><th>证据与验收要求</th></tr></thead><tbody><tr data-state="PASS"><td><strong>导出当前场景 JSON</strong><code>json.export</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:578 · exportProject()</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>导出 edit3dv4.scene、对象/部件覆盖、渲染和稳定资源引用;不冒称自包含 GLB 包。</td><td><span class="badge pass">已验证</span><small><a href="evidence/json-glb-roundtrip.json">工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返</a> · browser</small><small><a href="evidence/readonly.json">只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景</a> · browser</small><details><summary>验收要求</summary><p>打开下载 JSON 核对内容、结构和无临时 blob 依赖。</p><p>真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。</p><p>只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。</p></details></td></tr><tr data-state="PASS"><td><strong>导入有效场景工程 JSON</strong><code>json.import</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:462 · importProject(event)</a></small></td><td>本轮修复已完成验证<small>模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。</small></td><td>替换当前编辑文档后仍需保存;不得写入另一个项目身份。</td><td><span class="badge pass">已验证</span><small><a href="evidence/json-glb-roundtrip.json">工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返</a> · browser</small><details><summary>验收要求</summary><p>导出后实际文件导入、保存、重开;对象与渲染参数往返一致。</p><p>真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。</p></details></td></tr><tr data-state="PASS"><td><strong>非法 JSON / 错误 schema 拒绝</strong><code>json.invalid</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:462 · importProject(event)</a></small></td><td>本轮修复已完成验证<small>模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。</small></td><td>解析或结构错误不得清空当前场景。</td><td><span class="badge pass">已验证</span><small><a href="evidence/json-glb-roundtrip.json">工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返</a> · browser</small><details><summary>验收要求</summary><p>非法 JSON 与非场景文档各一次,原工程和模型仍可编辑。</p><p>真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。</p></details></td></tr><tr data-state="PASS"><td><strong>JSON 缺失/错误受控引用拒绝与回滚</strong><code>json.missing-resource</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:2016 · async restoreImportedObject</a></small></td><td>本轮修复已完成验证<small>模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。;前端目录和资源解析同时匹配指定项目、版本、code、id、URI、READY MODEL_FILE;错误引用不回退首文件或本地缓存。仅含 sourceAssetId/assetId 的本项目资源也必须将 ID 纳入缓存/去重键,防止已缓存有效引用掩盖另一个错误ID;失败请求缓存可重试。复制场景保留旧伪 URI 但只读取当前副本资产。</small></td><td>错误资产编码不能回退为另一个模型;恢复失败不半替换。</td><td><span class="badge pass">已验证</span><small><a href="evidence/json-glb-roundtrip.json">工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返</a> · browser</small><small><a href="validation.json">a failed replacement GLB keeps the previous live scene rather than clearing an unsaved draft</a> · unit</small><small><a href="validation.json">an explicit missing code cannot fall back to the first READY asset</a> · unit</small><details><summary>验收要求</summary><p>导入显式不存在编码/版本;原对象保留或明确阻断,零错误发布。</p><p>真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>相机、灯光、雾和环境序列化往返</strong><code>json.rendering</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:857 · serializeRendering()</a></small></td><td>本轮修复已完成验证<small>透视按钮实际恢复先前观察位置;导入、加载和历史恢复存在已保存相机时不再被车辆自动聚焦覆盖。</small></td><td>当前默认 UI 不提供全部渲染编辑器;导入/保存仍须保留其文档参数。</td><td><span class="badge pass">已验证</span><small><a href="evidence/json-glb-roundtrip.json">工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返</a> · browser</small><small><a href="validation.json">loading and history restoration preserve an authored camera rather than auto-focusing a vehicle</a> · unit</small><details><summary>验收要求</summary><p>合法带渲染参数文档导入、实际画面比对、保存重开字段一致。</p><p>真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>本地 GLB 上传并加入当前场景</strong><code>glb.import</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:2122 · async importGLB(event)</a></small></td><td>本轮修复已完成验证<small>恢复显示名、设备和物模型编号使用空值保留规则,不把显式空串补回旧值;新资源序号跳过既有编号,空白编号校验按 trim 处理。</small></td><td>上传到内容服务,消费票据后解析;对象加入不等于工程已保存。</td><td><span class="badge pass">已验证</span><small><a href="evidence/json-glb-roundtrip.json">工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返</a> · browser</small><details><summary>验收要求</summary><p>真实选择小 GLB,上传/登记/可见/保存重开均完成。</p><p>真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。</p></details></td></tr><tr data-state="PASS"><td><strong>非 GLB / 损坏 GLB / 上传失败处理</strong><code>glb.invalid</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:2122 · async importGLB(event)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>上传或解析失败不插入占位对象;原场景应保留,提示可重试。</td><td><span class="badge pass">已验证</span><small><a href="evidence/json-glb-roundtrip.json">工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返</a> · browser</small><details><summary>验收要求</summary><p>错误格式及损坏文件、上传 503;验证对象数/旧资产/后续成功导入。</p><p>真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。</p></details></td></tr><tr data-state="PASS"><td><strong>资源加载/上传期间操作互斥</strong><code>glb.pending</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1411 · setModelLoading(loading</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>加载中的模型不能被半保存、导出或发布;不能离开造成旧请求污染新项目。</td><td><span class="badge pass">已验证</span><small><a href="evidence/loading-retry.json">加载失败重试与加载互斥:不展示可写半场景、恢复网络后模型完整加载</a> · browser</small><small><a href="validation.json">replacement failure waits for other started loads before restoring the original scene</a> · unit</small><small><a href="validation.json">an upload finishing after a project switch cannot register into the new session</a> · unit</small><details><summary>验收要求</summary><p>延迟下载/上传,操作禁用与待完成状态一致,完成后恢复。</p><p>GET503只留中性失败和重试;延迟受控下载时禁用写入/导出/预览并阻止关闭,释放后真实狐狸加载。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>本项目 GLB 稳定资产引用</strong><code>glb.references</code><small><a href="../../../unreal_tran_web/src/features/scene-legacy/serverAssetStore.ts">serverAssetStore.ts:113 · async put(blob</a></small></td><td>本轮修复已完成验证<small>前端目录和资源解析同时匹配指定项目、版本、code、id、URI、READY MODEL_FILE;错误引用不回退首文件或本地缓存。仅含 sourceAssetId/assetId 的本项目资源也必须将 ID 纳入缓存/去重键,防止已缓存有效引用掩盖另一个错误ID;失败请求缓存可重试。复制场景保留旧伪 URI 但只读取当前副本资产。;新增 SceneResourceReferenceValidator 检查 objects 和递归 splitParts;自有/固定版本资源的所有显式引用必须指向同一 READY 模型文件,错误引用在发布/提交前阻断,保留受控的内置资源和旧引用兼容。</small></td><td>自导入使用 content://asset/scene/{projectId}/{assetCode} 和 READY 资产;blob 只供运行。</td><td><span class="badge pass">已验证</span><small><a href="evidence/json-glb-roundtrip.json">工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返</a> · browser</small><small><a href="validation.json">copied scene keeps its original pseudo URI but reads its own copied asset by stable code</a> · unit</small><details><summary>验收要求</summary><p>重新会话受控下载成功,主对象/资产身份一致,无本地缓存依赖。</p><p>真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr></tbody></table></div></article><article class="matrix-group" data-group><h3>左侧目录与资源放置</h3><div class="scroll"><table class="matrix"><thead><tr><th>功能 / 执行路径</th><th>实现 / 对应修复</th><th>闭环边界</th><th>证据与验收要求</th></tr></thead><tbody><tr data-state="PASS"><td><strong>场景大纲 / 资源库 / 场景模板切换</strong><code>panel.left-tabs</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:540 · const leftTab =</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>切面板不写业务文档;筛选状态行为需一致。</td><td><span class="badge pass">已验证</span><small><a href="evidence/template-confirm.json">模板搜索与未保存切换确认保留和放弃</a> · e2e</small><details><summary>验收要求</summary><p>三页签均可达,无 DOM 异常或资源目录中断。</p><p>中文/无匹配/清空搜索;真实取消保留对象,真实确认放弃并切换空白模板后保存。</p></details></td></tr><tr data-state="PASS"><td><strong>对象/部件名称搜索与清空</strong><code>search.tree</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:627 · renderTree()</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>根名与部件名可命中;无匹配状态应清楚,非删除对象。</td><td><span class="badge pass">已验证</span><small><a href="evidence/published-model-assets.json">模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开</a> · e2e</small><details><summary>验收要求</summary><p>根名/部件名/无结果/清空分别核对行和真实对象数。</p><p>API 已发布目录,狐狸双击和卡车真实拖入;固定来源版本、树根/部件搜索;卡车刚体部件隐藏保存重开,最后恢复可见。基线树没有折叠按钮,不将搜索伪称折叠。</p></details></td></tr><tr data-state="PASS"><td><strong>资源搜索与异步刷新保留筛选</strong><code>search.assets</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:742 · applyLeftFilter()</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>只筛当前目录;异步重绘不复活不匹配卡片。</td><td><span class="badge pass">已验证</span><small><a href="evidence/basic-assets.json">八种基础资源逐项放置、真实拖放、搜索和保存重开</a> · e2e</small><details><summary>验收要求</summary><p>命中、无结果、清空,目录重绘后过滤仍正确。</p><p>8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。</p></details></td></tr><tr data-state="PASS"><td><strong>模板搜索与清空</strong><code>search.templates</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:2278 · filterLeft(query)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>模板卡片与同一搜索框联动。</td><td><span class="badge pass">已验证</span><small><a href="evidence/template-confirm.json">模板搜索与未保存切换确认保留和放弃</a> · e2e</small><details><summary>验收要求</summary><p>中文模板关键字过滤、无结果和清空。</p><p>中文/无匹配/清空搜索;真实取消保留对象,真实确认放弃并切换空白模板后保存。</p></details></td></tr><tr data-state="PASS"><td><strong>真实已发布模型成果目录</strong><code>assets.catalog</code><small><a href="../../../unreal_tran_web/src/features/scene-legacy/contentApiClient.ts">contentApiClient.ts:783 · async listPublishedModels</a></small></td><td>本轮修复已完成验证<small>移除 runtime 旧 content.update 门槛,独立计算新建、更新和发布;无模型页面权限时不加载新模型目录,但既有固定依赖仍可按场景授权读取。</small></td><td>目录由 API 预加载,当前默认不以 localStorage 发布清单为真值。</td><td><span class="badge pass">已验证</span><small><a href="evidence/published-model-assets.json">模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开</a> · e2e</small><details><summary>验收要求</summary><p>显示发布模型名称/版本,草稿不冒充已发布,空态可操作。</p><p>API 已发布目录,狐狸双击和卡车真实拖入;固定来源版本、树根/部件搜索;卡车刚体部件隐藏保存重开,最后恢复可见。基线树没有折叠按钮,不将搜索伪称折叠。</p></details></td></tr><tr data-state="PASS"><td><strong>双击已发布模型放置</strong><code>assets.model-double-click</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1357 · async placeEditorAsset</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>按固定 project/version/assetCode 导入当前实例,继承发布部件和材质。</td><td><span class="badge pass">已验证</span><small><a href="evidence/published-model-assets.json">模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开</a> · e2e</small><small><a href="validation.json">fresh skinned placement reaches the requested size and floor after authored scale/pose replay</a> · unit</small><details><summary>验收要求</summary><p>狐狸和卡车分别双击加入、真实可见、保存重开固定版本。</p><p>API 已发布目录,狐狸双击和卡车真实拖入;固定来源版本、树根/部件搜索;卡车刚体部件隐藏保存重开,最后恢复可见。基线树没有折叠按钮,不将搜索伪称折叠。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>拖入已发布模型放置</strong><code>assets.model-drop</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:461 · handleDrop(event)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>拖放实际命中地平面;与双击共用受控资产链路。</td><td><span class="badge pass">已验证</span><small><a href="evidence/published-model-assets.json">模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开</a> · e2e</small><small><a href="validation.json">fresh skinned placement reaches the requested size and floor after authored scale/pose replay</a> · unit</small><details><summary>验收要求</summary><p>真实指针拖放到不同位置,坐标/实例数/固定引用准确。</p><p>API 已发布目录,狐狸双击和卡车真实拖入;固定来源版本、树根/部件搜索;卡车刚体部件隐藏保存重开,最后恢复可见。基线树没有折叠按钮,不将搜索伪称折叠。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>双击基础资源放置</strong><code>assets.basic-double-click</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:429 · onRootDblClick</a></small></td><td>本轮修复已完成验证<small>恢复显示名、设备和物模型编号使用空值保留规则,不把显式空串补回旧值;新资源序号跳过既有编号,空白编号校验按 trim 处理。</small></td><td>以当前轨道目标落地;每次生成独立对象身份。</td><td><span class="badge pass">已验证</span><small><a href="evidence/basic-assets.json">八种基础资源逐项放置、真实拖放、搜索和保存重开</a> · e2e</small><details><summary>验收要求</summary><p>双击生成对象,变换、保存后仍可见。</p><p>8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。</p></details></td></tr><tr data-state="PASS"><td><strong>基础资源拖入与拖放提示</strong><code>assets.basic-drop</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:461 · handleDrop(event)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>MIME 和 text/plain 回退协议一致;离开视口清提示。</td><td><span class="badge pass">已验证</span><small><a href="evidence/basic-assets.json">八种基础资源逐项放置、真实拖放、搜索和保存重开</a> · e2e</small><details><summary>验收要求</summary><p>真实拖放命中位置与 0.5m 吸附、dragleave/drop 提示复位。</p><p>8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。</p></details></td></tr><tr data-state="PASS"><td><strong>打开/返回模型编辑器入口</strong><code>assets.model-link</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:590 · open-model-editor</a></small></td><td>本轮修复已完成验证<small>模型来源按钮有来源权限时导航正确项目;无来源页面权限回到可访问列表,不将模型列表误称为来源工程编辑。</small></td><td>当前基线只有跳模型列表,来源按钮不得冒称直达正确源项目。</td><td><span class="badge pass">已验证</span><small><a href="validation.json">model source navigation locates the published owner and falls back without source permission</a> · unit</small><details><summary>验收要求</summary><p>空资源库和选中模型来源两入口,跳转/未保存保护正确。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>基础资源:液压诊断工位</strong><code>asset.diagnostic</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:123 · id: &#39;diagnostic&#39;</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>现有程序化场景资源;名称不代表已连接物理设备或真实人员。</td><td><span class="badge pass">已验证</span><small><a href="evidence/basic-assets.json">八种基础资源逐项放置、真实拖放、搜索和保存重开</a> · e2e</small><details><summary>验收要求</summary><p>实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。</p><p>8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。</p></details></td></tr><tr data-state="PASS"><td><strong>基础资源:维修装备主体</strong><code>asset.equipment</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:124 · id: &#39;equipment&#39;</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>现有程序化场景资源;名称不代表已连接物理设备或真实人员。</td><td><span class="badge pass">已验证</span><small><a href="evidence/basic-assets.json">八种基础资源逐项放置、真实拖放、搜索和保存重开</a> · e2e</small><details><summary>验收要求</summary><p>实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。</p><p>8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。</p></details></td></tr><tr data-state="PASS"><td><strong>基础资源:检修工作台</strong><code>asset.workbench</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:125 · id: &#39;workbench&#39;</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>现有程序化场景资源;名称不代表已连接物理设备或真实人员。</td><td><span class="badge pass">已验证</span><small><a href="evidence/basic-assets.json">八种基础资源逐项放置、真实拖放、搜索和保存重开</a> · e2e</small><details><summary>验收要求</summary><p>实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。</p><p>8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。</p></details></td></tr><tr data-state="PASS"><td><strong>基础资源:器材存放架</strong><code>asset.shelf</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:126 · id: &#39;shelf&#39;</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>现有程序化场景资源;名称不代表已连接物理设备或真实人员。</td><td><span class="badge pass">已验证</span><small><a href="evidence/basic-assets.json">八种基础资源逐项放置、真实拖放、搜索和保存重开</a> · e2e</small><details><summary>验收要求</summary><p>实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。</p><p>8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。</p></details></td></tr><tr data-state="PASS"><td><strong>基础资源:安全作业区域</strong><code>asset.safety-zone</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:127 · id: &#39;safety-zone&#39;</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>现有程序化场景资源;名称不代表已连接物理设备或真实人员。</td><td><span class="badge pass">已验证</span><small><a href="evidence/basic-assets.json">八种基础资源逐项放置、真实拖放、搜索和保存重开</a> · e2e</small><details><summary>验收要求</summary><p>实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。</p><p>8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。</p></details></td></tr><tr data-state="PASS"><td><strong>基础资源:视觉采集设备</strong><code>asset.camera</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:128 · id: &#39;camera&#39;</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>现有程序化场景资源;名称不代表已连接物理设备或真实人员。</td><td><span class="badge pass">已验证</span><small><a href="evidence/basic-assets.json">八种基础资源逐项放置、真实拖放、搜索和保存重开</a> · e2e</small><details><summary>验收要求</summary><p>实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。</p><p>8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。</p></details></td></tr><tr data-state="PASS"><td><strong>基础资源:训练人员占位</strong><code>asset.worker</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:129 · id: &#39;worker&#39;</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>现有程序化场景资源;名称不代表已连接物理设备或真实人员。</td><td><span class="badge pass">已验证</span><small><a href="evidence/basic-assets.json">八种基础资源逐项放置、真实拖放、搜索和保存重开</a> · e2e</small><details><summary>验收要求</summary><p>实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。</p><p>8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。</p></details></td></tr><tr data-state="PASS"><td><strong>基础资源:区域照明</strong><code>asset.light</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:130 · id: &#39;light&#39;</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>现有程序化场景资源;名称不代表已连接物理设备或真实人员。</td><td><span class="badge pass">已验证</span><small><a href="evidence/basic-assets.json">八种基础资源逐项放置、真实拖放、搜索和保存重开</a> · e2e</small><details><summary>验收要求</summary><p>实际放置可见,独立编号、位置和保存重开一致;照明应区分灯具外观与光源效果。</p><p>8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。</p></details></td></tr><tr data-state="PASS"><td><strong>场地资源:真实装备车库</strong><code>asset.vehicle-depot</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:121 · id: &#39;vehicle-depot&#39;</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>整体场地 GLB 与维修车间分开;重复放置按既有规则处理。</td><td><span class="badge pass">已验证</span><small><a href="evidence/venue-assets.json">两种整体场地加载、重复放置阻止与显式删除后替换</a> · e2e</small><details><summary>验收要求</summary><p>资源拖放或双击加载、比例/落地正常、重复放置不产生错误重叠。</p><p>车库和起伏地形真实加载并分别保存重开;已有整体场地时阻止叠加,显式删除旧场地后才能替换。</p></details></td></tr><tr data-state="PASS"><td><strong>场地资源:真实野外地形</strong><code>asset.field-terrain</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:122 · id: &#39;field-terrain&#39;</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>真实地形资源和指定落地基准;不能以程序地板代替。</td><td><span class="badge pass">已验证</span><small><a href="evidence/venue-assets.json">两种整体场地加载、重复放置阻止与显式删除后替换</a> · e2e</small><details><summary>验收要求</summary><p>加载后真实起伏地形可见,保存重开资源、地面高度一致。</p><p>车库和起伏地形真实加载并分别保存重开;已有整体场地时阻止叠加,显式删除旧场地后才能替换。</p></details></td></tr></tbody></table></div></article><article class="matrix-group" data-group><h3>全部可达场景模板</h3><div class="scroll"><table class="matrix"><thead><tr><th>功能 / 执行路径</th><th>实现 / 对应修复</th><th>闭环边界</th><th>证据与验收要求</th></tr></thead><tbody><tr data-state="PASS"><td><strong>真实装备车库</strong><code>template.depot-1</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:86 · id: &#39;depot-1&#39;</a></small></td><td>本轮修复已完成验证<small>模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。</small></td><td>模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。</td><td><span class="badge pass">已验证</span><small><a href="evidence/template-depot-1.json">场景模板实际加载、保存重开:真实装备车库</a> · e2e</small><details><summary>验收要求</summary><p>独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。</p><p>真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。</p></details></td></tr><tr data-state="PASS"><td><strong>真实野外训练场</strong><code>template.terrain-1</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:87 · id: &#39;terrain-1&#39;</a></small></td><td>本轮修复已完成验证<small>模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。</small></td><td>模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。</td><td><span class="badge pass">已验证</span><small><a href="evidence/template-terrain-1.json">场景模板实际加载、保存重开:真实野外训练场</a> · e2e</small><details><summary>验收要求</summary><p>独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。</p><p>真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。</p></details></td></tr><tr data-state="PASS"><td><strong>综合检验车间</strong><code>template.workshop-1</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:88 · id: &#39;workshop-1&#39;</a></small></td><td>本轮修复已完成验证<small>模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。</small></td><td>模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。</td><td><span class="badge pass">已验证</span><small><a href="evidence/template-workshop-1.json">场景模板实际加载、保存重开:综合检验车间</a> · e2e</small><details><summary>验收要求</summary><p>独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。</p><p>真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。</p></details></td></tr><tr data-state="PASS"><td><strong>演示维修车间(可拆分)</strong><code>template.workshop-real</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:89 · id: &#39;workshop-real&#39;</a></small></td><td>本轮修复已完成验证<small>模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。</small></td><td>模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。</td><td><span class="badge pass">已验证</span><small><a href="evidence/template-workshop-real.json">场景模板实际加载、保存重开:演示维修车间(可拆分)</a> · e2e</small><details><summary>验收要求</summary><p>独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。</p><p>真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。</p></details></td></tr><tr data-state="PASS"><td><strong>总成拆卸车间</strong><code>template.workshop-2</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:90 · id: &#39;workshop-2&#39;</a></small></td><td>本轮修复已完成验证<small>模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。</small></td><td>模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。</td><td><span class="badge pass">已验证</span><small><a href="evidence/template-workshop-2.json">场景模板实际加载、保存重开:总成拆卸车间</a> · e2e</small><details><summary>验收要求</summary><p>独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。</p><p>真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。</p></details></td></tr><tr data-state="PASS"><td><strong>部件分解车间</strong><code>template.workshop-3</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:91 · id: &#39;workshop-3&#39;</a></small></td><td>本轮修复已完成验证<small>模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。</small></td><td>模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。</td><td><span class="badge pass">已验证</span><small><a href="evidence/template-workshop-3.json">场景模板实际加载、保存重开:部件分解车间</a> · e2e</small><details><summary>验收要求</summary><p>独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。</p><p>真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。</p></details></td></tr><tr data-state="PASS"><td><strong>清洗检测车间</strong><code>template.workshop-4</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:92 · id: &#39;workshop-4&#39;</a></small></td><td>本轮修复已完成验证<small>模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。</small></td><td>模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。</td><td><span class="badge pass">已验证</span><small><a href="evidence/template-workshop-4.json">场景模板实际加载、保存重开:清洗检测车间</a> · e2e</small><details><summary>验收要求</summary><p>独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。</p><p>真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。</p></details></td></tr><tr data-state="PASS"><td><strong>复装调试车间</strong><code>template.workshop-5</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:93 · id: &#39;workshop-5&#39;</a></small></td><td>本轮修复已完成验证<small>模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。</small></td><td>模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。</td><td><span class="badge pass">已验证</span><small><a href="evidence/template-workshop-5.json">场景模板实际加载、保存重开:复装调试车间</a> · e2e</small><details><summary>验收要求</summary><p>独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。</p><p>真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。</p></details></td></tr><tr data-state="PASS"><td><strong>室外综合训练场</strong><code>template.outdoor-1</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:94 · id: &#39;outdoor-1&#39;</a></small></td><td>本轮修复已完成验证<small>模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。</small></td><td>模板场地与种子布局须有实际执行;切模板不是创建新的业务项目。</td><td><span class="badge pass">已验证</span><small><a href="evidence/template-outdoor-1.json">场景模板实际加载、保存重开:室外综合训练场</a> · e2e</small><details><summary>验收要求</summary><p>独立副本点击模板、确认/取消未存操作、真实画面、保存重开 presetId/环境/对象一致。</p><p>真实模板按钮、选中标签、环境字段、种子对象、PUT 后刷新重开;截图验证对应模板实际画面。</p></details></td></tr><tr data-state="PASS"><td><strong>未保存模板切换取消</strong><code>template.switch-cancel</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:765 · async loadPreset</a></small></td><td>本轮修复已完成验证<small>模板按钮使用模板种子,不再次读当前项目旧文档;模板/JSON 替换先保存当前运行对象,等待全部加载结束,失败恢复原环境、对象、选择和 dirty;真实确认取消保留原稿。</small></td><td>取消保留原场景、选择与 dirty;请求中不能切换半场景。</td><td><span class="badge pass">已验证</span><small><a href="evidence/template-confirm.json">模板搜索与未保存切换确认保留和放弃</a> · e2e</small><details><summary>验收要求</summary><p>有未存修改时取消/确认分别验证,取消零对象和服务端变更。</p><p>中文/无匹配/清空搜索;真实取消保留对象,真实确认放弃并切换空白模板后保存。</p></details></td></tr></tbody></table></div></article><article class="matrix-group" data-group><h3>三维视口、选择与变换</h3><div class="scroll"><table class="matrix"><thead><tr><th>功能 / 执行路径</th><th>实现 / 对应修复</th><th>闭环边界</th><th>证据与验收要求</th></tr></thead><tbody><tr data-state="PASS"><td><strong>鼠标旋转相机</strong><code>view.orbit</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:379 · new OrbitControls</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>相机轨道操作不应误选、拖动物体。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-camera-theme.json">真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</a> · 真实UI+只读投影</small><details><summary>验收要求</summary><p>实际鼠标拖动,相机变化且对象姿态不变。</p></details></td></tr><tr data-state="PASS"><td><strong>鼠标平移相机</strong><code>view.pan</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:379 · new OrbitControls</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>轨道目标平移与物体位置分开。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-camera-theme.json">真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</a> · 真实UI+只读投影</small><details><summary>验收要求</summary><p>实际右键/约定鼠标平移,目标变化且对象数据不变。</p></details></td></tr><tr data-state="PASS"><td><strong>滚轮缩放相机</strong><code>view.zoom</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:379 · new OrbitControls</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>改变观察距离,不改对象 scale。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-camera-theme.json">真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</a> · 真实UI+只读投影</small><details><summary>验收要求</summary><p>滚轮前后模型可见且对象 scale 不变。</p></details></td></tr><tr data-state="PASS"><td><strong>透视按钮</strong><code>view.perspective</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:259 · data-camera=&quot;perspective&quot;</a></small></td><td>本轮修复已完成验证<small>透视按钮实际恢复先前观察位置;导入、加载和历史恢复存在已保存相机时不再被车辆自动聚焦覆盖。</small></td><td>基线按钮无处理分支;需明确恢复相机视角,不静默无效。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-camera-theme.json">真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</a> · 真实UI+只读投影</small><details><summary>验收要求</summary><p>顶视切回透视,实际相机位置与激活样式变化。</p></details></td></tr><tr data-state="PASS"><td><strong>顶视图按钮</strong><code>view.top</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:585 · setTopView()</a></small></td><td>本轮修复已完成验证<small>透视按钮实际恢复先前观察位置;导入、加载和历史恢复存在已保存相机时不再被车辆自动聚焦覆盖。</small></td><td>仍为透视相机顶视位置,不冒称正交投影。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-camera-theme.json">真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</a> · 真实UI+只读投影</small><details><summary>验收要求</summary><p>实际点击后顶视方向与目标一致。</p></details></td></tr><tr data-state="PASS"><td><strong>聚焦选中对象</strong><code>view.focus</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:586 · focusSelected()</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>按对象真实 bounds 调整相机;无选中应提示。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-camera-theme.json">真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</a> · 真实UI+只读投影</small><details><summary>验收要求</summary><p>狐狸/卡车/环境分别聚焦可见;无选中提示不异常。</p></details></td></tr><tr data-state="PASS"><td><strong>网格显隐</strong><code>view.grid</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1643 · toggleGrid(button)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>当前视图辅助选项;不默认要求跨重开持久化。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-camera-theme.json">真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</a> · 真实UI+只读投影</small><small><a href="validation.json">copies synchronously without bounds, gizmo, grid or selection glow, then restores before JPEG encoding</a> · unit</small><details><summary>验收要求</summary><p>真实网格出现/消失、状态同步、截图封面不含网格。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>车库外壳完整显示</strong><code>view.enclosure-full</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/garage-enclosure.js">garage-enclosure.js:377 · createGarageEnclosureControls</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>完整显示屋顶/墙体/玻璃;仅车库资源存在时出现控制。</td><td><span class="badge pass">已验证</span><small><a href="evidence/truck-part-history.json">牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致</a> · browser</small><details><summary>验收要求</summary><p>实际点击、对象材料和显示恢复,状态与画面一致。</p><p>实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。</p></details></td></tr><tr data-state="PASS"><td><strong>车库外壳透明查看</strong><code>view.enclosure-xray</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/garage-enclosure.js">garage-enclosure.js:377 · createGarageEnclosureControls</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>车库多种围护材料联动;不等于普通相机透视。</td><td><span class="badge pass">已验证</span><small><a href="evidence/truck-part-history.json">牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致</a> · browser</small><details><summary>验收要求</summary><p>实际外壳透明且内部模型可辨,材质无串改。</p><p>实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。</p></details></td></tr><tr data-state="PASS"><td><strong>隐藏车库外壳</strong><code>view.enclosure-hidden</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/garage-enclosure.js">garage-enclosure.js:377 · createGarageEnclosureControls</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>隐藏围护后保留内部及地面;序列化 enclosureMode 与 dirty 契约须核对。</td><td><span class="badge pass">已验证</span><small><a href="evidence/truck-part-history.json">牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致</a> · browser</small><details><summary>验收要求</summary><p>隐藏/还原、保存重开和只读浏览行为准确。</p><p>实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。</p></details></td></tr><tr data-state="PASS"><td><strong>点击视口选中根对象/部件</strong><code>selection.viewport</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:456 · selectFromViewport(event)</a></small></td><td>本轮修复已完成验证<small>新实例部件 sceneId 包含所属根对象身份,不再直接复用来源模型部件 ID;源 interaction/editor 标识保留。旧单实例 ID 保留,旧文档重复 ID 仅按文档顺序迁移后续冲突实例并同步 override。第二完整轮进一步发现 GLB 并发完成顺序会改变根对象顺序;现等待 allSettled 成功后按原文档顺序排列恢复根对象,拆分模板返回数组内部顺序和既有对象均保留,同源两实例不再随网速互换列表位置。新增延迟 30/1/10ms 乱序加载合同,验证序列化仍按文档顺序;最终浏览器验证独立记录。</small></td><td>只命中真实可选择可见对象;拖相机超过阈值不误选。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-camera-theme.json">真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</a> · 真实UI+只读投影</small><details><summary>验收要求</summary><p>真实像素点击目标、树/属性/高亮一致,点击空白清选择。</p></details></td></tr><tr data-state="PASS"><td><strong>大纲根对象与部件选中</strong><code>selection.tree</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:546 · const treeItem =</a></small></td><td>本轮修复已完成验证<small>新实例部件 sceneId 包含所属根对象身份,不再直接复用来源模型部件 ID;源 interaction/editor 标识保留。旧单实例 ID 保留,旧文档重复 ID 仅按文档顺序迁移后续冲突实例并同步 override。第二完整轮进一步发现 GLB 并发完成顺序会改变根对象顺序;现等待 allSettled 成功后按原文档顺序排列恢复根对象,拆分模板返回数组内部顺序和既有对象均保留,同源两实例不再随网速互换列表位置。新增延迟 30/1/10ms 乱序加载合同,验证序列化仍按文档顺序;最终浏览器验证独立记录。</small></td><td>大纲显示所有可编辑实例和部件;环境锁定提示不是折叠控件。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><small><a href="validation.json">two published instances have unique tree part IDs and editing the second persists independently after reopen</a> · unit</small><small><a href="validation.json">successful parallel restores retain document order after delayed GLBs and split-object batches finish out of order</a> · unit</small><details><summary>验收要求</summary><p>树选中与视口高亮/属性联动、筛选后仍可选。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>隐藏祖先与软删除部件不可误拾取</strong><code>selection.hidden</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-selection.js">scene-selection.js:41 · chooseSceneSelection</a></small></td><td>本轮修复已完成验证<small>射线候选逐级检查父级可见性和软删除;删除根对象先恢复高亮材质再释放几何/材质,避免错误拾取及 GPU 资源残留。</small></td><td>父隐藏/子隐藏/软删除状态不能被射线重新命中。</td><td><span class="badge pass">已验证</span><small><a href="validation.json">real Three ray hits on hidden descendants are excluded before selecting a root or part</a> · unit</small><details><summary>验收要求</summary><p>用重叠模型/部件实测,命中应落可见有效对象。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>选中高亮与恢复原材质</strong><code>selection.highlight</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:627 · updateSelectionHighlight()</a></small></td><td>本轮修复已完成验证<small>射线候选逐级检查父级可见性和软删除;删除根对象先恢复高亮材质再释放几何/材质,避免错误拾取及 GPU 资源残留。</small></td><td>高亮是暂态,不写入模型/场景材质,不进入封面。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-camera-theme.json">真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</a> · 真实UI+只读投影</small><small><a href="validation.json">selection highlight cleanup restores source materials and releases its own GPU helper</a> · unit</small><small><a href="validation.json">copies synchronously without bounds, gizmo, grid or selection glow, then restores before JPEG encoding</a> · unit</small><details><summary>验收要求</summary><p>切换对象/清选择/预览/保存截图后材质原值恢复。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>移动工具及真实 XYZ 手柄</strong><code>transform.translate</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1627 · setTransform(mode)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>对象实例位置;部件按发布能力限制。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-transform.json">真实变换手柄移动旋转缩放、吸附与保存重开</a> · 真实UI手柄+只读几何投影定位</small><details><summary>验收要求</summary><p>真实拖动平移手柄,属性联动、撤销重做、保存重开一致。</p></details></td></tr><tr data-state="PASS"><td><strong>旋转工具及真实旋转手柄</strong><code>transform.rotate</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1627 · setTransform(mode)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>UI 度数与保存弧度转换;不能仅用脚本改姿态代替拖动。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-transform.json">真实变换手柄移动旋转缩放、吸附与保存重开</a> · 真实UI手柄+只读几何投影定位</small><details><summary>验收要求</summary><p>实际旋转手柄改变姿态并重开一致。</p></details></td></tr><tr data-state="PASS"><td><strong>缩放工具及真实缩放手柄</strong><code>transform.scale</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1627 · setTransform(mode)</a></small></td><td>本轮修复已完成验证<small>实际手柄跨越枢轴时按既有严格正尺度合同限制,避免产生负或零尺度导致模型翻转或消失;未新增镜像建模功能。</small></td><td>归一化模型已有 scale 必须保留,负/零/小值合同明确。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-transform.json">真实变换手柄移动旋转缩放、吸附与保存重开</a> · 真实UI手柄+只读几何投影定位</small><small><a href="validation.json">gizmo scaling across its pivot follows the same strictly positive scale contract as inspector edits</a> · unit</small><details><summary>验收要求</summary><p>真实缩放与属性数值一致;狐狸不突然放成超大或消失。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>W/E/R 快捷键</strong><code>transform.keys</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:469 · handleKeyboard(event)</a></small></td><td>本轮修复已完成验证<small>预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。</small></td><td>键盘焦点在表单时不切工具,失活缓存页不能抢键。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-transform.json">真实变换手柄移动旋转缩放、吸附与保存重开</a> · 真实UI手柄+只读几何投影定位</small><small><a href="validation.json">typing undo/redo stays inside text controls and inactive cached editors ignore global shortcuts</a> · unit</small><details><summary>验收要求</summary><p>视口按键切工具;名称输入包含 w/e/r 正常;只读不改。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>吸附 0.5m 与旋转 15° 开关</strong><code>transform.snap</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:581 · toggleSnap()</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>开关同时控制拖放/变换吸附;当前视图工具参数与业务值区分。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-transform.json">真实变换手柄移动旋转缩放、吸附与保存重开</a> · 真实UI手柄+只读几何投影定位</small><details><summary>验收要求</summary><p>开/关实际拖动位置、旋转角度,按钮/页脚状态一致。</p></details></td></tr></tbody></table></div></article><article class="matrix-group" data-group><h3>对象属性、部件与底部面板</h3><div class="scroll"><table class="matrix"><thead><tr><th>功能 / 执行路径</th><th>实现 / 对应修复</th><th>闭环边界</th><th>证据与验收要求</th></tr></thead><tbody><tr data-state="PASS"><td><strong>显示名称</strong><code>property.name</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:437 · handleInput(event)</a></small></td><td>本轮修复已完成验证<small>恢复显示名、设备和物模型编号使用空值保留规则,不把显式空串补回旧值;新资源序号跳过既有编号,空白编号校验按 trim 处理。</small></td><td>对象/部件 displayName 与项目名称分开。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><small><a href="evidence/truck-part-history.json">牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致</a> · browser</small><details><summary>验收要求</summary><p>修改名称,树/对象条/属性同步,保存重开保留。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p><p>实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。</p></details></td></tr><tr data-state="PASS"><td><strong>在场景中可见</strong><code>property.visible</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:438 · handleChange(event)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>实例显隐可保存;受保护蒙皮部件不得独立隐藏。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><small><a href="evidence/published-model-assets.json">模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开</a> · e2e</small><details><summary>验收要求</summary><p>开/关可见性、恢复、保存重开,视口与树状态准确。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p><p>API 已发布目录,狐狸双击和卡车真实拖入;固定来源版本、树根/部件搜索;卡车刚体部件隐藏保存重开,最后恢复可见。基线树没有折叠按钮,不将搜索伪称折叠。</p></details></td></tr><tr data-state="PASS"><td><strong>可作为实训目标</strong><code>property.operable</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:438 · handleChange(event)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>operable/capabilities 进入场景定义;不能扩大平台操作权限。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><details><summary>验收要求</summary><p>开/关并保存重开,发布数据的目标能力一致。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p></details></td></tr><tr data-state="PASS"><td><strong>位置 X/Y/Z 数值</strong><code>property.position</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:437 · handleInput(event)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>三个输入回写局部 position;不使用纯展示假字段。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><details><summary>验收要求</summary><p>三个轴独立输入与手柄联动、失焦/history/重开准确。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p></details></td></tr><tr data-state="PASS"><td><strong>旋转 X/Y/Z 数值</strong><code>property.rotation</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:437 · handleInput(event)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>UI 为角度,文档为弧度。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><details><summary>验收要求</summary><p>非整角/负角输入、实际姿态与 JSON/重开一致。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p></details></td></tr><tr data-state="PASS"><td><strong>缩放 X/Y/Z 数值及有效值</strong><code>property.scale</code><small><a href="../../../unreal_tran_web/src/features/scene-legacy/modelPlacement.ts">modelPlacement.ts:36 · parseSceneScale</a></small></td><td>本轮修复已完成验证<small>实际手柄跨越枢轴时按既有严格正尺度合同限制,避免产生负或零尺度导致模型翻转或消失;未新增镜像建模功能。</small></td><td>支持小数归一化 scale,避免小值格式化成零;非法值不得破坏模型。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><small><a href="validation.json">gizmo scaling across its pivot follows the same strictly positive scale contract as inspector edits</a> · unit</small><small><a href="validation.json">scale input accepts real GLTF normalization factors below 0.05</a> · unit</small><details><summary>验收要求</summary><p>小正数、负数/零/清空按合同处理,三轴保存往返正常。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>设备编号</strong><code>property.device-id</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:437 · handleInput(event)</a></small></td><td>本轮修复已完成验证<small>恢复显示名、设备和物模型编号使用空值保留规则,不把显式空串补回旧值;新资源序号跳过既有编号,空白编号校验按 trim 处理。</small></td><td>根对象绑定字符串;重复/空值由预检与后端核查。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><details><summary>验收要求</summary><p>修改后绑定表同步、保存重开,重复值能被阻断。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p></details></td></tr><tr data-state="PASS"><td><strong>物模型编号</strong><code>property.model-id</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:437 · handleInput(event)</a></small></td><td>本轮修复已完成验证<small>恢复显示名、设备和物模型编号使用空值保留规则,不把显式空串补回旧值;新资源序号跳过既有编号,空白编号校验按 trim 处理。</small></td><td>与受控 assetCode、来源项目 ID 分开。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><details><summary>验收要求</summary><p>修改后只改业务编号,源 GLB 仍加载正确。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p></details></td></tr><tr data-state="PASS"><td><strong>语义类型六个选项</strong><code>property.semantic</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:438 · handleChange(event)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>实训装备/工位设施/安全区域/采集设备/训练人员/环境设施。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><details><summary>验收要求</summary><p>逐项可选且保存重开保留;绑定表显示实际类型。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p></details></td></tr><tr data-state="PASS"><td><strong>模型来源与部件权限说明</strong><code>part.source</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1520 · renderInspector()</a></small></td><td>本轮修复已完成验证<small>模型来源按钮有来源权限时导航正确项目;无来源页面权限回到可访问列表,不将模型列表误称为来源工程编辑。</small></td><td>固定模型版本/源节点;蒙皮仅查看高亮,非骨骼独立变换。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><small><a href="evidence/published-model-assets.json">模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开</a> · e2e</small><details><summary>验收要求</summary><p>狐狸蒙皮与卡车刚性部件说明/禁用态准确。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p><p>API 已发布目录,狐狸双击和卡车真实拖入;固定来源版本、树根/部件搜索;卡车刚体部件隐藏保存重开,最后恢复可见。基线树没有折叠按钮,不将搜索伪称折叠。</p></details></td></tr><tr data-state="PASS"><td><strong>刚性部件独立变换并保存覆盖</strong><code>part.transform</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1123 · captureModelPartOverrides(root)</a></small></td><td>本轮修复已完成验证<small>新实例部件 sceneId 包含所属根对象身份,不再直接复用来源模型部件 ID;源 interaction/editor 标识保留。旧单实例 ID 保留,旧文档重复 ID 仅按文档顺序迁移后续冲突实例并同步 override。第二完整轮进一步发现 GLB 并发完成顺序会改变根对象顺序;现等待 allSettled 成功后按原文档顺序排列恢复根对象,拆分模板返回数组内部顺序和既有对象均保留,同源两实例不再随网速互换列表位置。新增延迟 30/1/10ms 乱序加载合同,验证序列化仍按文档顺序;最终浏览器验证独立记录。</small></td><td>partOverrides 与稳定 interactionId;多个实例互不串改。</td><td><span class="badge pass">已验证</span><small><a href="evidence/truck-part-history.json">牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致</a> · browser</small><small><a href="validation.json">two published instances have unique tree part IDs and editing the second persists independently after reopen</a> · unit</small><small><a href="validation.json">legacy duplicate instance IDs migrate only later owners in document order despite reversed GLB completion</a> · unit</small><details><summary>验收要求</summary><p>两卡车实例改单部件,保存重开另一实例不受影响。</p><p>实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>部件软删除与恢复</strong><code>part.soft-delete</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:588 · deleteSelected()</a></small></td><td>本轮修复已完成验证<small>新实例部件 sceneId 包含所属根对象身份,不再直接复用来源模型部件 ID;源 interaction/editor 标识保留。旧单实例 ID 保留,旧文档重复 ID 仅按文档顺序迁移后续冲突实例并同步 override。第二完整轮进一步发现 GLB 并发完成顺序会改变根对象顺序;现等待 allSettled 成功后按原文档顺序排列恢复根对象,拆分模板返回数组内部顺序和既有对象均保留,同源两实例不再随网速互换列表位置。新增延迟 30/1/10ms 乱序加载合同,验证序列化仍按文档顺序;最终浏览器验证独立记录。</small></td><td>被允许的部件 sceneDeleted/visible 持久化;可撤销恢复。</td><td><span class="badge pass">已验证</span><small><a href="evidence/truck-part-history.json">牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致</a> · browser</small><details><summary>验收要求</summary><p>实际删除、撤销、保存重开均一致;蒙皮不允许破坏骨架。</p><p>实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。</p></details></td></tr><tr data-state="PASS"><td><strong>删除根对象按钮 / Delete</strong><code>object.delete</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:588 · deleteSelected()</a></small></td><td>本轮修复已完成验证<small>射线候选逐级检查父级可见性和软删除;删除根对象先恢复高亮材质再释放几何/材质,避免错误拾取及 GPU 资源残留。</small></td><td>移除当前场景实例,不删除源模型项目/固定发布版本。</td><td><span class="badge pass">已验证</span><small><a href="evidence/venue-assets.json">两种整体场地加载、重复放置阻止与显式删除后替换</a> · e2e</small><small><a href="validation.json">deleting a root disposes its geometry and materials after clearing selection</a> · unit</small><details><summary>验收要求</summary><p>按钮和键盘各验证;源项目不变,撤销和重开正确。</p><p>车库和起伏地形真实加载并分别保存重开;已有整体场地时阻止叠加,显式删除旧场地后才能替换。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>底部对象列表与选中</strong><code>panel.objects</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1619 · renderBottom(mode</a></small></td><td>本轮修复已完成验证<small>设备绑定显示真实语义与未设置;校验按钮进入当前检查面板并随绑定变更刷新;日志记录本次运行实际操作,错误详情保持瞬时提示;摘要超过六项明确完整列表位置。</small></td><td>基线显示前 6 个摘要,完整对象仍在大纲;需明确截断。</td><td><span class="badge pass">已验证</span><small><a href="evidence/basic-assets.json">八种基础资源逐项放置、真实拖放、搜索和保存重开</a> · e2e</small><details><summary>验收要求</summary><p>大于 6 个对象时列表说明准确,摘要点击选择正确。</p><p>8 种基础资源分别双击放置并分开布置;真实指针拖入第 9 个工作台、吸附检查、唯一编号、保存刷新一致。照明为现有资源外观,不冒称连接真实灯控。</p></details></td></tr><tr data-state="PASS"><td><strong>设备绑定总表</strong><code>panel.binding</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1619 · renderBottom(mode</a></small></td><td>本轮修复已完成验证<small>设备绑定显示真实语义与未设置;校验按钮进入当前检查面板并随绑定变更刷新;日志记录本次运行实际操作,错误详情保持瞬时提示;摘要超过六项明确完整列表位置。</small></td><td>基线语义列恒写已绑定;需要真实类型/缺失/冲突反馈。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><details><summary>验收要求</summary><p>编辑字段即时同步,空/重复数据不得假标已绑定。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p></details></td></tr><tr data-state="PASS"><td><strong>底部发布检查面板</strong><code>panel.validation</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1619 · renderBottom(mode</a></small></td><td>本轮修复已完成验证<small>设备绑定显示真实语义与未设置;校验按钮进入当前检查面板并随绑定变更刷新;日志记录本次运行实际操作,错误详情保持瞬时提示;摘要超过六项明确完整列表位置。</small></td><td>显示当前逐项检查,不等于已发布。</td><td><span class="badge pass">已验证</span><small><a href="evidence/publish-fixed-version.json">发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用</a> · browser</small><small><a href="validation.json">verify opens live validation and whitespace binding edits immediately invalidate the active tab</a> · unit</small><details><summary>验收要求</summary><p>顶部校验/发布失败可定位检查面板,内容随修改更新。</p><p>实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>操作日志</strong><code>panel.logs</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1619 · renderBottom(mode</a></small></td><td>本轮修复已完成验证<small>设备绑定显示真实语义与未设置;校验按钮进入当前检查面板并随绑定变更刷新;日志记录本次运行实际操作,错误详情保持瞬时提示;摘要超过六项明确完整列表位置。</small></td><td>基线为静态示例且误称本地存储;本轮只需真实本次运行日志,不扩展服务端审计。</td><td><span class="badge pass">已验证</span><small><a href="validation.json">activity log reflects actual session events and keeps API error details transient</a> · unit</small><details><summary>验收要求</summary><p>实际操作产生对应时间/结果;API 模式不显示本地存储为真值。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr></tbody></table></div></article><article class="matrix-group" data-group><h3>历史与预览</h3><div class="scroll"><table class="matrix"><thead><tr><th>功能 / 执行路径</th><th>实现 / 对应修复</th><th>闭环边界</th><th>证据与验收要求</th></tr></thead><tbody><tr data-state="PASS"><td><strong>回退按钮 / Ctrl+Z</strong><code>history.undo</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:2417 · async undo()</a></small></td><td>本轮修复已完成验证<small>透视按钮实际恢复先前观察位置;导入、加载和历史恢复存在已保存相机时不再被车辆自动聚焦覆盖。;预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。;文档签名忽略更新时间和对象键顺序;保存后的 undo/redo 根据真实已保存文档判断 dirty;异步恢复失败保留现场并回滚历史索引。</small></td><td>输入连续修改合并成合理历史步;恢复对象/模板/选择。</td><td><span class="badge pass">已验证</span><small><a href="evidence/truck-part-history.json">牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致</a> · browser</small><small><a href="validation.json">typing undo/redo stays inside text controls and inactive cached editors ignore global shortcuts</a> · unit</small><small><a href="validation.json">loading and history restoration preserve an authored camera rather than auto-focusing a vehicle</a> · unit</small><small><a href="validation.json">save B then undo A stays dirty, and redo B returns to the actual saved baseline</a> · unit</small><small><a href="validation.json">default history caps at 60 snapshots, stops at either edge and discards redo only after a new branch</a> · unit</small><details><summary>验收要求</summary><p>属性、添加、删除、模板分别撤销,真实画面与文档一致。</p><p>实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>前进按钮 / Ctrl+Y / Ctrl+Shift+Z</strong><code>history.redo</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:2437 · async redo()</a></small></td><td>本轮修复已完成验证<small>预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。;文档签名忽略更新时间和对象键顺序;保存后的 undo/redo 根据真实已保存文档判断 dirty;异步恢复失败保留现场并回滚历史索引。</small></td><td>撤销后重做,分叉编辑清 redo。</td><td><span class="badge pass">已验证</span><small><a href="evidence/truck-part-history.json">牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致</a> · browser</small><small><a href="validation.json">typing undo/redo stays inside text controls and inactive cached editors ignore global shortcuts</a> · unit</small><small><a href="validation.json">save B then undo A stays dirty, and redo B returns to the actual saved baseline</a> · unit</small><small><a href="validation.json">default history caps at 60 snapshots, stops at either edge and discards redo only after a new branch</a> · unit</small><details><summary>验收要求</summary><p>按钮与两种快捷键各执行;新编辑后旧 redo 不可用。</p><p>实际部件变换和软删除、按钮/键盘历史、保存基线与输入框自身撤销;双实例隔离及车库围护三种模式真实点击、导出和隐藏后保存刷新。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>60 步历史上限与边界</strong><code>history.capacity</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:180 · new SceneHistory(60)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>历史属于本次编辑会话;不宣称服务端版本历史。</td><td><span class="badge pass">已验证</span><small><a href="validation.json">default history caps at 60 snapshots, stops at either edge and discards redo only after a new branch</a> · unit</small><details><summary>验收要求</summary><p>聚焦测试验证 60 步边界、空 undo/redo 与失焦快照。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>异步历史恢复失败/互斥</strong><code>history.failure</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:2328 · async restoreHistoryState</a></small></td><td>本轮修复已完成验证<small>文档签名忽略更新时间和对象键顺序;保存后的 undo/redo 根据真实已保存文档判断 dirty;异步恢复失败保留现场并回滚历史索引。</small></td><td>失败不能推进索引或丢当前有效场景;忙碌时写操作需一致。</td><td><span class="badge pass">已验证</span><small><a href="validation.json">failed undo restoration retains live objects and keeps the undo target available for retry</a> · unit</small><small><a href="validation.json">replacement failure waits for other started loads before restoring the original scene</a> · unit</small><details><summary>验收要求</summary><p>GLB 恢复失败、并发 undo/redo、保存混入均不半提交。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>进入运行预览</strong><code>preview.enter</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:583 · togglePreview()</a></small></td><td>本轮修复已完成验证<small>预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。</small></td><td>当前默认预览只展示场景,未接模型 timeline/blueprint 自动执行。</td><td><span class="badge pass">已验证</span><small><a href="evidence/readonly.json">只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景</a> · browser</small><small><a href="evidence/viewport-camera-theme.json">真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</a> · 真实UI+只读投影</small><details><summary>验收要求</summary><p>实际进入,辅助选择/手柄状态正常,模型可见。</p><p>只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。</p></details></td></tr><tr data-state="PASS"><td><strong>按钮 / Escape 退出预览</strong><code>preview.exit</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:469 · handleKeyboard(event)</a></small></td><td>本轮修复已完成验证<small>预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。</small></td><td>退出后恢复编辑选择与状态;不得丢未存变换。</td><td><span class="badge pass">已验证</span><small><a href="evidence/readonly.json">只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景</a> · browser</small><small><a href="evidence/viewport-camera-theme.json">真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</a> · 真实UI+只读投影</small><small><a href="validation.json">preview locks mutation controls and Delete while Escape restores editing</a> · unit</small><details><summary>验收要求</summary><p>两种退出方式均有效,输入焦点与缓存激活行为正确。</p><p>只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>预览中禁止编辑</strong><code>preview.lock</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:583 · togglePreview()</a></small></td><td>本轮修复已完成验证<small>预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。</small></td><td>显示“场景编辑锁定”必须覆盖属性、删除、模板、导入和快捷键。</td><td><span class="badge pass">已验证</span><small><a href="evidence/readonly.json">只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景</a> · browser</small><small><a href="evidence/viewport-camera-theme.json">真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</a> · 真实UI+只读投影</small><small><a href="validation.json">preview locks mutation controls and Delete while Escape restores editing</a> · unit</small><details><summary>验收要求</summary><p>尝试所有写入口,预览时文档不改变;退出后正常。</p><p>只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr></tbody></table></div></article><article class="matrix-group" data-group><h3>保存封面、发布与依赖</h3><div class="scroll"><table class="matrix"><thead><tr><th>功能 / 执行路径</th><th>实现 / 对应修复</th><th>闭环边界</th><th>证据与验收要求</th></tr></thead><tbody><tr data-state="PASS"><td><strong>保存时截取当前三维场景封面</strong><code>cover.capture</code><small><a href="../../../unreal_tran_web/src/features/scene-legacy/sceneCoverCapture.ts">sceneCoverCapture.ts:26 · export async function captureSceneCover</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>JPEG 最大边 640、当前相机;隐藏手柄/网格/高亮且恢复现场。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><small><a href="validation.json">copies synchronously without bounds, gizmo, grid or selection glow, then restores before JPEG encoding</a> · unit</small><details><summary>验收要求</summary><p>截图与服务端图片可辨一致,像素实际有模型,UI 状态不被破坏。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>封面资产与文档同次保存</strong><code>cover.atomic</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:47 · saveSceneDocumentWithCover</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>封面 upload ticket 与 version 同一次 PUT 消费,不另存过期文档。</td><td><span class="badge pass">已验证</span><small><a href="evidence/fox-properties-cover.json">狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</a> · browser</small><small><a href="evidence/publish-fixed-version.json">发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用</a> · browser</small><small><a href="validation.json">scene image ticket, fixed asset replacement, dependencies and coverUri commit in one versioned PUT</a> · unit</small><details><summary>验收要求</summary><p>检查资产/封面字段/当前版本相同,并真实列表加载图片。</p><p>真实字段输入与清空、六个语义选项、蒙皮保护、源模型可见,正文/资产/封面保存和刷新;不把原件旧模板组合当新模板结果。</p><p>实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>截图/封面上传失败保留原封面</strong><code>cover.failure</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:390 · coverFailed</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>正文可保存时给真实 warning;409 必须阻断且不得覆盖。</td><td><span class="badge pass">已验证</span><small><a href="evidence/save-cover-failure.json">保存失败与封面降级:503 保留草稿,封面上传失败保留旧图,恢复后可重试</a> · browser</small><small><a href="validation.json">capture failure saves content with the previous cover and reports a warning result</a> · unit</small><small><a href="validation.json">upload conflict propagates without any content PUT</a> · unit</small><details><summary>验收要求</summary><p>截图失败、上传503、PUT409各自核对保留/提示/零错误发布。</p><p>正文PUT503不清草稿;封面上传503保留旧图但正文可保存;恢复服务后保存刷新。截图异常和409另由聚焦合同验证。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>环境、对象用途、模型加载检查</strong><code>validation.complete</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1715 · verifyScene(showToast</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>纯环境允许;模型未完成解析必须阻断。</td><td><span class="badge pass">已验证</span><small><a href="evidence/publish-fixed-version.json">发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用</a> · browser</small><details><summary>验收要求</summary><p>空地板/纯环境/带模型和加载中分别核对检查结果。</p><p>实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。</p></details></td></tr><tr data-state="PASS"><td><strong>设备/物模型编号完整与唯一</strong><code>validation.bindings</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:1715 · verifyScene(showToast</a></small></td><td>本轮修复已完成验证<small>恢复显示名、设备和物模型编号使用空值保留规则,不把显式空串补回旧值;新资源序号跳过既有编号,空白编号校验按 trim 处理。;设备绑定显示真实语义与未设置;校验按钮进入当前检查面板并随绑定变更刷新;日志记录本次运行实际操作,错误详情保持瞬时提示;摘要超过六项明确完整列表位置。</small></td><td>空白、重复编号按前后端合同处理,不自动替用户分配新业务身份。</td><td><span class="badge pass">已验证</span><small><a href="evidence/publish-fixed-version.json">发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用</a> · browser</small><small><a href="validation.json">new resource bindings do not reuse a saved serial after earlier objects were deleted</a> · unit</small><small><a href="validation.json">verify opens live validation and whitespace binding edits immediately invalidate the active tab</a> · unit</small><details><summary>验收要求</summary><p>空、空格、重复与合法编号;错误项可见,发布被阻断。</p><p>实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>受控资源引用完整性</strong><code>validation.references</code><small><a href="../../../unreal_tran_web/src/features/scene-legacy/serverAssetStore.ts">serverAssetStore.ts:18 · hasReferenceSync</a></small></td><td>本轮修复已完成验证<small>前端目录和资源解析同时匹配指定项目、版本、code、id、URI、READY MODEL_FILE;错误引用不回退首文件或本地缓存。仅含 sourceAssetId/assetId 的本项目资源也必须将 ID 纳入缓存/去重键,防止已缓存有效引用掩盖另一个错误ID;失败请求缓存可重试。复制场景保留旧伪 URI 但只读取当前副本资产。;新增 SceneResourceReferenceValidator 检查 objects 和递归 splitParts;自有/固定版本资源的所有显式引用必须指向同一 READY 模型文件,错误引用在发布/提交前阻断,保留受控的内置资源和旧引用兼容。</small></td><td>project/version/assetCode/sourceAssetId 必须指向实际固定资产,不能只测有字段。</td><td><span class="badge pass">已验证</span><small><a href="evidence/json-glb-roundtrip.json">工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返</a> · browser</small><small><a href="validation.json">an explicit missing code cannot fall back to the first READY asset</a> · unit</small><details><summary>验收要求</summary><p>不存在编码/错误资产类型/跨项目引用显式拒绝;正确依赖通过。</p><p>真实JSON/GLB文件输入、错误schema/损坏资源/上传503拒绝、错误assetCode回滚、稳定URI及渲染参数保存重开。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>发布场景按钮的真实 API 闭环</strong><code>publish.saved</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:2213 · async publishScene()</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>有编辑权时先保存含封面再发布;两步均成功才显示已发布。</td><td><span class="badge pass">已验证</span><small><a href="evidence/publish-fixed-version.json">发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用</a> · browser</small><details><summary>验收要求</summary><p>POST 真正发布当前版本,列表状态与下游目录一致。</p><p>实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。</p></details></td></tr><tr data-state="PASS"><td><strong>只有发布权限时发布已存版本</strong><code>publish.only</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:335 · if (this.readOnly)</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>publish-only 不先 PUT、不上传封面、不要求 update;服务器校验真值。</td><td><span class="badge pass">已验证</span><small><a href="evidence/scene-publish-only.json">仅场景发布权限:浏览器发布已存副本,不发送保存或上传请求</a> · browser</small><details><summary>验收要求</summary><p>隔离权限会话发布已存有效版本,零 update/upload 请求。</p></details></td></tr><tr data-state="PASS"><td><strong>预检/保存/发布失败阻断</strong><code>publish.failure</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:2213 · async publishScene()</a></small></td><td>本轮修复已完成验证<small>新增 SceneResourceReferenceValidator 检查 objects 和递归 splitParts;自有/固定版本资源的所有显式引用必须指向同一 READY 模型文件,错误引用在发布/提交前阻断,保留受控的内置资源和旧引用兼容。</small></td><td>保存失败不 POST 发布;发布失败不假标已发布;保持错误恢复能力。</td><td><span class="badge pass">已验证</span><small><a href="evidence/publish-fixed-version.json">发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用</a> · browser</small><details><summary>验收要求</summary><p>预检错误、PUT409/503、POST失败及重试,版本/状态准确。</p><p>实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。</p></details></td></tr><tr data-state="PASS"><td><strong>重复发布与上传/保存队列</strong><code>publish.concurrent</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:509 · publishDocument:</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>整条 POST 完成前仍占 FIFO;重复操作不串版本。</td><td><span class="badge pass">已验证</span><small><a href="validation.json">role switch waits through validation, save, and the later queued publish after dirty clears</a> · unit</small><small><a href="validation.json">project operation queue serializes upload, save and publish including failures</a> · unit</small><details><summary>验收要求</summary><p>延迟上传/保存/发布并触发竞争,服务端只有完整合法版本。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>固定 SCENE_MODEL 版本与多个实例</strong><code>publish.fixed-model</code><small><a href="../../../unreal_tran_web/src/features/scene-legacy/contentApiClient.ts">contentApiClient.ts:938 · collectSceneModelDependencies</a></small></td><td>本轮修复已完成验证<small>前端目录和资源解析同时匹配指定项目、版本、code、id、URI、READY MODEL_FILE;错误引用不回退首文件或本地缓存。仅含 sourceAssetId/assetId 的本项目资源也必须将 ID 纳入缓存/去重键,防止已缓存有效引用掩盖另一个错误ID;失败请求缓存可重试。复制场景保留旧伪 URI 但只读取当前副本资产。;新增 SceneResourceReferenceValidator 检查 objects 和递归 splitParts;自有/固定版本资源的所有显式引用必须指向同一 READY 模型文件,错误引用在发布/提交前阻断,保留受控的内置资源和旧引用兼容。;完整 PUT 回写原有相同 SCENE_MODEL 三元组时沿用场景授权边界;新增或改版依赖仍要求来源模型可见及范围权限,不扩大模型页面访问权。;新实例部件 sceneId 包含所属根对象身份,不再直接复用来源模型部件 ID;源 interaction/editor 标识保留。旧单实例 ID 保留,旧文档重复 ID 仅按文档顺序迁移后续冲突实例并同步 override。第二完整轮进一步发现 GLB 并发完成顺序会改变根对象顺序;现等待 allSettled 成功后按原文档顺序排列恢复根对象,拆分模板返回数组内部顺序和既有对象均保留,同源两实例不再随网速互换列表位置。新增延迟 30/1/10ms 乱序加载合同,验证序列化仍按文档顺序;最终浏览器验证独立记录。</small></td><td>去重精确模型版本,不能随模型新发布静默换源版本。</td><td><span class="badge pass">已验证</span><small><a href="evidence/publish-fixed-version.json">发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用</a> · browser</small><small><a href="evidence/scene-publish-only.json">仅场景发布权限:浏览器发布已存副本,不发送保存或上传请求</a> · browser</small><small><a href="evidence/scene-update-only.json">仅场景编辑权限:固定模型可加载和保存,不能发布或访问模型页面</a> · browser</small><small><a href="validation.json">legacy duplicate instance IDs migrate only later owners in document order despite reversed GLB completion</a> · unit</small><small><a href="validation.json">fixed model reads the exact version and downloads through its visible scene dependency</a> · unit</small><small><a href="validation.json">fixed model dependencies are recursively collected, exact and de-duplicated</a> · unit</small><details><summary>验收要求</summary><p>同模型多实例及另模型新版本,场景重开仍使用所存固定版本。</p><p>实际校验、绑定阻断、POST503不假发布,重试发布当前固定版本;返回列表保留编辑标签,搜索当前项目并核对真实封面图片加载。下游静态渲染另有独立用例。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>发布场景供训练选择和静态还原</strong><code>publish.downstream</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:2227 · 场景已发布,训练编排可以引用</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>本轮只验证版本/目标/静态可见性;不承诺模型蓝图或原生动画自动执行。</td><td><span class="badge pass">已验证</span><small><a href="evidence/downstream-fox.json">fox:场景发布后在真实训练编排选择、保存并重开静态模型</a> · browser</small><small><a href="evidence/downstream-truck.json">truck:场景发布后在真实训练编排选择、保存并重开静态模型</a> · browser</small><details><summary>验收要求</summary><p>新场景出现在正式训练目录,绑定后狐狸/卡车可见且目标身份一致。</p></details></td></tr></tbody></table></div></article><article class="matrix-group" data-group><h3>权限、缓存与未保存恢复</h3><div class="scroll"><table class="matrix"><thead><tr><th>功能 / 执行路径</th><th>实现 / 对应修复</th><th>闭环边界</th><th>证据与验收要求</th></tr></thead><tbody><tr data-state="PASS"><td><strong>场景细分编辑权限</strong><code>permission.update</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:149 · this.readOnly =</a></small></td><td>本轮修复已完成验证<small>移除 runtime 旧 content.update 门槛,独立计算新建、更新和发布;无模型页面权限时不加载新模型目录,但既有固定依赖仍可按场景授权读取。;完整 PUT 回写原有相同 SCENE_MODEL 三元组时沿用场景授权边界;新增或改版依赖仍要求来源模型可见及范围权限,不扩大模型页面访问权。</small></td><td>只按 content.scene.update;基线残留 content.update 需修,不由角色名推断。</td><td><span class="badge pass">已验证</span><small><a href="evidence/readonly.json">只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景</a> · browser</small><small><a href="evidence/scene-update-only.json">仅场景编辑权限:固定模型可加载和保存,不能发布或访问模型页面</a> · browser</small><small><a href="validation.json">scene granular update permission enables editing without the retired shared code</a> · unit</small><small><a href="validation.json">fixed model reads the exact version and downloads through its visible scene dependency</a> · unit</small><details><summary>验收要求</summary><p>具更新权会话可编辑;无更新权的写入口和接口全部拒绝。</p><p>只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>场景细分发布权限</strong><code>permission.publish</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:152 · this.canPublish =</a></small></td><td>本轮修复已完成验证<small>移除 runtime 旧 content.update 门槛,独立计算新建、更新和发布;无模型页面权限时不加载新模型目录,但既有固定依赖仍可按场景授权读取。</small></td><td>页面 content.scene 与 publish 动作分开;更新权不隐含发布。</td><td><span class="badge pass">已验证</span><small><a href="evidence/readonly.json">只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景</a> · browser</small><small><a href="evidence/scene-no-page-permission.json">无场景页面权限:编辑路由和直接内容接口均拒绝访问</a> · browser</small><small><a href="evidence/scene-publish-only.json">仅场景发布权限:浏览器发布已存副本,不发送保存或上传请求</a> · browser</small><small><a href="evidence/scene-update-only.json">仅场景编辑权限:固定模型可加载和保存,不能发布或访问模型页面</a> · browser</small><details><summary>验收要求</summary><p>只更新不能发布;仅发布可发布已存版本;无页面权不可访问。</p><p>只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。</p></details></td></tr><tr data-state="PASS"><td><strong>只读仍可查看、搜索、选中和预览</strong><code>permission.readonly</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:198 · applyAccessMode()</a></small></td><td>本轮修复已完成验证<small>移除 runtime 旧 content.update 门槛,独立计算新建、更新和发布;无模型页面权限时不加载新模型目录,但既有固定依赖仍可按场景授权读取。</small></td><td>禁写不应误禁观察控件;临时视图不保存到服务器。</td><td><span class="badge pass">已验证</span><small><a href="evidence/readonly.json">只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景</a> · browser</small><small><a href="evidence/scene-role-switch.json">后台场景未保存时切换角色:关闭确认保留草稿,保存切换后重建只读权限</a> · browser</small><details><summary>验收要求</summary><p>真实键盘搜索、选择、相机、预览,零内容写请求。</p><p>只读按钮/模板/资源禁写、蒙皮浏览、运行预览与快捷键不变更对象,直接PUT拒绝;具体搜索/鼠标覆盖另见视口用例。</p></details></td></tr><tr data-state="PASS"><td><strong>800ms 未保存恢复缓冲</strong><code>recovery.autobuffer</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:654 · markDirty(label</a></small></td><td>本轮修复已完成验证<small>顶部新建委托宿主命名与创建 API,先确认未保存内容。选择放弃也要等创建及导航成功后才清除原 dirty/恢复稿;创建503仍保留原稿。成功后刷新原缓存页,避免再次打开时将已放弃修改冒称已保存。取消、失败不清原场景,成功导航到新项目 ID,并有创建并发守卫。;开始保存时取消之前安排的恢复稿定时器,避免它覆盖宿主保存快照 nonce;保存中的后续编辑仍单独保留。</small></td><td>仅写隔离浏览器恢复稿,不伪称自动入库。</td><td><span class="badge pass">已验证</span><small><a href="evidence/real-recovery-conflict.json">同版本恢复与 409:真实未保存草稿重载、服务器新版本保护与隔离下载</a> · browser</small><small><a href="evidence/save-concurrency.json">延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突</a> · browser</small><small><a href="evidence/scene-multi-session.json">双标签各自修改场景:保存和明确丢弃当前页均保留另一页真实恢复稿</a> · browser</small><details><summary>验收要求</summary><p>实际修改后等待,恢复稿存在但无自动 PUT;手动保存后入库。</p><p>真实未存输入产生恢复稿,同版本恢复不自动入库;另一版本先保存后409拒绝覆盖,旧稿隔离下载。</p><p>延迟真实PUT快照A、重复Ctrl+S不重复请求、期间编辑B仍dirty;再次保存刷新无旧恢复稿误报。</p></details></td></tr><tr data-state="PASS"><td><strong>同版本真实未保存恢复</strong><code>recovery.same-version</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:46 · sceneRecoveryMatchesProjectVersion</a></small></td><td>本轮修复已完成验证<small>文档签名忽略更新时间和对象键顺序;保存后的 undo/redo 根据真实已保存文档判断 dirty;异步恢复失败保留现场并回滚历史索引。;开始保存时取消之前安排的恢复稿定时器,避免它覆盖宿主保存快照 nonce;保存中的后续编辑仍单独保留。</small></td><td>先回到视口,须显式保存才入库;相同文档不应产生假恢复。</td><td><span class="badge pass">已验证</span><small><a href="evidence/real-recovery-conflict.json">同版本恢复与 409:真实未保存草稿重载、服务器新版本保护与隔离下载</a> · browser</small><small><a href="evidence/save-concurrency.json">延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突</a> · browser</small><details><summary>验收要求</summary><p>真实修改后刷新,恢复/保存/再刷新;无注入假 localStorage 代替。</p><p>真实未存输入产生恢复稿,同版本恢复不自动入库;另一版本先保存后409拒绝覆盖,旧稿隔离下载。</p><p>延迟真实PUT快照A、重复Ctrl+S不重复请求、期间编辑B仍dirty;再次保存刷新无旧恢复稿误报。</p></details></td></tr><tr data-state="PASS"><td><strong>不同版本恢复稿隔离</strong><code>recovery.stale-version</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:46 · sceneRecoveryMatchesProjectVersion</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>旧稿不能自动覆盖新服务器版本;下载供人工合并。</td><td><span class="badge pass">已验证</span><small><a href="evidence/real-recovery-conflict.json">同版本恢复与 409:真实未保存草稿重载、服务器新版本保护与隔离下载</a> · browser</small><details><summary>验收要求</summary><p>另一会话先保存,新会话面对旧稿只能下载/隔离/明确丢弃。</p><p>真实未存输入产生恢复稿,同版本恢复不自动入库;另一版本先保存后409拒绝覆盖,旧稿隔离下载。</p></details></td></tr><tr data-state="PASS"><td><strong>下载、保留、丢弃恢复稿</strong><code>recovery.discard</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:223 · async function quarantineRecovery</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>关闭/Escape 不算明确丢弃;他会话新写入稿不得被误删。</td><td><span class="badge pass">已验证</span><small><a href="evidence/scene-multi-session.json">双标签各自修改场景:保存和明确丢弃当前页均保留另一页真实恢复稿</a> · browser</small><small><a href="validation.json">recovery conflict defaults to preserve and only explicit discard may remove it</a> · unit</small><details><summary>验收要求</summary><p>按钮逐项核对本地隔离键、下载文件与服务器无意外写入。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>多标签/多会话恢复所有权</strong><code>recovery.multi-session</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:104 · recoveryStoragePrefix</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>会话键/nonce/锁避免覆盖别的未存稿;本地缓冲不冒充权限。</td><td><span class="badge pass">已验证</span><small><a href="evidence/scene-multi-session.json">双标签各自修改场景:保存和明确丢弃当前页均保留另一页真实恢复稿</a> · browser</small><small><a href="validation.json">per-page recovery keys prevent copied tabs from deleting each other checkpoints</a> · unit</small><details><summary>验收要求</summary><p>两个编辑会话产生不同草稿,保存/丢弃一份不删另一份。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>关闭场景标签时取消离开</strong><code>leave.cancel</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:704 · async function confirmLeave</a></small></td><td>本轮修复已完成验证<small>关闭单个或批量场景标签先按目标调用缓存守卫;取消或后续保存失败不提交此前放弃决定,全部确认后才关闭并清理。返回列表仍保留缓存,不扩展为强制关闭。</small></td><td>关闭确认保留当前 dirty 与恢复副本;返回列表或切普通标签不应提示。</td><td><span class="badge pass">已验证</span><small><a href="evidence/scene-role-switch.json">后台场景未保存时切换角色:关闭确认保留草稿,保存切换后重建只读权限</a> · browser</small><small><a href="validation.json">closing one selected scene checks its cached guard, leaves unrelated workspaces alone</a> · unit</small><small><a href="validation.json">cancel or failed save in a later tab never commits earlier draft discard</a> · unit</small><details><summary>验收要求</summary><p>真实关闭确认的关闭/Escape、继续编辑,数据未丢;不把返回列表当关闭。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>保存并离开 / 放弃修改</strong><code>leave.save</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:704 · async function confirmLeave</a></small></td><td>本轮修复已完成验证<small>关闭单个或批量场景标签先按目标调用缓存守卫;取消或后续保存失败不提交此前放弃决定,全部确认后才关闭并清理。返回列表仍保留缓存,不扩展为强制关闭。</small></td><td>等待保存完成并确认最终 dirty;明确放弃只影响当前工程会话。</td><td><span class="badge pass">已验证</span><small><a href="evidence/scene-role-switch.json">后台场景未保存时切换角色:关闭确认保留草稿,保存切换后重建只读权限</a> · browser</small><small><a href="validation.json">cancel or failed save in a later tab never commits earlier draft discard</a> · unit</small><small><a href="validation.json">all successful confirmations commit together and unmounted guards are removed</a> · unit</small><details><summary>验收要求</summary><p>保存成功才离开;503/409留原页;放弃按确认执行。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>加载/上传/发布途中离开</strong><code>leave.busy</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:708 · const resourcesBusy =</a></small></td><td>已有实现已完成验证<small>尚未关联本轮修复记录</small></td><td>资源未就绪阻止离开;进行中的保存/发布须等待最终结果。</td><td><span class="badge pass">已验证</span><small><a href="evidence/loading-retry.json">加载失败重试与加载互斥:不展示可写半场景、恢复网络后模型完整加载</a> · browser</small><small><a href="validation.json">role switch waits through validation, save, and the later queued publish after dirty clears</a> · unit</small><small><a href="validation.json">role switch rechecks resource uploads started while the save was finishing</a> · unit</small><details><summary>验收要求</summary><p>延迟资源与提交各验证取消/等待,旧请求不污染新项目。</p><p>GET503只留中性失败和重试;延迟受控下载时禁用写入/导出/预览并阻止关闭,释放后真实狐狸加载。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>角色切换覆盖后台场景未存稿</strong><code>leave.role-switch</code><small><a href="../../../unreal_tran_web/src/views/content/LegacyScenePageView.vue">LegacyScenePageView.vue:2 · registerWorkspaceLeaveGuard</a></small></td><td>本轮修复已完成验证<small>关闭单个或批量场景标签先按目标调用缓存守卫;取消或后续保存失败不提交此前放弃决定,全部确认后才关闭并清理。返回列表仍保留缓存,不扩展为强制关闭。</small></td><td>全体守卫确认后才切角色;后一页取消不能抹掉前页 dirty。</td><td><span class="badge pass">已验证</span><small><a href="evidence/scene-role-switch.json">后台场景未保存时切换角色:关闭确认保留草稿,保存切换后重建只读权限</a> · browser</small><small><a href="validation.json">cancel or failed save in a later tab never commits earlier draft discard</a> · unit</small><small><a href="validation.json">role switch waits through validation, save, and the later queued publish after dirty clears</a> · unit</small><details><summary>验收要求</summary><p>含后台场景的切角色确认取消/保存失败/成功后权限重建。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>标签失活 pause / 激活 resume</strong><code>lifecycle.cache</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:523 · pause()</a></small></td><td>本轮修复已完成验证<small>顶部新建委托宿主命名与创建 API,先确认未保存内容。选择放弃也要等创建及导航成功后才清除原 dirty/恢复稿;创建503仍保留原稿。成功后刷新原缓存页,避免再次打开时将已放弃修改冒称已保存。取消、失败不清原场景,成功导航到新项目 ID,并有创建并发守卫。;预览状态同步全部操作禁用,Delete/W/E/R 和表单输入遵守预览/权限;失活缓存页忽略全局快捷键,输入框撤销保持文本语义。;关闭单个或批量场景标签先按目标调用缓存守卫;取消或后续保存失败不提交此前放弃决定,全部确认后才关闭并清理。返回列表仍保留缓存,不扩展为强制关闭。</small></td><td>停止后台 RAF、恢复时重算视口;后台实例不能抢当前页键盘。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-cache-close.json">缓存标签暂停键盘、草稿恢复以及关闭取消和保存失败留页</a> · 真实UI缓存/关闭+浏览器模拟503拒绝写入</small><small><a href="validation.json">typing undo/redo stays inside text controls and inactive cached editors ignore global shortcuts</a> · unit</small><small><a href="validation.json">closing one selected scene checks its cached guard, leaves unrelated workspaces alone</a> · unit</small><details><summary>验收要求</summary><p>两场景标签切换、选择和未存值保留、后台无写/快捷键误动作。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>关闭释放 WebGL/监听/资源</strong><code>lifecycle.destroy</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:421 · destroy()</a></small></td><td>本轮修复已完成验证<small>射线候选逐级检查父级可见性和软删除;删除根对象先恢复高亮材质再释放几何/材质,避免错误拾取及 GPU 资源残留。;关闭单个或批量场景标签先按目标调用缓存守卫;取消或后续保存失败不提交此前放弃决定,全部确认后才关闭并清理。返回列表仍保留缓存,不扩展为强制关闭。</small></td><td>销毁后的异步回调无 DOM 写入,无残留全局事件和资产 URL。</td><td><span class="badge pass">已验证</span><small><a href="validation.json">deleting a root disposes its geometry and materials after clearing selection</a> · unit</small><small><a href="validation.json">selection highlight cleanup restores source materials and releases its own GPU helper</a> · unit</small><small><a href="validation.json">all successful confirmations commit together and unmounted guards are removed</a> · unit</small><small><a href="validation.json">an upload finishing after a project switch cannot register into the new session</a> · unit</small><details><summary>验收要求</summary><p>反复打开/关闭、加载中关闭边界,控制台无异常/泄漏事件。</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p><p>实际执行真实方法/纯函数合同,未冒称浏览器操作</p></details></td></tr><tr data-state="PASS"><td><strong>桌面尺寸与明亮/暗黑可读性</strong><code>lifecycle.resize-theme</code><small><a href="../../../unreal_tran_web/src/features/guide-legacy/demo-src/scene-editor.js">scene-editor.js:414 · resize()</a></small></td><td>本轮修复已完成验证<small>原型 CSS 全局重置收敛到场景作用域,降低按钮重置优先级;侧栏、输入、模板、属性和摘要使用正式主题变量,修复白字白底;修复 1440 宽时长工具栏/画布撑开网格覆盖属性栏。</small></td><td>继承正式主题;所有可达控件可读、无截断遮挡。</td><td><span class="badge pass">已验证</span><small><a href="evidence/viewport-camera-theme.json">真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</a> · 真实UI+只读投影</small><details><summary>验收要求</summary><p>1920/1440 下工具栏、属性、模板与弹窗截图,主题切换无低对比。</p></details></td></tr></tbody></table></div></article></section>
  9. <section id="testing"><div class="eyebrow">05 / 测试</div><h2>真实浏览器、API、单测分别记录</h2><article class="panel"><h3>最终整轮 E2E</h3><p class="muted">首轮记录:32 项通过,1 项失败。关闭保存失败用例的提示定位同时匹配成功和失败消息,已限定目标错误;另补列表搜索遮罩消失后截图的等待条件。此轮记录保留在验证文件中,不计入下列最终整轮结果。</p><p class="muted">第二轮记录:32 项通过,1 项失败。并行加载同源模型时恢复顺序不稳定;已保留文档对象顺序,并以实例编号验证部件隔离和重开。此轮记录保留在验证文件中,不计入下列最终整轮结果。</p><div class="scroll"><table><thead><tr><th>用例</th><th>结果</th><th>耗时 / 证据</th></tr></thead><tbody><tr><td>加载失败重试与加载互斥:不展示可写半场景、恢复网络后模型完整加载</td><td><span class="badge pass">已验证</span></td><td>8.7 秒 · 2 个附件</td></tr><tr><td>延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突</td><td><span class="badge pass">已验证</span></td><td>12.5 秒 · 1 个附件</td></tr><tr><td>fox:场景发布后在真实训练编排选择、保存并重开静态模型</td><td><span class="badge pass">已验证</span></td><td>15.8 秒 · 1 个附件</td></tr><tr><td>truck:场景发布后在真实训练编排选择、保存并重开静态模型</td><td><span class="badge pass">已验证</span></td><td>18.4 秒 · 1 个附件</td></tr><tr><td>仅场景编辑权限:固定模型可加载和保存,不能发布或访问模型页面</td><td><span class="badge pass">已验证</span></td><td>10.3 秒 · 1 个附件</td></tr><tr><td>仅场景发布权限:浏览器发布已存副本,不发送保存或上传请求</td><td><span class="badge pass">已验证</span></td><td>7.7 秒 · 1 个附件</td></tr><tr><td>无场景页面权限:编辑路由和直接内容接口均拒绝访问</td><td><span class="badge pass">已验证</span></td><td>4.8 秒 · 1 个附件</td></tr><tr><td>后台场景未保存时切换角色:关闭确认保留草稿,保存切换后重建只读权限</td><td><span class="badge pass">已验证</span></td><td>15.1 秒 · 2 个附件</td></tr><tr><td>双标签各自修改场景:保存和明确丢弃当前页均保留另一页真实恢复稿</td><td><span class="badge pass">已验证</span></td><td>17.4 秒 · 1 个附件</td></tr><tr><td>工程 JSON 与小 GLB:真实上传、损坏拒绝、引用失败回滚、相机参数往返</td><td><span class="badge pass">已验证</span></td><td>23.0 秒 · 3 个附件</td></tr><tr><td>保存失败与封面降级:503 保留草稿,封面上传失败保留旧图,恢复后可重试</td><td><span class="badge pass">已验证</span></td><td>13.8 秒 · 3 个附件</td></tr><tr><td>同版本恢复与 409:真实未保存草稿重载、服务器新版本保护与隔离下载</td><td><span class="badge pass">已验证</span></td><td>15.9 秒 · 3 个附件</td></tr><tr><td>发布:前端绑定校验、保存与发布失败阻断、真实固定版本供训练目录使用</td><td><span class="badge pass">已验证</span></td><td>14.9 秒 · 3 个附件</td></tr><tr><td>场景模板实际加载、保存重开:真实装备车库</td><td><span class="badge pass">已验证</span></td><td>9.5 秒 · 1 个附件</td></tr><tr><td>场景模板实际加载、保存重开:真实野外训练场</td><td><span class="badge pass">已验证</span></td><td>10.4 秒 · 1 个附件</td></tr><tr><td>场景模板实际加载、保存重开:综合检验车间</td><td><span class="badge pass">已验证</span></td><td>8.9 秒 · 1 个附件</td></tr><tr><td>场景模板实际加载、保存重开:演示维修车间(可拆分)</td><td><span class="badge pass">已验证</span></td><td>16.8 秒 · 1 个附件</td></tr><tr><td>场景模板实际加载、保存重开:总成拆卸车间</td><td><span class="badge pass">已验证</span></td><td>8.8 秒 · 1 个附件</td></tr><tr><td>场景模板实际加载、保存重开:部件分解车间</td><td><span class="badge pass">已验证</span></td><td>8.8 秒 · 1 个附件</td></tr><tr><td>场景模板实际加载、保存重开:清洗检测车间</td><td><span class="badge pass">已验证</span></td><td>8.6 秒 · 1 个附件</td></tr><tr><td>场景模板实际加载、保存重开:复装调试车间</td><td><span class="badge pass">已验证</span></td><td>9.0 秒 · 1 个附件</td></tr><tr><td>场景模板实际加载、保存重开:室外综合训练场</td><td><span class="badge pass">已验证</span></td><td>8.6 秒 · 1 个附件</td></tr><tr><td>模板搜索与未保存切换确认保留和放弃</td><td><span class="badge pass">已验证</span></td><td>7.9 秒 · 1 个附件</td></tr><tr><td>八种基础资源逐项放置、真实拖放、搜索和保存重开</td><td><span class="badge pass">已验证</span></td><td>9.9 秒 · 1 个附件</td></tr><tr><td>两种整体场地加载、重复放置阻止与显式删除后替换</td><td><span class="badge pass">已验证</span></td><td>14.4 秒 · 2 个附件</td></tr><tr><td>模型成果狐狸卡车双击与真实拖入、树搜索和部件隐藏重开</td><td><span class="badge pass">已验证</span></td><td>18.8 秒 · 2 个附件</td></tr><tr><td>真实相机操控、视口拾取、预览禁写和两尺寸主题可读性</td><td><span class="badge pass">已验证</span></td><td>19.0 秒 · 3 个附件</td></tr><tr><td>真实变换手柄移动旋转缩放、吸附与保存重开</td><td><span class="badge pass">已验证</span></td><td>11.7 秒 · 1 个附件</td></tr><tr><td>缓存标签暂停键盘、草稿恢复以及关闭取消和保存失败留页</td><td><span class="badge pass">已验证</span></td><td>7.4 秒 · 1 个附件</td></tr><tr><td>狐狸:真实细分权限、全部属性、清空绑定、蒙皮保护、相机与封面保存重开</td><td><span class="badge pass">已验证</span></td><td>11.3 秒 · 2 个附件</td></tr><tr><td>牛奶卡车:刚性部件独立变换、软删除、历史与保存基线一致</td><td><span class="badge pass">已验证</span></td><td>18.2 秒 · 4 个附件</td></tr><tr><td>新建独立场景:取消、失败、保护原工程以及成功新 ID</td><td><span class="badge pass">已验证</span></td><td>25.5 秒 · 2 个附件</td></tr><tr><td>只读管理员:可浏览预览与导出,模板/资源/键盘/接口不能改场景</td><td><span class="badge pass">已验证</span></td><td>7.1 秒 · 1 个附件</td></tr></tbody></table></div><p><a href="results.json">Playwright JSON</a> · <a href="playwright/index.html">Playwright HTML</a>。同名旧用例或定向重跑不会覆盖当前完整轮失败。</p></article><article class="panel"><h3>构建、后端与聚焦单元测试</h3><div class="scroll"><table><thead><tr><th>分类 / 检查</th><th>结果</th><th>实际数量</th><th>执行命令 / 说明</th></tr></thead><tbody><tr><td>构建与类型检查 · Web 构建与既有模型产物校验</td><td><span class="badge pass">已验证</span></td><td>独立检查,不计入测试总数</td><td>pnpm run build</td></tr><tr><td>后端构建与测试 · Tran 后端完整构建与测试</td><td><span class="badge pass">已验证</span></td><td>373 项测试 · 0 失败</td><td>mvn -pl ut_tran -am test package</td></tr><tr><td>Node 合同与单元 · 场景运行时、资源、关闭守卫与封面合同</td><td><span class="badge pass">已验证</span></td><td>106 项测试</td><td>未记录命令</td></tr><tr><td>Node 合同与单元 · 场景内容 API 客户端合同</td><td><span class="badge pass">已验证</span></td><td>32 项测试</td><td>未记录命令</td></tr></tbody></table></div><p>构建与类型检查:1 / 1 项检查;后端构建与测试:373 / 373 项测试;Node 合同与单元:138 / 138 项测试。上述数量分别来自 validation.json,不计入 E2E 数。</p></article><article class="panel"><h3>数据保留与清理</h3><p>数据清理已验证;临时项目 167 / 167、账号 28 / 28、角色 34 / 34 已清理;原件创作内容、版本、资产与依赖 4 / 4 保持不变。</p><p class="muted">测试副本增删会刷新原件引用统计及更新时间;创作内容、版本、资产和依赖未改写,引用数量已恢复,未回填或伪造原更新时间。</p><div class="scroll"><table><thead><tr><th>原始数据 / 版本</th><th>创作数据比对范围</th><th>引用数量:测试前 → 清理后</th><th>更新时间:测试前 → 清理后</th><th>完整记录</th></tr></thead><tbody><tr><td>实测场景-狐狸野外训练区-20260905<small class="muted"> · 版本 10</small></td><td><span class="badge pass">创作数据保持不变</span><p>全部创作字段、版本、资产、依赖;引用统计与其触发的更新时间单独核对</p></td><td>2 → 2<p><span class="badge pass">引用数量已恢复</span></p></td><td>2026-09-06T00:28:06 → 2026-09-06T18:24:42</td><td>存在派生字段差异;详见前后统计</td></tr><tr><td>实测场景-牛奶卡车检验区-20260905<small class="muted"> · 版本 7</small></td><td><span class="badge pass">创作数据保持不变</span><p>全部创作字段、版本、资产、依赖;引用统计与其触发的更新时间单独核对</p></td><td>2 → 2<p><span class="badge pass">引用数量已恢复</span></p></td><td>2026-09-05T23:52:35 → 2026-09-06T18:25:00</td><td>存在派生字段差异;详见前后统计</td></tr><tr><td>实测模型-狐狸动画-已验证-20260905<small class="muted"> · 版本 15</small></td><td><span class="badge pass">创作数据保持不变</span><p>全部创作字段、版本、资产、依赖;引用统计与其触发的更新时间单独核对</p></td><td>1 → 1<p><span class="badge pass">引用数量已恢复</span></p></td><td>2026-09-05T22:52:25 → 2026-09-06T18:35:18</td><td>存在派生字段差异;详见前后统计</td></tr><tr><td>实测模型-牛奶卡车-已验证-20260905<small class="muted"> · 版本 20</small></td><td><span class="badge pass">创作数据保持不变</span><p>全部创作字段、版本、资产、依赖;引用统计与其触发的更新时间单独核对</p></td><td>1 → 1<p><span class="badge pass">引用数量已恢复</span></p></td><td>2026-09-05T19:32:28 → 2026-09-06T18:35:18</td><td>存在派生字段差异;详见前后统计</td></tr></tbody></table></div><p><a href="cleanup.json">完整清理与保留证据</a> · <a href="artifact-checks.json">最终交付物扫描</a>。</p></article><article class="panel"><h3>逐项证据索引</h3><div class="scroll"><table><thead><tr><th>证据文件</th><th>关联类型 / 结果</th></tr></thead><tbody><tr><td><a href="evidence/basic-assets.json">evidence/basic-assets.json</a></td><td>e2e: PASS</td></tr><tr><td><a href="evidence/downstream-fox.json">evidence/downstream-fox.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/downstream-truck.json">evidence/downstream-truck.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/fox-properties-cover.json">evidence/fox-properties-cover.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/json-glb-roundtrip.json">evidence/json-glb-roundtrip.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/loading-retry.json">evidence/loading-retry.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/new-project.json">evidence/new-project.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/publish-fixed-version.json">evidence/publish-fixed-version.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/published-model-assets.json">evidence/published-model-assets.json</a></td><td>e2e: PASS</td></tr><tr><td><a href="evidence/readonly.json">evidence/readonly.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/real-recovery-conflict.json">evidence/real-recovery-conflict.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/save-concurrency.json">evidence/save-concurrency.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/save-cover-failure.json">evidence/save-cover-failure.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/scene-multi-session.json">evidence/scene-multi-session.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/scene-no-page-permission.json">evidence/scene-no-page-permission.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/scene-publish-only.json">evidence/scene-publish-only.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/scene-role-switch.json">evidence/scene-role-switch.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/scene-update-only.json">evidence/scene-update-only.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/template-confirm.json">evidence/template-confirm.json</a></td><td>e2e: PASS</td></tr><tr><td><a href="evidence/template-depot-1.json">evidence/template-depot-1.json</a></td><td>e2e: PASS</td></tr><tr><td><a href="evidence/template-outdoor-1.json">evidence/template-outdoor-1.json</a></td><td>e2e: PASS</td></tr><tr><td><a href="evidence/template-terrain-1.json">evidence/template-terrain-1.json</a></td><td>e2e: PASS</td></tr><tr><td><a href="evidence/template-workshop-1.json">evidence/template-workshop-1.json</a></td><td>e2e: PASS</td></tr><tr><td><a href="evidence/template-workshop-2.json">evidence/template-workshop-2.json</a></td><td>e2e: PASS</td></tr><tr><td><a href="evidence/template-workshop-3.json">evidence/template-workshop-3.json</a></td><td>e2e: PASS</td></tr><tr><td><a href="evidence/template-workshop-4.json">evidence/template-workshop-4.json</a></td><td>e2e: PASS</td></tr><tr><td><a href="evidence/template-workshop-5.json">evidence/template-workshop-5.json</a></td><td>e2e: PASS</td></tr><tr><td><a href="evidence/template-workshop-real.json">evidence/template-workshop-real.json</a></td><td>e2e: PASS</td></tr><tr><td><a href="evidence/truck-part-history.json">evidence/truck-part-history.json</a></td><td>browser: PASS</td></tr><tr><td><a href="evidence/venue-assets.json">evidence/venue-assets.json</a></td><td>e2e: PASS</td></tr><tr><td><a href="evidence/viewport-cache-close.json">evidence/viewport-cache-close.json</a></td><td>真实UI缓存/关闭+浏览器模拟503拒绝写入: PASS</td></tr><tr><td><a href="evidence/viewport-camera-theme.json">evidence/viewport-camera-theme.json</a></td><td>真实UI+只读投影: PASS</td></tr><tr><td><a href="evidence/viewport-transform.json">evidence/viewport-transform.json</a></td><td>真实UI手柄+只读几何投影定位: PASS</td></tr></tbody></table></div><p><a href="validation.json">构建与最终验证记录</a> · <a href="screenshot-verification.json">截图附件匹配结果</a>。</p><p>最终整轮确认:已完成;最终交付物哈希扫描:通过。</p></article></section>
  10. <section id="boundaries"><div class="eyebrow">范围与产品确认</div><h2>较大调整单列,不纳入本轮通过数</h2><div class="panel"><h3>未挂载正式工作区的能力差异</h3><div class="scroll"><table><thead><tr><th>能力组</th><th>当前边界</th></tr></thead><tbody><tr><td>正式工作区入口</td><td>SceneEditorView.vue → SceneEditorWorkspace.vue 存在实现;当前场景路由不引用,不能纳入默认入口已通过数。</td></tr><tr><td>对象管理</td><td>正式工作区有层级折叠、锁定、复制、软删除恢复按钮;默认 legacy 大纲没有对应全部控件。</td></tr><tr><td>变换和相机</td><td>正式工作区有局部/世界空间、自定义移动吸附、前视/右视、双击与 F 聚焦;默认入口只提供既有 W/E/R、固定吸附、顶视/透视/聚焦。</td></tr><tr><td>材质和阴影</td><td>正式工作区提供颜色、透明度、投射/接收阴影;默认场景属性没有这些编辑控件,只还原模型发布数据。</td></tr><tr><td>原生动画</td><td>正式工作区有片段、速度、自动播放、循环和播放按钮;默认 legacy 场景没有这些控件,也未在 animate 中播放模型原生动画。</td></tr><tr><td>运行组件</td><td>正式工作区有持续旋转/往复/呼吸缩放/热点组件及启用、名称、轴、速度、幅度;默认入口未显示组件页签。</td></tr><tr><td>语义扩展</td><td>正式工作区有交互标识、标签、能力清单与资源引用详情;默认只有显示名、可见、实训目标、设备/物模型编号和语义类型。</td></tr><tr><td>环境渲染编辑</td><td>正式工作区有背景/透明背景/曝光/环境强度、半球光/主光、雾范围、车间剖切;默认入口只保存/恢复文档中的渲染参数。</td></tr><tr><td>底部面板</td><td>正式工作区有真实日志、绑定状态、问题定位、底部折叠;默认入口对应基线有静态信息和六项对象摘要。</td></tr></tbody></table></div><p><a href="../../../unreal_tran_web/src/features/editors/scene/SceneEditorWorkspace.vue">SceneEditorWorkspace.vue 源码参考</a>。没有可达 UI 的功能不臆造测试、不自动迁入默认入口。</p></div><article class="panel decision"><span class="eyebrow">SCENE-D01</span><h3>保留的正式工作区额外控件是否迁入默认入口</h3><dl><dt>当前行为</dt><dd>默认入口为 legacy 场景编辑器。另一份未挂载工作区的额外渲染、组件、动画和布局控件不能计入当前 UI 已闭环。</dd><dt>建议确认的范围</dt><dd>单独确认所需控件清单和入口迁移范围,优先保留已有场景文档、权限、封面、固定版本与恢复机制。</dd><dt>可观察验收</dt><dd>所选入口只有一条业务真值链路;既有场景往返不丢字段,权限、导入、保存、发布和截图均完成独立端到端回归。</dd></dl></article><article class="panel decision"><span class="eyebrow">SCENE-D02</span><h3>下游自动执行来源模型时间轴和蓝图</h3><dl><dt>当前行为</dt><dd>场景当前导入模型几何、部件状态和材质;静态预览不能证明来源模型动作或蓝图在场景和训练中执行。</dd><dt>建议确认的范围</dt><dd>沿用前轮模型审查的大范围待确认项,单独定义固定版本的运行状态、触发事件、权限、生命周期与训练结果追踪。</dd><dt>可观察验收</dt><dd>用明确的动作样例验证导入、发布、场景预览、训练运行、停止重置和结果记录;不能只以模型可见或接口引用通过验收。</dd></dl></article></section>
  11. <section id="screenshots"><div class="eyebrow">实际截图</div><h2>52 张原图 · 点击放大</h2><p class="muted">失败注入、加载遮罩、环境全景和实际模型可见证据分别写明。故意移动/缩放部件的测试姿态不能误判为加载丢失。</p><div class="gallery"><figure><a class="shot" href="screenshots/01-fox-actual-scene.png" data-caption="原场景副本中实际加载的狐狸"><img src="screenshots/01-fox-actual-scene.png" alt="原场景副本中实际加载的狐狸" loading="lazy"></a><figcaption><strong>1. 原场景副本中实际加载的狐狸</strong> <span class="badge pass">已验证</span><p>狐狸来源固定模型版本,聚焦后核对真实几何。原场景已有模板标识与地形对象不一致,未擅自推断并改写原件。</p><small class="muted">01-fox-actual-scene.png</small></figcaption></figure><figure><a class="shot" href="screenshots/02-fox-reopened-skin-protection.png" data-caption="狐狸属性保存重开与蒙皮部件保护"><img src="screenshots/02-fox-reopened-skin-protection.png" alt="狐狸属性保存重开与蒙皮部件保护" loading="lazy"></a><figcaption><strong>2. 狐狸属性保存重开与蒙皮部件保护</strong> <span class="badge pass">已验证</span><p>位置、旋转、小数缩放和空绑定保存重开;狐狸蒙皮部件显示只可查看与高亮,独立变换/隐藏禁止。姿态为属性测试值。</p><small class="muted">02-fox-reopened-skin-protection.png</small></figcaption></figure><figure><a class="shot" href="screenshots/03-truck-part-history-reopened.png" data-caption="卡车部件编辑和历史恢复后的场景"><img src="screenshots/03-truck-part-history-reopened.png" alt="卡车部件编辑和历史恢复后的场景" loading="lazy"></a><figcaption><strong>3. 卡车部件编辑和历史恢复后的场景</strong> <span class="badge pass">已验证</span><p>刚性部件做过独立位移、软删除与撤销恢复;保留的测试姿态不应误判为模型加载损坏,具体值以文档和刷新断言为准。</p><small class="muted">03-truck-part-history-reopened.png</small></figcaption></figure><figure><a class="shot" href="screenshots/04-new-failure-original-preserved.png" data-caption="新建失败时原场景和恢复保护仍保留"><img src="screenshots/04-new-failure-original-preserved.png" alt="新建失败时原场景和恢复保护仍保留" loading="lazy"></a><figcaption><strong>4. 新建失败时原场景和恢复保护仍保留</strong> <span class="badge pass">已验证</span><p>定向注入创建 POST 503,失败提示为预期分支。用例分别检查保存与选择放弃两种路径;选择放弃也不会在创建失败时清除原 dirty/恢复稿。最终成功创建后,重新打开旧缓存页应显示服务端内容。具体分支以最终断言为准。</p><small class="muted">04-new-failure-original-preserved.png</small></figcaption></figure><figure><a class="shot" href="screenshots/05-new-separate-scene.png" data-caption="真正创建独立场景并保存重开"><img src="screenshots/05-new-separate-scene.png" alt="真正创建独立场景并保存重开" loading="lazy"></a><figcaption><strong>5. 真正创建独立场景并保存重开</strong> <span class="badge pass">已验证</span><p>新项目具有独立 ID,初始空白模板正常;原项目内容另通过 API 核对保留。</p><small class="muted">05-new-separate-scene.png</small></figcaption></figure><figure><a class="shot" href="screenshots/06-readonly-scene-permissions.png" data-caption="只读管理员浏览场景"><img src="screenshots/06-readonly-scene-permissions.png" alt="只读管理员浏览场景" loading="lazy"></a><figcaption><strong>6. 只读管理员浏览场景</strong> <span class="badge pass">已验证</span><p>更新、导入、新建、发布等写入口禁用;查看、预览和导出仍可用,直接内容 PUT 返回 403。</p><small class="muted">06-readonly-scene-permissions.png</small></figcaption></figure><figure><a class="shot" href="screenshots/07-garage-full.png" data-caption="车库围护完整显示"><img src="screenshots/07-garage-full.png" alt="车库围护完整显示" loading="lazy"></a><figcaption><strong>7. 车库围护完整显示</strong> <span class="badge pass">已验证</span><p>实际点击完整模式并核对导出字段;建筑墙体、屋顶与支柱可能遮住内部模型,属于本模式显示范围。</p><small class="muted">07-garage-full.png</small></figcaption></figure><figure><a class="shot" href="screenshots/07-garage-hidden.png" data-caption="车库围护隐藏"><img src="screenshots/07-garage-hidden.png" alt="车库围护隐藏" loading="lazy"></a><figcaption><strong>8. 车库围护隐藏</strong> <span class="badge pass">已验证</span><p>实际点击隐藏外壳,导出后保存并刷新验证模式保留;内部对象不会因为围护隐藏而删除。</p><small class="muted">07-garage-hidden.png</small></figcaption></figure><figure><a class="shot" href="screenshots/07-garage-xray.png" data-caption="车库围护透明查看"><img src="screenshots/07-garage-xray.png" alt="车库围护透明查看" loading="lazy"></a><figcaption><strong>9. 车库围护透明查看</strong> <span class="badge pass">已验证</span><p>实际点击透明模式,导出 enclosureMode 同步;透明围护与保留的结构柱区别于模型未加载。</p><small class="muted">07-garage-xray.png</small></figcaption></figure><figure><a class="shot" href="screenshots/10-invalid-reference-rolled-back.png" data-caption="错误资源编码被拒绝并保留场景"><img src="screenshots/10-invalid-reference-rolled-back.png" alt="错误资源编码被拒绝并保留场景" loading="lazy"></a><figcaption><strong>10. 错误资源编码被拒绝并保留场景</strong> <span class="badge pass">已验证</span><p>JSON 明确引用不存在的 assetCode,预期报错;原场景对象仍保留,未回退为其他模型文件。</p><small class="muted">10-invalid-reference-rolled-back.png</small></figcaption></figure><figure><a class="shot" href="screenshots/11-small-glb-real-upload.png" data-caption="小 GLB 真实上传进入当前场景"><img src="screenshots/11-small-glb-real-upload.png" alt="小 GLB 真实上传进入当前场景" loading="lazy"></a><figcaption><strong>11. 小 GLB 真实上传进入当前场景</strong> <span class="badge pass">已验证</span><p>通过文件输入上传测试 Box.glb,受控资产登记后加入场景;与之前故意损坏文件/上传503分支分开验收。</p><small class="muted">11-small-glb-real-upload.png</small></figcaption></figure><figure><a class="shot" href="screenshots/12-json-glb-reopened.png" data-caption="JSON 与受控 GLB 保存刷新往返"><img src="screenshots/12-json-glb-reopened.png" alt="JSON 与受控 GLB 保存刷新往返" loading="lazy"></a><figcaption><strong>12. JSON 与受控 GLB 保存刷新往返</strong> <span class="badge pass">已验证</span><p>有效工程导入后的对象、受控 URI 和渲染参数重开保持。画面相机为测试导入值,不强行套用默认视角。</p><small class="muted">12-json-glb-reopened.png</small></figcaption></figure><figure><a class="shot" href="screenshots/13-save-failure-keeps-draft.png" data-caption="保存 PUT 503 时保留未存稿"><img src="screenshots/13-save-failure-keeps-draft.png" alt="保存 PUT 503 时保留未存稿" loading="lazy"></a><figcaption><strong>13. 保存 PUT 503 时保留未存稿</strong> <span class="badge pass">已验证</span><p>注入保存失败,版本未前进且 dirty 保留;红色/警告提示为故障恢复验收的预期画面。</p><small class="muted">13-save-failure-keeps-draft.png</small></figcaption></figure><figure><a class="shot" href="screenshots/14-cover-failure-keeps-old-image.png" data-caption="封面上传失败时保留旧图"><img src="screenshots/14-cover-failure-keeps-old-image.png" alt="封面上传失败时保留旧图" loading="lazy"></a><figcaption><strong>14. 封面上传失败时保留旧图</strong> <span class="badge pass">已验证</span><p>封面上传被注入503,正文仍可成功保存,真实提示封面降级;不把此步骤记为新封面生成成功。</p><small class="muted">14-cover-failure-keeps-old-image.png</small></figcaption></figure><figure><a class="shot" href="screenshots/15-save-retry-success.png" data-caption="恢复服务后重新保存成功"><img src="screenshots/15-save-retry-success.png" alt="恢复服务后重新保存成功" loading="lazy"></a><figcaption><strong>15. 恢复服务后重新保存成功</strong> <span class="badge pass">已验证</span><p>恢复请求后再次修改并保存、刷新核对内容;先前故障提示若仍处正常消退周期,不改变当前响应和刷新断言。</p><small class="muted">15-save-retry-success.png</small></figcaption></figure><figure><a class="shot" href="screenshots/16-real-unsaved-recovery.png" data-caption="真实未保存输入恢复到编辑器"><img src="screenshots/16-real-unsaved-recovery.png" alt="真实未保存输入恢复到编辑器" loading="lazy"></a><figcaption><strong>16. 真实未保存输入恢复到编辑器</strong> <span class="badge pass">已验证</span><p>由实际输入产生本地恢复稿,刷新后明确恢复;恢复仅回到当前编辑视口,未自动写入服务器。</p><small class="muted">16-real-unsaved-recovery.png</small></figcaption></figure><figure><a class="shot" href="screenshots/17-conflict-does-not-overwrite.png" data-caption="409 版本冲突不覆盖最新内容"><img src="screenshots/17-conflict-does-not-overwrite.png" alt="409 版本冲突不覆盖最新内容" loading="lazy"></a><figcaption><strong>17. 409 版本冲突不覆盖最新内容</strong> <span class="badge pass">已验证</span><p>另一请求先保存新版本,当前旧会话再保存被拒绝;未存修改保留,不能显示保存成功。</p><small class="muted">17-conflict-does-not-overwrite.png</small></figcaption></figure><figure><a class="shot" href="screenshots/18-stale-recovery-explicit-decision.png" data-caption="旧版本恢复稿隔离与下载决策"><img src="screenshots/18-stale-recovery-explicit-decision.png" alt="旧版本恢复稿隔离与下载决策" loading="lazy"></a><figcaption><strong>18. 旧版本恢复稿隔离与下载决策</strong> <span class="badge pass">已验证</span><p>旧稿不能直接覆盖最新服务端版本,需明确下载/保留或丢弃;此图用于验证保护提示。</p><small class="muted">18-stale-recovery-explicit-decision.png</small></figcaption></figure><figure><a class="shot" href="screenshots/19-publish-failure-stays-draft.png" data-caption="发布接口故障仍保持草稿"><img src="screenshots/19-publish-failure-stays-draft.png" alt="发布接口故障仍保持草稿" loading="lazy"></a><figcaption><strong>19. 发布接口故障仍保持草稿</strong> <span class="badge pass">已验证</span><p>定向注入发布 POST 503;保存和发布为独立结果,项目状态保持 DRAFT,后续恢复才真正发布。</p><small class="muted">19-publish-failure-stays-draft.png</small></figcaption></figure><figure><a class="shot" href="screenshots/20-scene-published-fixed-version.png" data-caption="场景发布完成并固定模型依赖"><img src="screenshots/20-scene-published-fixed-version.png" alt="场景发布完成并固定模型依赖" loading="lazy"></a><figcaption><strong>20. 场景发布完成并固定模型依赖</strong> <span class="badge pass">已验证</span><p>发布 POST 成功、当前版本与 publishedVersionId 一致;模型来源固定到既有发布版本。</p><small class="muted">20-scene-published-fixed-version.png</small></figcaption></figure><figure><a class="shot" href="screenshots/20-template-depot-1.png" data-caption="真实装备车库模板保存重开"><img src="screenshots/20-template-depot-1.png" alt="真实装备车库模板保存重开" loading="lazy"></a><figcaption><strong>21. 真实装备车库模板保存重开</strong> <span class="badge pass">已验证</span><p>独立副本通过模板按钮加载真实车库,保存后刷新;左侧模板选中状态和画面环境同时核对。</p><small class="muted">20-template-depot-1.png</small></figcaption></figure><figure><a class="shot" href="screenshots/21-published-scene-cover-in-list.png" data-caption="返回列表查看实际场景封面"><img src="screenshots/21-published-scene-cover-in-list.png" alt="返回列表查看实际场景封面" loading="lazy"></a><figcaption><strong>22. 返回列表查看实际场景封面</strong> <span class="badge pass">已验证</span><p>返回列表保留编辑器缓存标签;搜索当前项目并验证图片 naturalWidth 大于零,封面来自服务端受控资产。</p><small class="muted">21-published-scene-cover-in-list.png</small></figcaption></figure><figure><a class="shot" href="screenshots/21-template-terrain-1.png" data-caption="真实野外训练场模板保存重开"><img src="screenshots/21-template-terrain-1.png" alt="真实野外训练场模板保存重开" loading="lazy"></a><figcaption><strong>23. 真实野外训练场模板保存重开</strong> <span class="badge pass">已验证</span><p>真实起伏地形和默认装备、区域、照明种子对象;不把默认装备占位模型误称为狐狸。</p><small class="muted">21-template-terrain-1.png</small></figcaption></figure><figure><a class="shot" href="screenshots/22-template-workshop-1.png" data-caption="综合检验车间的空白场地"><img src="screenshots/22-template-workshop-1.png" alt="综合检验车间的空白场地" loading="lazy"></a><figcaption><strong>24. 综合检验车间的空白场地</strong> <span class="badge pass">已验证</span><p>此模板设计为锁定地板和零业务对象,空白是正常模板结果;保存刷新后仍为零对象。</p><small class="muted">22-template-workshop-1.png</small></figcaption></figure><figure><a class="shot" href="screenshots/23-template-workshop-real.png" data-caption="演示维修车间的 74 个独立部件"><img src="screenshots/23-template-workshop-real.png" alt="演示维修车间的 74 个独立部件" loading="lazy"></a><figcaption><strong>25. 演示维修车间的 74 个独立部件</strong> <span class="badge pass">已验证</span><p>通过可拆分模板加载,74 个场景对象保存重开;与整体装备车库为不同资源。</p><small class="muted">23-template-workshop-real.png</small></figcaption></figure><figure><a class="shot" href="screenshots/24-template-workshop-2.png" data-caption="总成拆卸车间模板"><img src="screenshots/24-template-workshop-2.png" alt="总成拆卸车间模板" loading="lazy"></a><figcaption><strong>26. 总成拆卸车间模板</strong> <span class="badge pass">已验证</span><p>装备主体、工作台和存放架三种种子资源,模板选择、类型和保存刷新一致。</p><small class="muted">24-template-workshop-2.png</small></figcaption></figure><figure><a class="shot" href="screenshots/25-template-workshop-3.png" data-caption="部件分解车间模板"><img src="screenshots/25-template-workshop-3.png" alt="部件分解车间模板" loading="lazy"></a><figcaption><strong>27. 部件分解车间模板</strong> <span class="badge pass">已验证</span><p>工作台、存放架和视觉采集设备三种种子资源保存重开。</p><small class="muted">25-template-workshop-3.png</small></figcaption></figure><figure><a class="shot" href="screenshots/26-template-workshop-4.png" data-caption="清洗检测车间模板"><img src="screenshots/26-template-workshop-4.png" alt="清洗检测车间模板" loading="lazy"></a><figcaption><strong>28. 清洗检测车间模板</strong> <span class="badge pass">已验证</span><p>诊断工位、工作台、采集设备和安全区域四种种子资源保存重开。</p><small class="muted">26-template-workshop-4.png</small></figcaption></figure><figure><a class="shot" href="screenshots/27-template-workshop-5.png" data-caption="复装调试车间模板"><img src="screenshots/27-template-workshop-5.png" alt="复装调试车间模板" loading="lazy"></a><figcaption><strong>29. 复装调试车间模板</strong> <span class="badge pass">已验证</span><p>诊断工位、工作台、照明和人员占位四种种子资源保存重开;占位不代表真实设备接入。</p><small class="muted">27-template-workshop-5.png</small></figcaption></figure><figure><a class="shot" href="screenshots/28-template-outdoor-1.png" data-caption="室外综合训练场模板"><img src="screenshots/28-template-outdoor-1.png" alt="室外综合训练场模板" loading="lazy"></a><figcaption><strong>30. 室外综合训练场模板</strong> <span class="badge pass">已验证</span><p>程序化室外场地及三种种子资源,区别于正式起伏地形模板。</p><small class="muted">28-template-outdoor-1.png</small></figcaption></figure><figure><a class="shot" href="screenshots/29-template-confirm-empty.png" data-caption="模板切换确认与空白模板保存"><img src="screenshots/29-template-confirm-empty.png" alt="模板切换确认与空白模板保存" loading="lazy"></a><figcaption><strong>31. 模板切换确认与空白模板保存</strong> <span class="badge pass">已验证</span><p>先取消切换确认保留未存对象,再明确放弃并切换;图中空场地是确认后的正常结果。</p><small class="muted">29-template-confirm-empty.png</small></figcaption></figure><figure><a class="shot" href="screenshots/30-eight-basic-assets-reopened.png" data-caption="八种基础资源逐项放置并刷新"><img src="screenshots/30-eight-basic-assets-reopened.png" alt="八种基础资源逐项放置并刷新" loading="lazy"></a><figcaption><strong>32. 八种基础资源逐项放置并刷新</strong> <span class="badge pass">已验证</span><p>8 种资源分别双击并分开摆放,第 9 个工作台通过真实鼠标拖入;保存后核对各对象位置和唯一编号。</p><small class="muted">30-eight-basic-assets-reopened.png</small></figcaption></figure><figure><a class="shot" href="screenshots/31-venue-depot-reopened.png" data-caption="资源库车库作为整体场地"><img src="screenshots/31-venue-depot-reopened.png" alt="资源库车库作为整体场地" loading="lazy"></a><figcaption><strong>33. 资源库车库作为整体场地</strong> <span class="badge pass">已验证</span><p>从空白模板的资源库加载车库并保存重开;已有整体场地时阻止重复叠加。</p><small class="muted">31-venue-depot-reopened.png</small></figcaption></figure><figure><a class="shot" href="screenshots/32-venue-terrain-reopened.png" data-caption="显式删除旧场地后加载真实地形"><img src="screenshots/32-venue-terrain-reopened.png" alt="显式删除旧场地后加载真实地形" loading="lazy"></a><figcaption><strong>34. 显式删除旧场地后加载真实地形</strong> <span class="badge pass">已验证</span><p>先删除车库,再加入地形;保存刷新确认新场地资产引用与起伏高度,未静默覆盖原场地。</p><small class="muted">32-venue-terrain-reopened.png</small></figcaption></figure><figure><a class="shot" href="screenshots/33-fox-placed-visible.png" data-caption="API 已发布狐狸模型双击放置"><img src="screenshots/33-fox-placed-visible.png" alt="API 已发布狐狸模型双击放置" loading="lazy"></a><figcaption><strong>35. API 已发布狐狸模型双击放置</strong> <span class="badge pass">已验证</span><p>真实资源卡片双击导入当前实例,移动到 X=-3 并聚焦;保留来源项目和已发布固定版本。</p><small class="muted">33-fox-placed-visible.png</small></figcaption></figure><figure><a class="shot" href="screenshots/34-truck-placed-part-restored.png" data-caption="真实拖入卡车并完成部件隐藏往返"><img src="screenshots/34-truck-placed-part-restored.png" alt="真实拖入卡车并完成部件隐藏往返" loading="lazy"></a><figcaption><strong>36. 真实拖入卡车并完成部件隐藏往返</strong> <span class="badge pass">已验证</span><p>卡车通过鼠标拖入;刚体部件隐藏保存刷新后再恢复可见,取消高亮后展示完整材质。聚焦卡车导致左侧狐狸部分出画,不是狐狸加载丢失。</p><small class="muted">34-truck-placed-part-restored.png</small></figcaption></figure><figure><a class="shot" href="screenshots/40-viewport-dark-1920.png" data-caption="1920 暗色主题的真实视口操作"><img src="screenshots/40-viewport-dark-1920.png" alt="1920 暗色主题的真实视口操作" loading="lazy"></a><figcaption><strong>37. 1920 暗色主题的真实视口操作</strong> <span class="badge pass">已验证</span><p>实际轨道旋转、右键平移、滚轮和点击拾取后检查侧栏、属性和视口;定位钩子只读几何,不修改场景。</p><small class="muted">40-viewport-dark-1920.png</small></figcaption></figure><figure><a class="shot" href="screenshots/41-viewport-light-1440.png" data-caption="1440 军工绿主题的完整编辑布局"><img src="screenshots/41-viewport-light-1440.png" alt="1440 军工绿主题的完整编辑布局" loading="lazy"></a><figcaption><strong>38. 1440 军工绿主题的完整编辑布局</strong> <span class="badge pass">已验证</span><p>真实切换主题和浏览器尺寸,检查长工具栏、画布与右侧属性栏不会互相遮挡。</p><small class="muted">41-viewport-light-1440.png</small></figcaption></figure><figure><a class="shot" href="screenshots/42-fox-preview-original-material.png" data-caption="狐狸运行预览与原材质"><img src="screenshots/42-fox-preview-original-material.png" alt="狐狸运行预览与原材质" loading="lazy"></a><figcaption><strong>39. 狐狸运行预览与原材质</strong> <span class="badge pass">已验证</span><p>进入静态场景预览,隐藏选中高亮和手柄并禁止写入;黄色狐狸可见不代表原生动画或蓝图已执行。</p><small class="muted">42-fox-preview-original-material.png</small></figcaption></figure><figure><a class="shot" href="screenshots/43-real-transform-gizmo.png" data-caption="真实鼠标拖动三种变换手柄"><img src="screenshots/43-real-transform-gizmo.png" alt="真实鼠标拖动三种变换手柄" loading="lazy"></a><figcaption><strong>40. 真实鼠标拖动三种变换手柄</strong> <span class="badge pass">已验证</span><p>移动、旋转和单轴缩放后的故意测试姿态;通过真实鼠标命中手柄,只读投影仅用于定位,保存重开再核对数值。</p><small class="muted">43-real-transform-gizmo.png</small></figcaption></figure><figure><a class="shot" href="screenshots/44-cache-close-save-failure.png" data-caption="后台缓存和关闭保存失败保留编辑器"><img src="screenshots/44-cache-close-save-failure.png" alt="后台缓存和关闭保存失败保留编辑器" loading="lazy"></a><figcaption><strong>41. 后台缓存和关闭保存失败保留编辑器</strong> <span class="badge pass">已验证</span><p>切标签保留未存内容、后台快捷键不修改对象;关闭时保存503为预期故障注入,当前场景和未存稿保留。</p><small class="muted">44-cache-close-save-failure.png</small></figcaption></figure><figure><a class="shot" href="screenshots/51-scene-update-only-saved.png" data-caption="只有场景更新权限仍可保存固定模型"><img src="screenshots/51-scene-update-only-saved.png" alt="只有场景更新权限仍可保存固定模型" loading="lazy"></a><figcaption><strong>42. 只有场景更新权限仍可保存固定模型</strong> <span class="badge pass">已验证</span><p>已有固定依赖通过场景授权读取并保存刷新;无模型页面权限,直接来源项目访问和发布接口仍拒绝。</p><small class="muted">51-scene-update-only-saved.png</small></figcaption></figure><figure><a class="shot" href="screenshots/52-scene-publish-only-no-save.png" data-caption="只有发布权限发布已保存版本"><img src="screenshots/52-scene-publish-only-no-save.png" alt="只有发布权限发布已保存版本" loading="lazy"></a><figcaption><strong>43. 只有发布权限发布已保存版本</strong> <span class="badge pass">已验证</span><p>真实浏览器仅发送一个发布 POST,没有内容 PUT 或封面上传;发布权限不扩大为编辑权限。</p><small class="muted">52-scene-publish-only-no-save.png</small></figcaption></figure><figure><a class="shot" href="screenshots/53-scene-route-access-denied.png" data-caption="没有场景页面权限时拒绝入口"><img src="screenshots/53-scene-route-access-denied.png" alt="没有场景页面权限时拒绝入口" loading="lazy"></a><figcaption><strong>44. 没有场景页面权限时拒绝入口</strong> <span class="badge pass">已验证</span><p>编辑路由被拦截且直接内容接口返回403;画面不是场景加载成功的证据。</p><small class="muted">53-scene-route-access-denied.png</small></figcaption></figure><figure><a class="shot" href="screenshots/54-scene-role-switch-cancel-keeps-draft.png" data-caption="取消切角色保留后台场景草稿"><img src="screenshots/54-scene-role-switch-cancel-keeps-draft.png" alt="取消切角色保留后台场景草稿" loading="lazy"></a><figcaption><strong>45. 取消切角色保留后台场景草稿</strong> <span class="badge pass">已验证</span><p>关闭未存确认后没有角色 PUT、没有保存请求;返回场景标签仍保留编辑名称和 dirty。</p><small class="muted">54-scene-role-switch-cancel-keeps-draft.png</small></figcaption></figure><figure><a class="shot" href="screenshots/55-scene-role-switch-saved-readonly.png" data-caption="保存后切换角色并重建只读权限"><img src="screenshots/55-scene-role-switch-saved-readonly.png" alt="保存后切换角色并重建只读权限" loading="lazy"></a><figcaption><strong>46. 保存后切换角色并重建只读权限</strong> <span class="badge pass">已验证</span><p>明确保存后才切换角色,重新启动拉取新权限;场景名称保存成功,更新和发布入口按新角色禁用。</p><small class="muted">55-scene-role-switch-saved-readonly.png</small></figcaption></figure><figure><a class="shot" href="screenshots/56-scene-other-tab-recovery-preserved.png" data-caption="另一浏览器页的真实恢复稿仍保留"><img src="screenshots/56-scene-other-tab-recovery-preserved.png" alt="另一浏览器页的真实恢复稿仍保留" loading="lazy"></a><figcaption><strong>47. 另一浏览器页的真实恢复稿仍保留</strong> <span class="badge pass">已验证</span><p>A 页保存并明确删除自己的恢复稿,B 页实际输入的未存稿及版本仍保留;未通过伪造 localStorage 构造该分支。</p><small class="muted">56-scene-other-tab-recovery-preserved.png</small></figcaption></figure><figure><a class="shot" href="screenshots/60-scene-load-failure-retry.png" data-caption="加载失败保留中性状态和重试入口"><img src="screenshots/60-scene-load-failure-retry.png" alt="加载失败保留中性状态和重试入口" loading="lazy"></a><figcaption><strong>48. 加载失败保留中性状态和重试入口</strong> <span class="badge pass">已验证</span><p>项目 GET 503 注入后不展示可写半场景;错误详情由短消息提供,页面仍能重新加载。</p><small class="muted">60-scene-load-failure-retry.png</small></figcaption></figure><figure><a class="shot" href="screenshots/61-model-loading-blocks-partial-save.png" data-caption="模型下载未完成时禁止半提交"><img src="screenshots/61-model-loading-blocks-partial-save.png" alt="模型下载未完成时禁止半提交" loading="lazy"></a><figcaption><strong>49. 模型下载未完成时禁止半提交</strong> <span class="badge pass">已验证</span><p>真实延迟受控资源下载,视口显示加载状态,保存/导出/发布等操作禁用,关闭被阻止;释放请求后再验证完整加载。</p><small class="muted">61-model-loading-blocks-partial-save.png</small></figcaption></figure><figure><a class="shot" href="screenshots/62-new-edits-preserved-during-save.png" data-caption="保存快照 A 期间编辑 B 仍保留"><img src="screenshots/62-new-edits-preserved-during-save.png" alt="保存快照 A 期间编辑 B 仍保留" loading="lazy"></a><figcaption><strong>50. 保存快照 A 期间编辑 B 仍保留</strong> <span class="badge pass">已验证</span><p>延迟PUT后继续修改名称,先完成的请求只保存A;新修改B保持dirty,重复快捷键不重复提交,随后保存刷新核对B。</p><small class="muted">62-new-edits-preserved-during-save.png</small></figcaption></figure><figure><a class="shot" href="screenshots/70-fox-training-scene-reopened.png" data-caption="新发布狐狸场景在训练编排中重开"><img src="screenshots/70-fox-training-scene-reopened.png" alt="新发布狐狸场景在训练编排中重开" loading="lazy"></a><figcaption><strong>51. 新发布狐狸场景在训练编排中重开</strong> <span class="badge pass">已验证</span><p>独立训练副本真实选择新发布场景,保存 TRAINING_SCENE 固定版本并刷新,橙黄色狐狸实际可见;仅验证静态还原。</p><small class="muted">70-fox-training-scene-reopened.png</small></figcaption></figure><figure><a class="shot" href="screenshots/70-truck-training-scene-reopened.png" data-caption="新发布卡车场景在训练编排中重开"><img src="screenshots/70-truck-training-scene-reopened.png" alt="新发布卡车场景在训练编排中重开" loading="lazy"></a><figcaption><strong>52. 新发布卡车场景在训练编排中重开</strong> <span class="badge pass">已验证</span><p>独立训练副本绑定新场景固定版本后保存刷新,卡车实际可见;默认车库支柱局部遮挡车身,未将遮挡误认为模型丢失。</p><small class="muted">70-truck-training-scene-reopened.png</small></figcaption></figure></div></section><section><div class="panel links"><a href="feature-catalog.json">feature-catalog.json</a><a href="coverage.json">coverage.json</a><a href="coverage-plan.md">coverage-plan.md</a><a href="data/coverage-test-plan.json">data/coverage-test-plan.json</a><a href="data/inventory-scene.md">data/inventory-scene.md</a><a href="data/baseline-findings.json">data/baseline-findings.json</a><a href="decisions.json">decisions.json</a><a href="fixes.json">fixes.json</a><a href="results.json">results.json</a><a href="validation.json">validation.json</a><a href="cleanup.json">cleanup.json</a><a href="artifact-checks.json">artifact-checks.json</a><a href="screenshot-verification.json">screenshot-verification.json</a><a href="screenshot-captions.json">screenshot-captions.json</a><a href="covers/fox.jpg">covers/fox.jpg</a><a href="playwright/index.html">playwright/index.html</a><a href="evidence/basic-assets.json">evidence/basic-assets.json</a><a href="evidence/downstream-fox.json">evidence/downstream-fox.json</a><a href="evidence/downstream-truck.json">evidence/downstream-truck.json</a><a href="evidence/fox-properties-cover.json">evidence/fox-properties-cover.json</a><a href="evidence/json-glb-roundtrip.json">evidence/json-glb-roundtrip.json</a><a href="evidence/loading-retry.json">evidence/loading-retry.json</a><a href="evidence/new-project.json">evidence/new-project.json</a><a href="evidence/publish-fixed-version.json">evidence/publish-fixed-version.json</a><a href="evidence/published-model-assets.json">evidence/published-model-assets.json</a><a href="evidence/readonly.json">evidence/readonly.json</a><a href="evidence/real-recovery-conflict.json">evidence/real-recovery-conflict.json</a><a href="evidence/save-concurrency.json">evidence/save-concurrency.json</a><a href="evidence/save-cover-failure.json">evidence/save-cover-failure.json</a><a href="evidence/scene-multi-session.json">evidence/scene-multi-session.json</a><a href="evidence/scene-no-page-permission.json">evidence/scene-no-page-permission.json</a><a href="evidence/scene-publish-only.json">evidence/scene-publish-only.json</a><a href="evidence/scene-role-switch.json">evidence/scene-role-switch.json</a><a href="evidence/scene-update-only.json">evidence/scene-update-only.json</a><a href="evidence/template-confirm.json">evidence/template-confirm.json</a><a href="evidence/template-depot-1.json">evidence/template-depot-1.json</a><a href="evidence/template-outdoor-1.json">evidence/template-outdoor-1.json</a><a href="evidence/template-terrain-1.json">evidence/template-terrain-1.json</a><a href="evidence/template-workshop-1.json">evidence/template-workshop-1.json</a><a href="evidence/template-workshop-2.json">evidence/template-workshop-2.json</a><a href="evidence/template-workshop-3.json">evidence/template-workshop-3.json</a><a href="evidence/template-workshop-4.json">evidence/template-workshop-4.json</a><a href="evidence/template-workshop-5.json">evidence/template-workshop-5.json</a><a href="evidence/template-workshop-real.json">evidence/template-workshop-real.json</a><a href="evidence/truck-part-history.json">evidence/truck-part-history.json</a><a href="evidence/venue-assets.json">evidence/venue-assets.json</a><a href="evidence/viewport-cache-close.json">evidence/viewport-cache-close.json</a><a href="evidence/viewport-camera-theme.json">evidence/viewport-camera-theme.json</a><a href="evidence/viewport-transform.json">evidence/viewport-transform.json</a></div></section><footer>生成命令:node tools/build-scene-editor-closure-report.mjs。仅处理此场景报告;原模型报告不改动。没有当前轮关联证据的功能保持 PENDING。</footer></main><dialog id="viewer" aria-label="截图放大"><div class="viewer-bar"><span id="caption"></span><div><a id="original" target="_blank" rel="noopener">原图</a><button id="close" type="button">关闭 ×</button></div></div><img alt=""></dialog><script>
  12. const search=document.getElementById('search'),state=document.getElementById('state');function filter(){let total=0;document.querySelectorAll('[data-state]').forEach(row=>{const show=(state.value==='all'||state.value===row.dataset.state)&&row.textContent.toLowerCase().includes(search.value.toLowerCase());row.classList.toggle('hidden',!show);if(show)total++});document.querySelectorAll('[data-group]').forEach(group=>group.classList.toggle('hidden',!group.querySelector('[data-state]:not(.hidden)')));document.getElementById('visible').textContent=total+' 项'}search.addEventListener('input',filter);state.addEventListener('change',filter);const viewer=document.getElementById('viewer');document.querySelectorAll('.shot').forEach(anchor=>anchor.addEventListener('click',event=>{event.preventDefault();viewer.querySelector('img').src=anchor.href;viewer.querySelector('img').alt=anchor.dataset.caption;document.getElementById('caption').textContent=anchor.dataset.caption;document.getElementById('original').href=anchor.href;viewer.showModal()}));document.getElementById('close').addEventListener('click',()=>viewer.close());viewer.addEventListener('click',event=>{if(event.target===viewer)viewer.close()});
  13. </script></body></html>