Compare commits
2 Commits
7e4444c460
...
5f90174c57
Author | SHA1 | Date | |
---|---|---|---|
5f90174c57 | |||
7f761ceaad |
98
docs/库位功能逻辑分析.md
Normal file
98
docs/库位功能逻辑分析.md
Normal file
@ -0,0 +1,98 @@
|
||||
# 库位功能逻辑分析
|
||||
|
||||
## 1. 概述
|
||||
|
||||
本文档旨在详细解析项目中“库位”(Storage Location)功能的实现逻辑。该功能的核心目标是实时监控与地图中“动作点”关联的库位状态,并将这些状态直观地反馈在前端界面上,包括更新画布上点的边框颜色和在详情卡片中展示详细的库位信息。
|
||||
|
||||
整个功能逻辑主要由 **`StorageLocationService`** 服务和 **`PointDetailCard`** UI组件协作完成,数据通过 WebSocket 从后端实时推送。
|
||||
|
||||
## 2. 核心服务: `StorageLocationService`
|
||||
|
||||
`StorageLocationService` 是库位管理的中枢,封装了所有核心业务逻辑。它在 `movement-supervision.vue` 页面中被实例化和管理。
|
||||
|
||||
**文件路径:** `src/services/storage-location.service.ts`
|
||||
|
||||
### 主要职责
|
||||
|
||||
1. **WebSocket 通信**:
|
||||
|
||||
- 通过调用 `@api/scene` 中的 `monitorStorageLocationById` 方法,建立一个 WebSocket 连接来接收实时的库位状态更新。
|
||||
- 连接建立后,会主动向后端请求一次全量的库位状态数据。
|
||||
|
||||
2. **数据处理与状态管理**:
|
||||
|
||||
- 维护一个核心数据结构 `storageLocations: Ref<Map<string, StorageLocationInfo[]>>`。这是一个响应式的 Map,其中:
|
||||
- `key`: 画布中“动作点”的 ID (`pointId`)。
|
||||
- `value`: 一个数组,包含所有绑定到该动作点的库位信息 (`StorageLocationInfo[]`)。
|
||||
- 通过 `handleStorageLocationUpdate` 方法处理来自 WebSocket 的两种消息:
|
||||
- `storage_location_update`: 用于全量更新所有库位信息。
|
||||
- `storage_location_status_change`: 用于更新单个库位的状态,实现增量更新。
|
||||
|
||||
3. **与编辑器 (`EditorService`) 的交互**:
|
||||
- **ID 映射**: 通过 `buildStationToPointIdMap` 方法,将后端数据中的 `station_name` (如 "AP9") 与画布中“动作点”的唯一 `id` (如 "3351") 进行映射。这是连接后端逻辑与前端视觉呈现的关键桥梁。
|
||||
- **视觉状态更新**: 实现 `updatePointBorderColor` 方法,根据一个点所关联的所有库位的占用状态 (`is_occupied`),动态更新该点在画布上的边框颜色:
|
||||
- **全部占用**: 红色 (`#ff4d4f`)
|
||||
- **部分或全部未占用**: 绿色 (`#52c41a`)
|
||||
|
||||
### 对外接口
|
||||
|
||||
- `getLocationsByPointId(pointId: string)`: 向外部组件提供一个接口,用于根据“动作点”的 ID 获取其关联的所有库位的详细信息数组。
|
||||
|
||||
## 3. UI 展示: `PointDetailCard`
|
||||
|
||||
当用户在 `movement-supervision.vue` 页面中选中一个“动作点”时,`PointDetailCard` 组件会负责展示该点的详细信息,其中就包括了库位的实时状态。
|
||||
|
||||
**文件路径:** `src/components/card/point-detail-card.vue`
|
||||
|
||||
### 主要职责
|
||||
|
||||
1. **接收数据**:
|
||||
|
||||
- 通过 `props` 从父组件接收 `storageLocations` 数组。该数组的数据源是 `StorageLocationService.getLocationsByPointId()` 方法的返回值。
|
||||
|
||||
2. **渲染库位状态**:
|
||||
- 遍历 `storageLocations` 数组,为每一个库位渲染一个信息块。
|
||||
- 使用 `getStorageStatusTag` 方法,根据库位的多个布尔状态 (`is_occupied`, `is_locked`, `is_disabled`, `is_empty_tray`),生成对应文本(如“已占用”、“未锁定”)和颜色样式的标签,为用户提供清晰、直观的状态反馈。
|
||||
|
||||
## 4. 数据流转图
|
||||
|
||||
下面的流程图清晰地展示了从后端数据推送到前端UI渲染的完整过程。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Backend as Backend (WebSocket)
|
||||
participant StorageLocationService as StorageLocationService
|
||||
participant MovementSupervision as movement-supervision.vue
|
||||
participant PointDetailCard as PointDetailCard
|
||||
participant EditorService as EditorService (Canvas)
|
||||
|
||||
MovementSupervision->>StorageLocationService: new StorageLocationService(editor, sceneId)
|
||||
MovementSupervision->>StorageLocationService: startMonitoring()
|
||||
StorageLocationService->>Backend: 建立 WebSocket 连接
|
||||
Backend-->>StorageLocationService: 推送库位状态消息 (JSON)
|
||||
|
||||
StorageLocationService->>StorageLocationService: handleStorageLocationUpdate(message)
|
||||
StorageLocationService->>EditorService: buildStationToPointIdMap() <br> (获取点位ID与站点名称映射)
|
||||
StorageLocationService->>StorageLocationService: 更新内部 storageLocations Map
|
||||
StorageLocationService->>EditorService: updatePointBorderColor(pointId, color) <br> (更新画布点的边框颜色)
|
||||
|
||||
Note right of MovementSupervision: 用户点击一个动作点
|
||||
MovementSupervision->>StorageLocationService: getLocationsByPointId(current.id)
|
||||
StorageLocationService-->>MovementSupervision: 返回库位信息数组
|
||||
MovementSupervision->>PointDetailCard: :storage-locations="locations"
|
||||
|
||||
PointDetailCard->>PointDetailCard: 渲染库位名称和状态标签
|
||||
```
|
||||
|
||||
## 5. 关联文件清单
|
||||
|
||||
- **`src/pages/movement-supervision.vue`**:
|
||||
- **角色**: 核心页面。负责实例化和管理 `StorageLocationService` 的生命周期,并将获取到的库位数据传递给 `PointDetailCard`。
|
||||
- **`src/services/storage-location.service.ts`**:
|
||||
- **角色**: 核心服务。处理所有与库位相关的业务逻辑,包括数据获取、状态管理和与画布的交互。
|
||||
- **`src/components/card/point-detail-card.vue`**:
|
||||
- **角色**: UI组件。负责展示单个动作点所绑定的库位的详细状态信息。
|
||||
- **`src/apis/scene/api.ts`**:
|
||||
- **角色**: API定义。包含 `monitorStorageLocationById` 方法,用于发起 WebSocket 连接请求。
|
||||
- **`src/core/editor.service.ts`**:
|
||||
- **角色**: 编辑器服务。`StorageLocationService` 依赖此服务来获取画布中的点位信息并更新其视觉样式。
|
@ -29,6 +29,7 @@ export interface MapPointInfo {
|
||||
isBlock?: boolean; // 是否禁行
|
||||
isForbidAvoid?: boolean; // 是否禁止避让
|
||||
associatedStorageLocations?: string[]; // 库位名称
|
||||
deviceId?: string; // 设备ID
|
||||
}
|
||||
//#endregion
|
||||
|
||||
|
@ -44,6 +44,7 @@ export interface StandardScenePoint {
|
||||
associatedStorageLocations?: string[]; // 库位名称
|
||||
config?: object; // 其它属性配置(可按需增加)
|
||||
properties?: unknown; // 附加数据(前端不做任何处理)
|
||||
deviceId?: string; // 设备ID
|
||||
}
|
||||
export interface StandardSceneRoute {
|
||||
id: string;
|
||||
|
@ -109,6 +109,10 @@ const getStorageStatusTag = (location: StorageLocationInfo) => {
|
||||
<a-typography-text type="secondary">{{ $t('站点坐标') }}</a-typography-text>
|
||||
<a-typography-text>({{ rect.x?.toFixed() }},{{ rect.y?.toFixed() }})</a-typography-text>
|
||||
</a-list-item>
|
||||
<a-list-item v-if="point.type === MapPointType.自动门点 && point.deviceId">
|
||||
<a-typography-text type="secondary">{{ $t('设备ID') }}</a-typography-text>
|
||||
<a-typography-text>{{ point.deviceId }}</a-typography-text>
|
||||
</a-list-item>
|
||||
<a-list-item v-if="point.extensionType">
|
||||
<a-typography-text type="secondary">{{ $t('扩展类型') }}</a-typography-text>
|
||||
<a-typography-text>{{ $t(MapPointType[point.extensionType]) }}</a-typography-text>
|
||||
|
@ -109,6 +109,19 @@ function onChangeLocation(i: number, v: string) {
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row v-if="point.type === MapPointType.自动门点" :gutter="[8, 8]">
|
||||
<a-col :span="24">
|
||||
<a-typography-text>{{ $t('设备ID') }}:</a-typography-text>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-input
|
||||
:placeholder="$t('请输入设备ID')"
|
||||
:value="point.deviceId"
|
||||
@change="editor.updatePen(id, { point: { ...point, deviceId: ($event.target as any).value } }, false)"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="[8, 8]">
|
||||
<a-col :span="24">
|
||||
<a-typography-text>{{ $t('描述') }}:</a-typography-text>
|
||||
|
@ -110,10 +110,10 @@ export class EditorService extends Meta2d {
|
||||
if (!points?.length) return;
|
||||
await Promise.all(
|
||||
points.map(async (v) => {
|
||||
const { id, name, desc, x, y, type, extensionType, robots, actions, properties } = v;
|
||||
const { id, name, desc, x, y, type, extensionType, robots, actions, properties, deviceId } = v;
|
||||
await this.addPoint({ x, y }, type, id);
|
||||
this.setValue(
|
||||
{ id, label: name, desc, properties, point: { type, extensionType, robots, actions } },
|
||||
{ id, label: name, desc, properties, point: { type, extensionType, robots, actions, deviceId } },
|
||||
{ render: false, history: false, doEvent: false },
|
||||
);
|
||||
}),
|
||||
@ -199,7 +199,7 @@ export class EditorService extends Meta2d {
|
||||
#mapScenePoint(pen?: MapPen): StandardScenePoint | null {
|
||||
if (!pen?.id || isEmpty(pen?.point)) return null;
|
||||
const { id, label, desc, properties } = pen;
|
||||
const { type, extensionType, robots, actions, associatedStorageLocations } = pen.point;
|
||||
const { type, extensionType, robots, actions, associatedStorageLocations, deviceId } = pen.point;
|
||||
const { x = 0, y = 0 } = this.getPointRect(pen) ?? {};
|
||||
|
||||
// 进行坐标转换:左上角原点 -> 中心点原点,同时应用ratio缩放
|
||||
@ -225,6 +225,9 @@ export class EditorService extends Meta2d {
|
||||
if (MapPointType.动作点 === type) {
|
||||
point.associatedStorageLocations = associatedStorageLocations;
|
||||
}
|
||||
if (MapPointType.自动门点 === type) {
|
||||
point.deviceId = deviceId;
|
||||
}
|
||||
return point;
|
||||
}
|
||||
#mapSceneRoute(pen?: MapPen): StandardSceneRoute | null {
|
||||
|
Loading…
x
Reference in New Issue
Block a user