# 平台 v3 重构 — 阶段 0~6 实际改动 > 范围:把 Simple3 WebTerminal 由"全功能浮动 Panel"模式收敛到"纯 3D Canvas",所有 UI 改由 Vue 平台前端 (`frontends/apps/simple-platform-vue`) 接管;同时补齐反射 / 编辑 / 监控 / 编排 4 大业务面板与撤销栈、AI 生图、报警 SSE 等关键能力。 > > 基线:上一份方案见 `ARCHITECTURE.md` v2/v3 章节;后续以本文件为准。 ## 编译验证 - `dotnet build Simple3/Simple3.csproj` —— 0 警告 0 错误 - `dotnet build MiGu.Server/MiGu.Server.csproj` —— 0 警告 0 错误 - `build-platform-frontend.bat` —— vite build 46.5s,无 vue-tsc / ts 错误,robocopy /MIR 已同步 MiGu.Server/wwwroot - 旧 `SimpleComposer` (WinForms) 在 v3 之外,不在本次构建目标里 - 旧 `HttpClientHelper` 提升 LangVersion=latest 修通 ## 阶段 0:后端 Simple3 平台化(已完成) ### 新增文件 | 文件 | 作用 | |------|------| | `Simple3/Web/MapEditApiController.cs` | 写操作 8 路 API:create/delete/batch/copy-to/pick/dashboard/upload/ai-generate;阶段 6 又补:**project/save**、**project/load**、**project/current**、**sample-sites-along-track** | | `Simple3/Web/ReflectionApiController.cs` | 只读 + execute API;listKinds / listObjects / methods-by-type / setField / getBundle / selection 等 | | `Simple3/Web/ReflectionApiDtos.cs` | 反射 API DTO + 统一信封 `ReflectionEnvelope` | | `Simple3/Web/AiCompletionService.cs` | OpenAI Function Calling 调用 + map-generate 工具桥(真实 LLM,不再桩) | | `Simple3/Web/AiConfigController.cs` | AI 服务配置(endpoint/apiKey/model/system prompt/temperature/maxTokens/timeout)读写 | | `Simple3/Web/AlarmStreamService.cs` | 1s 周期 diff `car.status.enums.AlarmInfo`(语义与 `E:\Work\FRLD\StandardScene\Forklift.cs` 的 `Commons.GetCarStatus(this, "AlarmInfo")` 等价),变化时通过 SSE `alarm` 事件推送 raise/update/clear | | `Simple3/Web/ProjectionStreamModule.cs` | 平台 SSE 总线,对外广播 `object-created` / `object-deleted` / `object-patched` / `object-batch-changed` / `alarm` / `pick-result` / `project-saved` / `project-loaded` | | `Simple3/Web/PluginAssemblyScan.cs` | 反射扫描 plugins/*.dll,把 `MethodMember` 标注的方法 + Car/Mission/CarProgram/Site/Track 子类型暴露给反射 API | ### 修改文件 | 文件 | 关键改动 | |------|----------| | `Simple3/Startup.cs` | EntryPoint 第一步 `EnsureImguiIniFromEmbedded()`;启动 AlarmStreamService、ProjectionStreamModule、AiConfig 加载 | | `Simple3/UI/SimpleUI.cs` | `CreateRemotePanelSetForTerminal` 检测 CanvasOnly 旗标:不创建 Workbench / Toolbar / BottomBar / CAD / Welcome;保留 SelectionAction、SceneInput、SceneDrawable、2DMapView | | `Simple3/Workspace/WorkspaceWebUiContext.cs` | 增加 `IsCanvasOnly(terminal)` 旗标 + `/declareCanvasOnly` 注册器(与 `/declareEmbedUi` 互斥) | | `Simple3/Web/ProjectionWebHost.cs` | `/declareCanvasOnly` 路由注册 | | `Simple3/Web/ProjectionWebApiController.cs` | 现有 projection API 上挂 `selection-detail` SSE hook | | `Simple3/UI/ComposerDockPanel.cs` | CanvasOnly 时 PrepareWelcomePanel 返回空 | | `Simple3/Props/UIHelpers.cs` | 增加 `UI_Image` / `UI_Text` / `UI_Model` PropType。`UI_Model` 用于 CycleGUI 装饰物挂模型(obj/glb/gltf/cyclemodel) | ### 关键 API 路径 ``` POST /projection/map-edit/objects/{kind} # site|track|bezier|arc|nurbs|image|text|model DELETE /projection/map-edit/objects/{kind}/{id} POST /projection/map-edit/objects/batch # create/delete/patch 混合 POST /projection/map-edit/objects/{kind}/{id}/fields/copy-to POST /projection/map-edit/pick # 拾取会话 GET /projection/map-edit/dashboard/summary # KPI 聚合 POST /projection/map-edit/assets/upload # 图片/模型 base64 POST /projection/map-edit/ai/map-generate # 真实 LLM 生图 # 阶段 6 新增(本会话) POST /projection/map-edit/project/save # body: { path?: string } POST /projection/map-edit/project/load # body: { path: string } GET /projection/map-edit/project/current POST /projection/map-edit/objects/track/{id}/sample-sites # body: { spacingMm, includeEndpoints?, layer? } GET /projection/ai-config # 读 AI 服务配置 POST /projection/ai-config # 写 AI 服务配置 GET /projection/reflection/kinds | objects/{kind} | methods/{kind}/{id} | ... SSE /projection/stream # 所有 SSE 事件 ``` ## 阶段 1:MapEditorView 四栏壳(已完成) `frontends/apps/simple-platform-vue/src/views/admin/MapEditorView.vue` 完整重写为: ``` ┌─────────────────────────────────────────────────────┐ │ EditTopBar:项目 / 文件 / 编辑 / 视图 / 图层 / 对齐吸附 │ │ + 撤销/重做 + AI 生图 + 保存 │ ├──────┬─────────────────────────┬───────────────────┤ │ Edit │ Workspace3D │ EditPropertyPanel │ │ Tool │ (canvas-only iframe) │ 对象/类型默认/图层 │ │ Rail │ │ │ ├──────┴─────────────────────────┴───────────────────┤ │ EditStatusBar:选中 / 鼠标坐标 / 工具 / 吸附 / undo │ └─────────────────────────────────────────────────────┘ ``` ### 新增组件 - `components/map-editor/EditTopBar.vue` —— 6 个 dropdown 菜单(项目/文件/编辑/视图/图层/对齐吸附)+ 撤销重做按钮 + AI 生图入口 + 保存 - `components/map-editor/EditToolRail.vue` —— 6 组 CAD 工具按钮 + 吸附组(网格/对象/端点/中点 + 网格步长) - `components/map-editor/EditPropertyPanel.vue` —— 3 Tab(对象 / 类型默认 / 图层),自动反射出 `getFields`,每行可改可保存;多选时统一字段写入;不一致字段显示「-」;执行 reflectionApi.execute 调任何 MethodMember - `components/map-editor/EditStatusBar.vue` —— 状态栏 - `components/map-editor/AiGenerateDialog.vue` —— AI 生图对话框 ### 反射 API 接入 - `api/mapEdit.ts` —— MapEditApiController 全 8+4 路 API 胶水(本会话补的 4 个:projectSave / projectLoad / projectCurrent / sampleSitesAlongTrack) - `api/reflection.ts` —— ReflectionApiController 全套胶水(含 MOCK 离线 fallback) ## 阶段 2:对齐 / 批量生成 / 复制字段 / 类型默认 / 图层(已完成) - `composables/useAlignment.ts` —— 11 种对齐模式:left/right/top/bottom/centerH/centerV/center/distributeH/distributeV/toFirst/toLast - `composables/useBatchGenerate.ts` —— 横向/纵向/矩阵/环形 4 种批量生成器,输出 BatchOp[] - `composables/useClipboard.ts` —— 对象/字段剪贴板 - `transform.copyFields` 工具:MapEditorView `runCopyFields` 读取多选(第 1 个为源,其余目标),prompt 字段名列表,调 `mapEditApi.copyFieldsTo`;撤销时用前置 patch 回滚 - 类型默认 Tab:站点/路径默认 layer/前缀/颜色/线宽,存 localStorage `mapEditor.defaults` - 图层 Tab:图层名列表 + 可见 / 可选 / 颜色,本地状态 ## 阶段 3:AI 生图 + 状态栏 + 项目保存/加载(已完成) - AiGenerateDialog 通过 `mapEditApi.aiMapGenerate` 真实调用 LLM,后端走 `AiCompletionService.GenerateMapAsync`:用 OpenAI Function Calling 让 LLM 直接调 create_site / create_track / batch_create 等"工具",落地到 SimpleLib - 入口:EditTopBar 「AI 生图」按钮 → 弹对话框,参数:prompt / mode (sites/tracks/sites+tracks/full) / bounds / referenceImageBase64 / layer / constraints - 项目保存/加载:EditTopBar「保存」/「另存为」/「加载项目」→ prompt 输入路径 → 调 `mapEditApi.projectSave/Load` → 服务端走 `SimpleProject.ToFile / FromFile`;最近路径缓存到 localStorage `mapEditor.lastProjectPath` ## 阶段 4:MapMonitor 浮动报警卡 + KPI + 实时列表(已完成) `frontends/apps/simple-platform-vue/src/views/admin/MapMonitorView.vue` 改动: - 删除原 `AlarmTicker.vue` 底部滚动条 - 新增 `components/map-monitor/FloatingAlarmStack.vue` + `FloatingAlarmCard.vue`,可拖动 / 可吸边 / 可堆叠 - 报警来源走 `useMapEditStream.onAlarm`(订阅 SSE 的 `alarm` 事件,由 AlarmStreamService 1s diff 推送) - KPI 3 卡:在线车辆 / 运行中任务 / 排队任务 - 实时列表:左侧 Workspace3D + 右侧 VehicleMonitorPanel + SelectionDetailPanel - 「定位」按钮通过 `reflectionApi.setSelection('car', id)` 让 Simple3 3D 高亮该车(视觉上等效 flyTo) ## 阶段 5:OrchestrationView 设计与编排(已完成) `views/admin/OrchestrationView.vue` 5-Tab 壳: | Tab | 来源 | |-----|------| | 车辆 | 复用现有 `CarPanelView` | | 进程 | 复用现有 `MissionEditorView` | | 脚本 | `components/orchestration/ReflectionKindTable.vue`(kind=script):动态列表 + 反射 method 执行 | | 任务流 | ReflectionKindTable(kind=mission, extraFilter='taskflow')| | 触发器 | ReflectionKindTable(kind=mission, extraFilter='trigger')| Tab 状态缓存到 sessionStorage。 ## 阶段 6:联调 / 撤销栈做实 / 文档(已完成) ### 撤销栈做实 `composables/useHistory.ts` 提供 `run/undo/redo/composite`,每个命令 `apply/revert` 双向。本次把 MapEditorView 里所有占位 revert 改成真实回滚: | 命令 | 原 revert | 现 revert | |------|-----------|-----------| | `runCreateSite` | delete 创建的 id | (未变)| | `handleTrackPick` 创建路径 | delete 创建的 id | (未变)| | `deleteSelection` | ⚠️ 仅本地撤销 | **getBundle 抓快照 → batch.create 重建(id 重排)** | | `onSetField` 字段修改 | ⚠️ 不回滚 | **getBundle 抓原值 → setField 回去** | | `applyAlignment` | (已用 patch 反向恢复) | (未变)| | `applyBatchAsCommand` | (已用 delete 反向移除) | (未变)| | `runAlongPathSampling`(新增)| —— | delete 所有采样产生的 site id | | `runCopyFields`(新增)| —— | patch 反向把 targets 字段值恢复 | ### 沿路径采样 - 后端:`POST /projection/map-edit/objects/track/{id}/sample-sites { spacingMm, includeEndpoints?, layer? }` —— 当前在 siteA → siteB 之间欧式直线均分(贝塞尔/弧的真实参数化采样留待后续 Track 子类 PR) - 前端:选中 1+ 条 track,工具栏「沿路径」→ prompt 间距和端点开关 → 调后端,撤销可回退 ### 编译相关清理 - `Simple3/Recording/`(之前的录制/回放实验目录)误用 `Simple3.Rendering` namespace、`Car` 找不到、`Start(string, out)` 默认参数顺序等 3 个错误 → 修 - `Simple3/Props/UISite.cs` 在 `Simple3.Props` namespace 下 `Workspace.AddProp` 被解析成 `Simple3.Workspace.AddProp` → 改为全限定 `CycleGUI.Workspace.AddProp` - `Simple3/Workspace/WorkspaceBottomBar.cs` 缺 `using Simple3.Workspace;` → 补 - `HttpClientHelper.csproj` LangVersion 7.3 → latest ## 已知 TODO(不阻塞 v3 上线) 1. 贝塞尔/弧/Nurbs 路径的真实参数化采样(当前直线均分) 2. 撤销栈跨 Tab / 跨 session 共享(当前仅 MapEditor 视图局部) 3. Workspace3D 真正的 postMessage 'workspace.flyTo' 双向桥(当前用 setSelection 代替) 4. 复制字段 UI 支持类型化字段挑选器(当前 prompt 输入字段名) 5. AI 生图 OAuth/键持久化加密(当前明文存 config) 6. Recording 录制/回放模块未完工(PlaybackController 已修通编译,UI 已挂底栏,但实际录制/回放业务逻辑需要后续单独 PR 完成) ## 启动方式 ``` # 1. 后端 build(Simple3 + MiGu.Server) dotnet build Simple3\Simple3.csproj dotnet build MiGu.Server\MiGu.Server.csproj # 2. 前端打包并同步到 wwwroot .\build-platform-frontend.bat # 3. 启动平台 + Simple3 dotnet run --project MiGu.Server # 监听 0.0.0.0:8080 # Simple3 端口 8222/8223 由 Simple3 自己启动 # 4. 访问 http://127.0.0.1:8080/admin/map-editor # 地图编辑(v3 四栏壳) http://127.0.0.1:8080/admin/map-monitor # 地图监控(驾驶舱 + 浮动报警) http://127.0.0.1:8080/admin/orchestration # 设计与编排(5 Tab) ```