# 项目核心记忆

## 时间轴和 WebSocket 数据流同步
- `isChange_flag_firstBackDate`：首次返回数据变化标志，用于协调时间轴选择和 WebSocket 数据更新的时序
  - 当用户拖动时间轴时重置为 false
  - WebSocket 返回新数据时设为 true
  - 只有为 true 时，selectedEndTime 变化才会触发子系统数据更新
  - 解决"先改时间还是先等数据"的竞态条件

## WebUI 嵌入 UE 的透明问题
- 页面透明：由于多个层级（body、#app、el-dialog、容器）的背景色设置不当导致 UE 场景透出
- Element Plus 的 el-container/el-header 等组件默认没有背景色
- 修复方案：统一设置各层级的背景色或背景图片，确保完全覆盖

## CSS 背景图常见错误
- `background-image` 只接受图片地址，不能包含 no-repeat、center 等参数
- 正确的做法是用 `background` 简写属性：`background: url(...) no-repeat center center / cover`
- 或者分开写：`background-image` + `background-repeat` + `background-position` + `background-size`

## 项目结构特点
- 使用 Vue 3 + Element Plus + Vite
- 时间轴组件在 footer2/timers.vue 和 footer/component/timers.vue
- WebSocket 用于实时数据获取和子系统状态更新
- 卫星数据可视化项目，包含多个图表组件（热力图、曲线图等）

## UE 与 WebUI 集成
- WebUI 嵌入 UE 需要处理渲染层级和光照影响
- 建议使用 Unlit 材质或调整材质 Emissive 值来避免 UE 灯光影响 WebUI 显示

## 主系统/子系统 间的时间轴状态同步
- `footerTimeHasedSelectTimeToSub`：标记主系统时间范围是否已同步给子系统
- `globalTimeLineEndSelectTime`：记录滑块当前指向的时间，用于分系统返回后恢复（**实时和历史模式都要更新**，`updateSelectedTimes` 已改为无条件更新，不再限制 `!is_flag_isRealtime`）
- `globalSubsystemSelectedStartTime`：记录用户选择的时间范围 `[startTime, endTime]`
- `globalLeftWssCONTINE`：WebSocket 数据对象，包含 `type`（`"history"` 或 `"realtime"`）。**注意**：`lefts/index.vue` 的 `onMounted` 会把 `wss`（WebSocket 实例）写入该字段，导致 `.type` 变成 `undefined`，**不可用于判断实时/历史模式**。
- `globalIsRealtimeBeforeSub`：**专门用于从子系统返回时恢复**，在 `timers.vue` 的 `onUnmounted` 时写入 `is_flag_isRealtime.value`，`onMounted` 恢复时读该字段判断。
- 从子系统返回时，根据 `footerTimeHasedSelectTimeToSub` 和 `globalIsRealtimeBeforeSub` 判断走历史模式恢复还是实时模式恢复
- **实时模式返回**：恢复 `is_flag_isRealtime = true`，重新开启实时 WS + 自动播放，不重置滑块
  - 实时模式不回显时间段：`dateRange=[]`、`pick_select_date=null`
  - **不走 `handleDateRangeChange`**，因为它会重置 `is_flag_isRealtime=false`、`isDateRangeNotEmpty=false`、`updateTimeline()` 重置滑块为0；之后设 `isDateRangeNotEmpty=true` 会触发 watch，`autoHandleDateRangeChange` 每秒把时间改成 `Date.now()-24h`，覆盖原来时间
  - 实时模式恢复只需：`is_flag_isRealtime=true` + `isChange_flag_select_date=true` + `updateTimeline()` + `wssOpenHandle({continue})`
  - **不设 `isDateRangeNotEmpty=true`**，避免 `autoHandleDateRangeChange` 把时间改成当前时间；实时模式的 WS 返回数据时会自动更新时间
- **历史模式返回**：恢复时间范围和滑块位置，发 `continue` WS 请求
  - 滑块恢复：`handleDateRangeChange(..., true)` 内部会把滑块重置为 0，需在 `nextTick` 后调用 `jumpToSpecificTime(new Date(tempDateTime.value))` 跳回保存的位置
  - `tempDateTime.value` 在 `onMounted` 开头赋值为 `globalTimeLineEndSelectTime`（每次 `updateSelectedTimes` 都会更新该 Store 字段）
  - `jumpToSpecificTime(time)` 通过 `((targetMs - start) / totalMs) * 100` 计算百分比，写入 `endHandlePercent.value`
  - ⚠️ **关键时序**：`wssOpenHandle` 末尾无条件调用 `startAutoPlay()`，因此必须把 `wssOpenHandle` 放在 `nextTick` **内部**（在 `jumpToSpecificTime` 之后），否则 `startAutoPlay` 会从 `endHandlePercent=0` 开始播放，自动走的滑块仍会从头重置
  - 正确顺序：`handleDateRangeChange` → `nextTick` → `jumpToSpecificTime` → `wssOpenHandle`（含 `startAutoPlay`）
  - ⚠️ **onMounted 末尾的 realTimeData() 陷阱**：`onUnmounted` 调用 `stopAutoPlay()` 导致 `isPlaying=false`，所以 onMounted 末尾的 `if (!isPlaying.value) { realTimeData() }` 在历史恢复分支执行后**还会继续执行**，生成新 taskCode、重开 WS，把恢复逻辑全部覆盖。修复：在 `setGlobalisFromTosub(false)` 之后，`realTimeData()` 之前，判断 `if (footerTimeHasedSelectTimeToSub) return;` 提前退出。
  - ⚠️ **实时模式 is_flag_isRealtime 被覆盖**：`handleDateRangeChange` 内部会把 `is_flag_isRealtime = false`，导致实时模式返回后变成历史模式。修复：在 `onUnmounted` 时把 `is_flag_isRealtime.value` 存入 `globalIsRealtimeBeforeSub`，`onMounted` 恢复时读 `wasRealtime = globalIsRealtimeBeforeSub === true`，调用 `handleDateRangeChange` 后如果 `wasRealtime=true` 则恢复 `is_flag_isRealtime.value = true`。
  - ⚠️ **isPlaying 没有恢复**：`handleDateRangeChange` 内部设置 `isPlaying=false`，但 `startAutoPlay` 里没有设置 `isPlaying=true`，导致 `moveEndHandle` 检查 `if (!isPlaying) return` 直接返回，滑块不动。修复：在 `nextTick` 里 `jumpToSpecificTime` 之后、`wssOpenHandle` 之前手动设置 `isPlaying.value = true`。