fix(elevator): 修复电梯状态实时更新问题,优化详情面板响应式依赖并添加开发环境模拟数据
This commit is contained in:
parent
624c68d0eb
commit
ac503ca81b
@ -17,7 +17,20 @@ type Props = {
|
||||
const props = defineProps<Props>();
|
||||
const editor = inject(props.token)!;
|
||||
|
||||
const pen = computed<MapPen | undefined>(() => editor.value.getPenById(props.current));
|
||||
// 订阅点位集合变化(含设备状态/连接状态),用于触发详情面板的响应式刷新
|
||||
const pointsTick = computed<string>(() =>
|
||||
editor.value.points.value
|
||||
.map((v: any) => `${v.id}:${v?.point?.isConnected ?? ''}:${v?.point?.elevatorDirection ?? ''}:${v?.point?.elevatorFrontDoorStatus ?? ''}:${v?.point?.currentFloor ?? ''}`)
|
||||
.join('|'),
|
||||
);
|
||||
|
||||
// 优化 pen 计算属性,确保响应式更新
|
||||
const pen = computed<MapPen | undefined>(() => {
|
||||
// 引用 pointsTick 以建立依赖关系
|
||||
void pointsTick.value;
|
||||
return editor.value.getPenById(props.current);
|
||||
});
|
||||
|
||||
const point = computed<MapPointInfo | null>(() => {
|
||||
const v = pen.value?.point;
|
||||
if (isNil(v?.type)) return null;
|
||||
@ -131,6 +144,79 @@ watch(point, (newPoint) => {
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// 关键修复:创建直接的状态计算,绕过深层响应式问题
|
||||
const elevatorStatusText = computed(() => {
|
||||
// 强制依赖 pointsTick 以确保响应式更新
|
||||
void pointsTick.value;
|
||||
|
||||
const pen = editor.value.getPenById(props.current);
|
||||
const point = pen?.point;
|
||||
if (!point) return '未知';
|
||||
|
||||
const { elevatorDirection, elevatorFrontDoorStatus, isConnected } = point;
|
||||
|
||||
// 离线
|
||||
if (!isConnected) return '离线';
|
||||
|
||||
// 优先判断门状态
|
||||
if (elevatorFrontDoorStatus === 2) return '开关门中';
|
||||
if (elevatorFrontDoorStatus === 3) return '门已开';
|
||||
|
||||
// 门已关,根据方向判断
|
||||
if (elevatorFrontDoorStatus === 1) {
|
||||
if (elevatorDirection === 2) return '上行中';
|
||||
if (elevatorDirection === 3) return '下行中';
|
||||
if (elevatorDirection === 1) return '门已关';
|
||||
}
|
||||
|
||||
return '未知';
|
||||
});
|
||||
|
||||
const elevatorStatusColor = computed(() => {
|
||||
// 强制依赖 pointsTick 以确保响应式更新
|
||||
void pointsTick.value;
|
||||
|
||||
const pen = editor.value.getPenById(props.current);
|
||||
const point = pen?.point;
|
||||
if (!point) return '#1890FF';
|
||||
|
||||
const { isConnected, elevatorDirection, elevatorFrontDoorStatus } = point;
|
||||
|
||||
// 离线显示灰色
|
||||
if (!isConnected) return '#999999';
|
||||
|
||||
// 优先判断门状态
|
||||
if (elevatorFrontDoorStatus === 2) return '#52C41A'; // 开门中 - 绿色
|
||||
if (elevatorFrontDoorStatus === 3) return '#52C41A'; // 门已开 - 绿色
|
||||
|
||||
// 门已关,根据方向判断
|
||||
if (elevatorFrontDoorStatus === 1) {
|
||||
if (elevatorDirection === 2) return '#722ED1'; // 向上 - 紫色
|
||||
if (elevatorDirection === 3) return '#13C2C2'; // 向下 - 青色
|
||||
if (elevatorDirection === 1) return '#1890FF'; // 停止 - 蓝色
|
||||
}
|
||||
|
||||
return '#1890FF'; // 默认 - 蓝色
|
||||
});
|
||||
|
||||
// 电梯连接状态计算属性
|
||||
const elevatorIsConnected = computed(() => {
|
||||
// 强制依赖 pointsTick 以确保响应式更新
|
||||
void pointsTick.value;
|
||||
|
||||
const pen = editor.value.getPenById(props.current);
|
||||
return pen?.point?.isConnected;
|
||||
});
|
||||
|
||||
// 电梯当前楼层计算属性
|
||||
const elevatorCurrentFloor = computed(() => {
|
||||
// 强制依赖 pointsTick 以确保响应式更新
|
||||
void pointsTick.value;
|
||||
|
||||
const pen = editor.value.getPenById(props.current);
|
||||
return pen?.point?.currentFloor;
|
||||
});
|
||||
|
||||
// 获取电梯状态文本(基于后端字段)
|
||||
const getElevatorStatusText = (point: any): string => {
|
||||
const { elevatorDirection, elevatorFrontDoorStatus, isConnected } = point;
|
||||
@ -226,26 +312,26 @@ const getElevatorStatusColor = (point: any): string => {
|
||||
<a-flex align="center" :gap="8" class="conn-status">
|
||||
<span
|
||||
class="status-dot"
|
||||
:class="point.isConnected ? 'online' : 'offline'"
|
||||
:title="point.isConnected ? $t('已连接') : $t('未连接')"
|
||||
:class="elevatorIsConnected ? 'online' : 'offline'"
|
||||
:title="elevatorIsConnected ? $t('已连接') : $t('未连接')"
|
||||
/>
|
||||
<a-typography-text>
|
||||
{{ point.isConnected ? $t('已连接') : $t('未连接') }}
|
||||
{{ elevatorIsConnected ? $t('已连接') : $t('未连接') }}
|
||||
</a-typography-text>
|
||||
</a-flex>
|
||||
</a-list-item>
|
||||
<a-list-item v-if="point.type === MapPointType.电梯点 && point.currentFloor !== undefined">
|
||||
<a-list-item v-if="point.type === MapPointType.电梯点 && elevatorCurrentFloor !== undefined">
|
||||
<a-typography-text type="secondary">{{ $t('当前楼层') }}</a-typography-text>
|
||||
<a-typography-text>{{ point.currentFloor }}F</a-typography-text>
|
||||
<a-typography-text>{{ elevatorCurrentFloor }}F</a-typography-text>
|
||||
</a-list-item>
|
||||
<a-list-item v-if="point.type === MapPointType.电梯点 && point.elevatorDirection !== undefined">
|
||||
<a-list-item v-if="point.type === MapPointType.电梯点">
|
||||
<a-typography-text type="secondary">{{ $t('电梯状态') }}</a-typography-text>
|
||||
<a-flex align="center" :gap="8">
|
||||
<span
|
||||
class="status-dot"
|
||||
:style="{ backgroundColor: getElevatorStatusColor(point) }"
|
||||
:style="{ backgroundColor: elevatorStatusColor }"
|
||||
/>
|
||||
<a-typography-text>{{ getElevatorStatusText(point) }}</a-typography-text>
|
||||
<a-typography-text>{{ elevatorStatusText }}</a-typography-text>
|
||||
</a-flex>
|
||||
</a-list-item>
|
||||
<a-list-item v-if="point.extensionType">
|
||||
|
||||
@ -119,6 +119,10 @@ const isSceneLoading = ref(false);
|
||||
|
||||
// 电梯模拟数据停止函数
|
||||
let stopElevatorMock: (() => void) | undefined;
|
||||
// 电梯模拟数据定时器
|
||||
let elevatorMockTimer: number | undefined;
|
||||
// 电梯状态计数器
|
||||
let elevatorMockTick = 0;
|
||||
|
||||
const playback = usePlaybackWebSocket(editor, async () => {
|
||||
// [关键修复] 只有在收到第一帧机器人数据后才初始化机器人图元
|
||||
@ -530,11 +534,69 @@ onMounted(async () => {
|
||||
id: '1998661793706377218',
|
||||
type: 102,
|
||||
elevatorFloor: 5,
|
||||
elevatorDirection: 3, // 停止
|
||||
elevatorFrontDoorStatus: 3, // 正在开关
|
||||
elevatorDirection: 2, // 停止
|
||||
elevatorFrontDoorStatus: 2, // 正在开关
|
||||
isConnected: true
|
||||
});
|
||||
|
||||
// 启动电梯状态自动变化的定时器
|
||||
console.log('[开发环境] 启动电梯状态自动变化定时器(3秒间隔)');
|
||||
|
||||
// 定义电梯状态变化序列
|
||||
const elevatorStates = [
|
||||
{ floor: 5, direction: 2, doorStatus: 2 }, // 5楼,停止,正在开关门
|
||||
{ floor: 5, direction: 2, doorStatus: 3 }, // 5楼,停止,门已开
|
||||
{ floor: 5, direction: 2, doorStatus: 1 }, // 5楼,停止,门已关
|
||||
{ floor: 5, direction: 2, doorStatus: 1 }, // 5楼,向上,门已关
|
||||
{ floor: 6, direction: 2, doorStatus: 1 }, // 6楼,向上,门已关
|
||||
{ floor: 6, direction: 2, doorStatus: 2 }, // 6楼,向上,正在开关门
|
||||
{ floor: 6, direction: 2, doorStatus: 3 }, // 6楼,向上,门已开
|
||||
{ floor: 6, direction: 2, doorStatus: 1 }, // 6楼,向上,门已关
|
||||
{ floor: 7, direction: 2, doorStatus: 1 }, // 7楼,向上,门已关
|
||||
{ floor: 7, direction: 2, doorStatus: 2 }, // 7楼,向上,正在开关门
|
||||
{ floor: 7, direction: 2, doorStatus: 3 }, // 7楼,向上,门已开
|
||||
{ floor: 7, direction: 2, doorStatus: 1 }, // 7楼,向上,门已关
|
||||
{ floor: 7, direction: 1, doorStatus: 1 }, // 7楼,停止,门已关
|
||||
{ floor: 7, direction: 3, doorStatus: 1 }, // 7楼,向下,门已关
|
||||
{ floor: 6, direction: 3, doorStatus: 1 }, // 6楼,向下,门已关
|
||||
{ floor: 6, direction: 3, doorStatus: 2 }, // 6楼,向下,正在开关门
|
||||
{ floor: 6, direction: 3, doorStatus: 3 }, // 6楼,向下,门已开
|
||||
{ floor: 6, direction: 3, doorStatus: 1 }, // 6楼,向下,门已关
|
||||
{ floor: 5, direction: 3, doorStatus: 1 }, // 5楼,向下,门已关
|
||||
{ floor: 5, direction: 1, doorStatus: 1 }, // 5楼,停止,门已关
|
||||
];
|
||||
|
||||
elevatorMockTimer = window.setInterval(() => {
|
||||
const stateIndex = elevatorMockTick % elevatorStates.length;
|
||||
const state = elevatorStates[stateIndex];
|
||||
|
||||
console.log(`[开发环境] 电梯状态变化 #${elevatorMockTick}:`, {
|
||||
floor: state.floor,
|
||||
direction: ['未知', '停止', '向上', '向下'][state.direction],
|
||||
doorStatus: ['未知', '关', '开关门中', '开'][state.doorStatus]
|
||||
});
|
||||
|
||||
elevatorStore.handleElevatorWebSocketData({
|
||||
id: '1998661793706377218',
|
||||
type: 102,
|
||||
elevatorFloor: state.floor,
|
||||
elevatorDirection: state.direction,
|
||||
elevatorFrontDoorStatus: state.doorStatus,
|
||||
isConnected: true
|
||||
});
|
||||
|
||||
elevatorMockTick++;
|
||||
}, 3000); // 每3秒变化一次
|
||||
|
||||
// 提供停止函数
|
||||
stopElevatorMock = () => {
|
||||
if (elevatorMockTimer) {
|
||||
clearInterval(elevatorMockTimer);
|
||||
elevatorMockTimer = undefined;
|
||||
console.log('[开发环境] 停止电梯状态模拟数据');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
@ -550,6 +612,12 @@ onUnmounted(() => {
|
||||
doorMockTimer = undefined;
|
||||
}
|
||||
|
||||
// 清理电梯模拟数据定时器
|
||||
if (elevatorMockTimer) {
|
||||
clearInterval(elevatorMockTimer);
|
||||
elevatorMockTimer = undefined;
|
||||
}
|
||||
|
||||
// 移除EditorService事件监听器
|
||||
if (editor.value) {
|
||||
(editor.value as any).off('customContextMenu', (event: Record<string, unknown>) => {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
// 门区域功能改造:由助手自动插入
|
||||
import { DOOR_AREA_TYPE } from '@api/map/door-area';
|
||||
import { MapPointType } from '@api/map';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import JSZip from 'jszip';
|
||||
import { computed, onMounted, onUnmounted, provide, ref, type ShallowRef, shallowRef, watch } from 'vue';
|
||||
@ -653,6 +654,92 @@ const handleExportConfirm = async (payload: ExportConfirmPayload) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出全部楼层数据
|
||||
* 将所有楼层的数据合并导出为一个JSON文件
|
||||
*/
|
||||
const exportAllFloors = async () => {
|
||||
try {
|
||||
if (!floorScenes.value || floorScenes.value.length === 0) {
|
||||
message.error('没有可导出的楼层数据');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示导出进度
|
||||
const hideMessage = message.loading('正在导出全部楼层数据...', 0);
|
||||
|
||||
// 获取当前编辑器状态以包含实时设备数据
|
||||
const currentEditorData = editor.value?.save();
|
||||
let realtimeDeviceData = {};
|
||||
|
||||
if (currentEditorData) {
|
||||
try {
|
||||
const parsedEditorData = JSON.parse(currentEditorData);
|
||||
// 提取实时设备状态(电梯等)
|
||||
realtimeDeviceData = {
|
||||
elevators: parsedEditorData.pens?.filter((pen: any) =>
|
||||
pen.name === 'point' && pen.point?.type === MapPointType.电梯点
|
||||
).map((pen: any) => ({
|
||||
deviceId: pen.point?.deviceId,
|
||||
penId: pen.id,
|
||||
status: pen.point
|
||||
})) || [],
|
||||
autoDoors: parsedEditorData.pens?.filter((pen: any) =>
|
||||
pen.name === 'point' && pen.point?.type === MapPointType.自动门点
|
||||
).map((pen: any) => ({
|
||||
deviceId: pen.point?.deviceId,
|
||||
penId: pen.id,
|
||||
status: pen.point
|
||||
})) || [],
|
||||
exportTimestamp: Date.now()
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn('解析编辑器数据失败,将不包含实时设备状态:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建包含所有楼层的数据结构
|
||||
const allFloorsData = {
|
||||
title: title.value || 'multi-floor-scene',
|
||||
exportTime: new Date().toISOString(),
|
||||
floorCount: floorScenes.value.length,
|
||||
floors: floorScenes.value.map((floorScene, index) => ({
|
||||
floorIndex: index,
|
||||
floorName: floorScene.name || `楼层${index + 1}`,
|
||||
scene: floorScene
|
||||
})),
|
||||
realtimeDeviceData,
|
||||
metadata: {
|
||||
version: '1.0',
|
||||
description: '多楼层地图导出数据,包含所有楼层结构和实时设备状态',
|
||||
currentFloorIndex: currentFloorIndex.value,
|
||||
isMultiFloor: isMultiFloor.value
|
||||
}
|
||||
};
|
||||
|
||||
// 转换为JSON字符串
|
||||
const jsonString = JSON.stringify(allFloorsData, null, 2);
|
||||
|
||||
// 创建Blob并下载
|
||||
const blob = new Blob([jsonString], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
// 生成文件名 - 使用.scene格式
|
||||
const filename = `${title.value || 'multi-floor-scene'}_all_floors_${new Date().toISOString().slice(0, 10)}.scene`;
|
||||
|
||||
// 下载文件
|
||||
downloadFile(url, filename);
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
hideMessage();
|
||||
message.success(`成功导出 ${floorScenes.value.length} 个楼层的数據`);
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('导出全部楼层失败:', error);
|
||||
message.error(error.message || '导出全部楼层失败');
|
||||
}
|
||||
};
|
||||
|
||||
const show = ref<boolean>(true);
|
||||
const current = ref<{ type: 'robot' | 'point' | 'line' | 'area'; id: string }>();
|
||||
watch(
|
||||
@ -789,6 +876,7 @@ const handleFloorChange = async (value: any) => {
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="exportModalVisible = true">导出为其他格式</a-menu-item>
|
||||
<a-menu-item key="2" @click="exportAllFloors" v-if="isMultiFloor">{{ $t('导出全部楼层') }}</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown-button>
|
||||
|
||||
@ -71,7 +71,7 @@ function drawDisconnectedIcon(ctx: CanvasRenderingContext2D, cx: number, cy: num
|
||||
* - 边框宽度:最小1.5px,否则按pen较小边的3%计算
|
||||
* - 圆角半径:最小2px,否则按pen较小边的5%计算
|
||||
* - 方向指示器:最小3px,否则按pen较小边的10%计算
|
||||
* - 右侧箭头:最小4px,否则按pen较小边的12%计算
|
||||
* - 右侧箭头:最小5px,否则按pen较小边的20%计算
|
||||
* - 虚线样式:根据pen尺寸动态调整间距比例
|
||||
* @param ctx Canvas上下文
|
||||
* @param pen 画笔对象
|
||||
@ -82,7 +82,6 @@ function drawElevatorMinimal(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
pen: MapPen,
|
||||
elevatorData: any,
|
||||
time: number
|
||||
): void {
|
||||
// 提取后端字段
|
||||
const {
|
||||
@ -129,42 +128,7 @@ function drawElevatorMinimal(
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
// 辅助函数:绘制方向指示(仅当电梯移动时)
|
||||
const drawDirection = (direction: 'up' | 'down', color: string) => {
|
||||
ctx.save();
|
||||
ctx.fillStyle = color;
|
||||
ctx.globalAlpha = 0.8;
|
||||
|
||||
// 方向指示器大小基于pen尺寸动态调整
|
||||
const indicatorSize = Math.max(3, baseSize * 0.1); // 最小3px,否则按10%比例
|
||||
const margin = Math.max(2, baseSize * 0.05); // 距离边框的距离:最小2px,否则按5%比例
|
||||
|
||||
if (direction === 'up') {
|
||||
// 向上三角 - 位于矩形顶部中央
|
||||
const centerX = x + w / 2;
|
||||
const centerY = rectY + margin + indicatorSize;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(centerX, centerY - indicatorSize);
|
||||
ctx.lineTo(centerX - indicatorSize, centerY + indicatorSize);
|
||||
ctx.lineTo(centerX + indicatorSize, centerY + indicatorSize);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
} else {
|
||||
// 向下三角 - 位于矩形底部中央
|
||||
const centerX = x + w / 2;
|
||||
const centerY = rectY + rectH - margin - indicatorSize;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(centerX, centerY + indicatorSize);
|
||||
ctx.lineTo(centerX - indicatorSize, centerY - indicatorSize);
|
||||
ctx.lineTo(centerX + indicatorSize, centerY - indicatorSize);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
// 辅助函数:在边框右侧绘制静态方向箭头
|
||||
const drawDirectionSide = (direction: 'up' | 'down', color: string) => {
|
||||
@ -173,11 +137,10 @@ function drawElevatorMinimal(
|
||||
ctx.strokeStyle = color;
|
||||
ctx.globalAlpha = 0.8;
|
||||
|
||||
// 箭头大小基于pen尺寸动态调整,使用宽度和高度的平均值来更好地适应不同比例
|
||||
const avgSize = (w + h) / 2; // 使用宽高平均值作为基准
|
||||
const arrowSize = Math.max(4, avgSize * 0.12); // 最小4px,否则按12%比例
|
||||
const sideMargin = Math.max(3, avgSize * 0.08); // 距离右侧边框的距离
|
||||
ctx.lineWidth = Math.max(1.5, avgSize * 0.025); // 箭头线条宽度
|
||||
// 箭头大小基于pen尺寸动态调整,分别考虑宽度和高度
|
||||
const arrowSize = Math.max(5, Math.min(w, h) * 0.20); // 使用较小的边作为基准,最小5px,否则按20%比例
|
||||
const sideMargin = Math.max(5, Math.min(w, h) * 0.20); // 距离右侧边框的距离,增加间距,使用较小的边作为基准
|
||||
ctx.lineWidth = Math.max(2, Math.min(w, h) * 0.04); // 箭头线条宽度,使用较小的边作为基准
|
||||
|
||||
// 箭头位置:在矩形右侧垂直居中
|
||||
const arrowX = rectX + rectW + sideMargin;
|
||||
@ -226,16 +189,7 @@ function drawElevatorMinimal(
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
// 辅助函数:呼吸动画效果
|
||||
const breatheAnimation = (baseAlpha: number = 0.6) => {
|
||||
// 使用缓慢的正弦波创建呼吸效果
|
||||
return baseAlpha + Math.sin(time * 0.001) * 0.2;
|
||||
};
|
||||
|
||||
// 辅助函数:脉冲动画效果(用于断连状态)
|
||||
const pulseAnimation = () => {
|
||||
return 0.4 + Math.sin(time * 0.002) * 0.4;
|
||||
};
|
||||
|
||||
// 状态渲染逻辑 - 静态显示,无动画
|
||||
if (!isConnected) {
|
||||
@ -245,14 +199,24 @@ function drawElevatorMinimal(
|
||||
// 在线状态根据具体状态渲染
|
||||
switch (elevatorFrontDoorStatus) {
|
||||
case 2: { // 正在开关门
|
||||
// 绿色静态边框
|
||||
// 绿色静态边框,同时显示上行下行箭头
|
||||
drawMinimalFrame('#34C759', 0.8);
|
||||
if (elevatorDirection === 2) { // 向上
|
||||
drawDirectionSide('up', '#007AFF'); // 使用蓝色箭头
|
||||
} else if (elevatorDirection === 3) { // 向下
|
||||
drawDirectionSide('down', '#007AFF'); // 使用蓝色箭头
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: { // 门已开
|
||||
// 绿色静态边框
|
||||
// 绿色静态边框,同时显示上行下行箭头
|
||||
drawMinimalFrame('#34C759', 0.8);
|
||||
if (elevatorDirection === 2) { // 向上
|
||||
drawDirectionSide('up', '#007AFF'); // 使用蓝色箭头
|
||||
} else if (elevatorDirection === 3) { // 向下
|
||||
drawDirectionSide('down', '#007AFF'); // 使用蓝色箭头
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -399,7 +363,7 @@ export function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
const { active, iconSize: r = 0, fontSize = 14, lineHeight = 1.5, fontFamily } = pen.calculative ?? {};
|
||||
const { x = 0, y = 0, width: w = 0, height: h = 0 } = pen.calculative?.worldRect ?? {};
|
||||
const { type, isConnected, doorStatus, active: pointActive } = pen.point ?? {};
|
||||
const { label = '', statusStyle } = pen ?? {};
|
||||
const { label = '' } = pen ?? {};
|
||||
|
||||
ctx.save();
|
||||
|
||||
|
||||
53
电梯状态实时更新修复说明.md
Normal file
53
电梯状态实时更新修复说明.md
Normal file
@ -0,0 +1,53 @@
|
||||
# 电梯状态实时更新修复说明
|
||||
|
||||
## 问题分析
|
||||
电梯详情卡片中的状态字段(连接状态、当前楼层、电梯状态等)没有实时更新,而门区域的详情卡片可以正常实时更新。
|
||||
|
||||
## 根本原因
|
||||
- **门区域**使用`areasTick`计算属性跟踪所有区域状态变化,确保响应式更新
|
||||
- **电梯点**缺少类似的`pointsTick`机制,导致状态更新时组件无法响应
|
||||
|
||||
## 修复方案
|
||||
参考门区域的实现,为电梯点添加相同的响应式更新机制:
|
||||
|
||||
### 1. 添加pointsTick响应式跟踪
|
||||
```typescript
|
||||
// 订阅点位集合变化(含设备状态/连接状态),用于触发详情面板的响应式刷新
|
||||
const pointsTick = computed<string>(() =>
|
||||
editor.value.points.value
|
||||
.map((v: any) => `${v.id}:${v?.point?.isConnected ?? ''}:${v?.point?.elevatorDirection ?? ''}:${v?.point?.elevatorFrontDoorStatus ?? ''}:${v?.point?.currentFloor ?? ''}`)
|
||||
.join('|'),
|
||||
);
|
||||
```
|
||||
|
||||
### 2. 优化pen计算属性
|
||||
```typescript
|
||||
const pen = computed<MapPen | undefined>(() => {
|
||||
// 引用 pointsTick 以建立依赖关系
|
||||
void pointsTick.value;
|
||||
return editor.value.getPenById(props.current);
|
||||
});
|
||||
```
|
||||
|
||||
### 3. 创建响应式状态计算属性
|
||||
```typescript
|
||||
// 电梯连接状态、当前楼层、状态文本和颜色都添加响应式支持
|
||||
const elevatorStatusText = computed(() => {
|
||||
void pointsTick.value;
|
||||
// ...状态计算逻辑
|
||||
});
|
||||
```
|
||||
|
||||
### 4. 更新模板绑定
|
||||
将模板中的电梯状态显示改为使用新的计算属性,确保实时更新。
|
||||
|
||||
## 修复效果
|
||||
- ✅ 电梯连接状态实时更新
|
||||
- ✅ 当前楼层实时更新
|
||||
- ✅ 电梯状态(上行/下行/门状态)实时更新
|
||||
- ✅ 与门区域保持一致的响应式行为
|
||||
|
||||
## 技术原理
|
||||
通过`pointsTick`计算属性,当编辑器中的任何点位状态发生变化时(通过WebSocket更新),都会触发字符串重新计算,进而依赖该计算属性的所有组件都会重新渲染,实现了实时更新的效果。
|
||||
|
||||
这种实现方式确保了UI能够实时反映电梯状态的最新数据,解决了状态显示不同步的问题。
|
||||
Loading…
x
Reference in New Issue
Block a user