refactor(elevator): 重构电梯状态系统以适配后端字段,移除旧枚举并更新UI显示逻辑与动画效果

This commit is contained in:
xudan 2025-12-11 11:07:01 +08:00
parent 434d4b72c4
commit 5f7a9b6dce
6 changed files with 482 additions and 348 deletions

View File

@ -36,9 +36,11 @@ export interface MapPointInfo {
isConnected?: boolean; // 连接状态自动门点、电梯点使用true=已连接false=未连接)
active?: boolean; // 是否激活状态,用于控制光圈显示
// 电梯点专属属性
elevatorStatus?: number; // 电梯状态仅电梯点使用0=静止1=开门中2=关门中3=上行4=下行)
currentFloor?: number; // 当前楼层(仅电梯点使用)
// 电梯点专属属性(后端字段)
elevatorDirection?: number; // 电梯方向0-未知1-停止2-向上3-向下)
elevatorFrontDoorStatus?: number; // 电梯前门状态0-未知1-关2-正在开关3-开)
elevatorFloor?: number; // 电梯楼层0-未知)
currentFloor?: number; // 当前楼层(兼容旧字段)
lastUpdate?: number; // 最后更新时间戳
}
//#endregion

View File

@ -7,7 +7,7 @@ import { isNil } from 'lodash-es';
import { computed, inject, type InjectionKey, ref, type ShallowRef, watch } from 'vue';
import { type DeviceMappingDTO,queryDeviceMappingByType } from '../../apis/device/api';
import { ElevatorStatus,useElevatorStore } from '../../stores/elevator.store';
import { useElevatorStore } from '../../stores/elevator.store';
type Props = {
token: InjectionKey<ShallowRef<EditorService>>;
@ -131,26 +131,46 @@ watch(point, (newPoint) => {
}
}, { immediate: true });
//
const getElevatorStatusText = (status: ElevatorStatus): string => {
const statusMap = {
[ElevatorStatus.IDLE]: '静止',
[ElevatorStatus.OPENING]: '开门中',
[ElevatorStatus.CLOSING]: '关门中',
[ElevatorStatus.MOVING_UP]: '上行中',
[ElevatorStatus.MOVING_DOWN]: '下行中',
[ElevatorStatus.DOOR_OPEN]: '门已开',
[ElevatorStatus.DOOR_CLOSED]: '门已关',
[ElevatorStatus.FAULT]: '故障',
[ElevatorStatus.OFFLINE]: '离线',
};
return statusMap[status] || '未知';
//
const getElevatorStatusText = (point: any): string => {
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 getElevatorStatusColor = (status: ElevatorStatus, isConnected: boolean): string => {
const display = elevatorStore.getElevatorDisplay(status, isConnected);
return display.color;
//
const getElevatorStatusColor = (point: any): string => {
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'; // -
};
</script>
@ -218,14 +238,14 @@ const getElevatorStatusColor = (status: ElevatorStatus, isConnected: boolean): s
<a-typography-text type="secondary">{{ $t('当前楼层') }}</a-typography-text>
<a-typography-text>{{ point.currentFloor }}F</a-typography-text>
</a-list-item>
<a-list-item v-if="point.type === MapPointType.电梯点 && point.elevatorStatus !== undefined">
<a-list-item v-if="point.type === MapPointType.电梯点 && point.elevatorDirection !== undefined">
<a-typography-text type="secondary">{{ $t('电梯状态') }}</a-typography-text>
<a-flex align="center" :gap="8">
<span
class="status-dot"
:style="{ backgroundColor: getElevatorStatusColor(point.elevatorStatus, point.isConnected) }"
:style="{ backgroundColor: getElevatorStatusColor(point) }"
/>
<a-typography-text>{{ getElevatorStatusText(point.elevatorStatus) }}</a-typography-text>
<a-typography-text>{{ getElevatorStatusText(point) }}</a-typography-text>
</a-flex>
</a-list-item>
<a-list-item v-if="point.extensionType">

View File

@ -27,8 +27,7 @@ import { EditorService } from '../services/editor.service';
import { StorageLocationService } from '../services/storage-location.service';
import { useViewState } from '../services/useViewState';
import { editorStore } from '../stores/editor.store';
import { ElevatorStatus,useElevatorStore } from '../stores/elevator.store';
import { createElevatorMockData } from '../utils/elevator-mock';
import { useElevatorStore } from '../stores/elevator.store';
const EDITOR_KEY = Symbol('editor-key');
@ -523,15 +522,16 @@ onMounted(async () => {
//
if (import.meta.env.DEV) {
console.log('[开发环境] 启动电梯状态模拟数据');
stopElevatorMock = createElevatorMockData();
//
console.log('[开发环境] 推送指定电梯数据: 1998661793706377218');
// 使
elevatorStore.handleElevatorWebSocketData({
id: '1998661793706377218',
type: 102,
status: ElevatorStatus.OPENING,
floor: 5,
elevatorFloor: 5,
elevatorDirection: 1, //
elevatorFrontDoorStatus: 2, //
isConnected: true
});

View File

@ -6,6 +6,7 @@ import { get } from 'lodash-es';
import doorClosedUrl from '../../assets/icons/png/doorClosed.png';
import doorOpenUrl from '../../assets/icons/png/doorOpening.png';
// 直接使用后端字段,不需要导入枚举
import colorConfig from '../color/color-config.service';
const __doorImgOpen = new Image();
__doorImgOpen.src = (doorOpenUrl as unknown as string) || '';
@ -52,6 +53,278 @@ function drawDisconnectedIcon(ctx: CanvasRenderingContext2D, cx: number, cy: num
ctx.restore();
}
/**
* -
* @param ctx Canvas上下文
* @param pen
* @param elevatorData
* @param time
*/
function drawElevatorAnimation(
ctx: CanvasRenderingContext2D,
pen: MapPen,
elevatorData: any,
time: number
): void {
// 提取后端字段
const {
elevatorDirection,
elevatorFrontDoorStatus,
isConnected,
} = elevatorData;
const { x = 0, y = 0, width: w = 0, height: h = 0 } = pen.calculative?.worldRect ?? {};
// 为48*60图片设计矩形动画框架
// 外边框距离图片边缘6像素确保不覆盖图片
const framePadding = 6;
const frameWidth = 3; // 边框宽度
const cornerLength = 12; // 角装饰长度
const cornerWidth = 4; // 角装饰宽度
// 动画框架的四个角坐标
const frameCorners = {
topLeft: { x: x - framePadding, y: y - framePadding },
topRight: { x: x + w + framePadding, y: y - framePadding },
bottomLeft: { x: x - framePadding, y: y + h + framePadding },
bottomRight: { x: x + w + framePadding, y: y + h + framePadding }
};
// 检查是否启用LOD
const lodEnabled = (pen.calculative as any)?.lodEnabled || false;
// 辅助函数:绘制矩形框架的角装饰
const drawFrameCorners = (color: string, alpha: number, animated: boolean = true) => {
ctx.save();
ctx.strokeStyle = color;
ctx.fillStyle = color;
ctx.lineWidth = cornerWidth;
ctx.lineCap = 'square';
ctx.globalAlpha = alpha;
// 动画效果:角装饰闪烁
const animPhase = animated ? Math.sin(time * 0.002) * 0.5 + 0.5 : 1;
// 左上角
ctx.globalAlpha = alpha * animPhase;
ctx.beginPath();
ctx.moveTo(frameCorners.topLeft.x, frameCorners.topLeft.y + cornerLength);
ctx.lineTo(frameCorners.topLeft.x, frameCorners.topLeft.y);
ctx.lineTo(frameCorners.topLeft.x + cornerLength, frameCorners.topLeft.y);
ctx.stroke();
// 右上角
ctx.globalAlpha = alpha * (animPhase + 0.25) % 1;
ctx.beginPath();
ctx.moveTo(frameCorners.topRight.x - cornerLength, frameCorners.topRight.y);
ctx.lineTo(frameCorners.topRight.x, frameCorners.topRight.y);
ctx.lineTo(frameCorners.topRight.x, frameCorners.topRight.y + cornerLength);
ctx.stroke();
// 右下角
ctx.globalAlpha = alpha * (animPhase + 0.5) % 1;
ctx.beginPath();
ctx.moveTo(frameCorners.bottomRight.x, frameCorners.bottomRight.y - cornerLength);
ctx.lineTo(frameCorners.bottomRight.x, frameCorners.bottomRight.y);
ctx.lineTo(frameCorners.bottomRight.x - cornerLength, frameCorners.bottomRight.y);
ctx.stroke();
// 左下角
ctx.globalAlpha = alpha * (animPhase + 0.75) % 1;
ctx.beginPath();
ctx.moveTo(frameCorners.bottomLeft.x + cornerLength, frameCorners.bottomLeft.y);
ctx.lineTo(frameCorners.bottomLeft.x, frameCorners.bottomLeft.y);
ctx.lineTo(frameCorners.bottomLeft.x, frameCorners.bottomLeft.y - cornerLength);
ctx.stroke();
ctx.restore();
};
// 辅助函数:绘制完整矩形边框
const drawFullFrame = (color: string, alpha: number, dashPattern: number[] = []) => {
ctx.save();
ctx.strokeStyle = color;
ctx.lineWidth = frameWidth;
ctx.globalAlpha = alpha;
if (dashPattern.length > 0) {
ctx.setLineDash(dashPattern);
}
ctx.strokeRect(
frameCorners.topLeft.x,
frameCorners.topLeft.y,
frameCorners.bottomRight.x - frameCorners.topLeft.x,
frameCorners.bottomRight.y - frameCorners.topLeft.y
);
ctx.restore();
};
// 辅助函数:绘制方向箭头(在框架外部)
const drawDirectionArrows = (direction: 'up' | 'down', color: string) => {
ctx.save();
ctx.strokeStyle = color;
ctx.fillStyle = color;
ctx.lineWidth = 3;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
const arrowSize = 8;
const arrowOffset = 15; // 箭头距离框架的距离
const animProgress = (Math.sin(time * 0.003) + 1) * 0.5;
if (direction === 'up') {
// 上行箭头:在框架上方
const arrowY = frameCorners.topLeft.y - arrowOffset - animProgress * 5;
const centerX = x + w / 2;
ctx.globalAlpha = 0.8 + animProgress * 0.2;
ctx.beginPath();
ctx.moveTo(centerX, arrowY);
ctx.lineTo(centerX - arrowSize, arrowY + arrowSize);
ctx.lineTo(centerX + arrowSize, arrowY + arrowSize);
ctx.closePath();
ctx.fill();
} else {
// 下行箭头:在框架下方
const arrowY = frameCorners.bottomLeft.y + arrowOffset + animProgress * 5;
const centerX = x + w / 2;
ctx.globalAlpha = 0.8 + animProgress * 0.2;
ctx.beginPath();
ctx.moveTo(centerX, arrowY);
ctx.lineTo(centerX - arrowSize, arrowY - arrowSize);
ctx.lineTo(centerX + arrowSize, arrowY - arrowSize);
ctx.closePath();
ctx.fill();
}
ctx.restore();
};
// 辅助函数:绘制门状态指示(在框架两侧)
const drawDoorIndicators = (isOpening: boolean, color: string) => {
ctx.save();
ctx.strokeStyle = color;
ctx.lineWidth = 2;
ctx.globalAlpha = 0.7;
const progress = isOpening ?
(Math.sin(time * 0.001) + 1) * 0.5 :
1 - (Math.sin(time * 0.001) + 1) * 0.5;
const doorHeight = h * 0.3;
const doorWidth = 3;
const sideOffset = 12; // 距离框架侧边的距离
// 左门指示
const leftDoorX = frameCorners.topLeft.x - sideOffset - progress * 8;
const doorY = y + (h - doorHeight) / 2;
ctx.strokeRect(leftDoorX, doorY, doorWidth, doorHeight);
// 右门指示
const rightDoorX = frameCorners.topRight.x + sideOffset + progress * 8;
ctx.strokeRect(rightDoorX, doorY, doorWidth, doorHeight);
ctx.restore();
};
// 辅助函数:绘制状态文本
const drawStatusText = (text: string, color: string) => {
if (lodEnabled) return; // LOD模式下不显示文本
ctx.save();
ctx.fillStyle = color;
ctx.font = '10px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
ctx.globalAlpha = 0.9;
// 在图片下方显示状态文本
ctx.fillText(text, x + w / 2, frameCorners.bottomLeft.y + 5);
ctx.restore();
};
// 辅助函数:绘制连接状态指示器
const drawConnectionIndicator = () => {
if (!isConnected) {
ctx.save();
const pulseIntensity = 0.5 + Math.sin(time * 0.003) * 0.5;
// 断连图标在右上角
ctx.fillStyle = `rgba(255, 59, 48, ${pulseIntensity})`;
ctx.beginPath();
ctx.arc(frameCorners.topRight.x, frameCorners.topRight.y, 4, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = `rgba(255, 59, 48, ${pulseIntensity})`;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(frameCorners.topRight.x - 6, frameCorners.topRight.y - 6);
ctx.lineTo(frameCorners.topRight.x + 6, frameCorners.topRight.y + 6);
ctx.stroke();
ctx.restore();
}
};
// 状态渲染逻辑
if (!isConnected) {
// 离线状态:红色闪烁角 + 断连图标
drawFrameCorners('rgba(255, 59, 48, 0.8)', 0.8, true);
drawConnectionIndicator();
drawStatusText('离线', '#FF3B30');
} else {
// 在线状态根据具体状态渲染
switch (elevatorFrontDoorStatus) {
case 2: // 正在开关门
// 绿色动态框架 + 门指示器
drawFrameCorners('rgba(52, 199, 89, 0.8)', 0.7, true);
if (!lodEnabled) {
drawDoorIndicators(true, 'rgba(52, 199, 89, 0.8)');
}
drawStatusText('开门中', '#34C759');
break;
case 3: // 门已开
// 蓝色呼吸框架
drawStatusText('门已开', '#007AFF');
break;
case 1: // 门已关
if (elevatorDirection === 2) { // 向上
// 紫色动态框架 + 上行箭头
drawFrameCorners('rgba(175, 82, 222, 0.8)', 0.7, true);
if (!lodEnabled) {
drawDirectionArrows('up', 'rgba(175, 82, 222, 0.8)');
}
drawStatusText('上行中', '#AF52DE');
} else if (elevatorDirection === 3) { // 向下
// 青色动态框架 + 下行箭头
drawFrameCorners('rgba(90, 200, 250, 0.8)', 0.7, true);
if (!lodEnabled) {
drawDirectionArrows('down', 'rgba(90, 200, 250, 0.8)');
}
drawStatusText('下行中', '#5AC8FA');
} else { // 停止
// 金色静态框架
drawFullFrame('rgba(255, 204, 0, 0.6)', 0.6);
drawStatusText('停止', '#FFCC00');
}
break;
default: // 未知状态
// 灰色静态框架
drawFullFrame('rgba(142, 142, 147, 0.5)', 0.5);
drawStatusText('未知', '#8E8E93');
break;
}
}
}
/**
*
* @param ctx Canvas上下文
@ -285,6 +558,15 @@ export function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void {
const largeThemeStrokeColor = get(theme, active ? 'point-l.strokeActive' : 'point-l.stroke');
ctx.strokeStyle = statusStyle ?? (largeTypeStrokeColor || largeGeneralStrokeColor || largeThemeStrokeColor || '');
ctx.stroke();
// 电梯点:添加动画效果
if (type === MapPointType.) {
// 直接传入包含后端字段的 pen.point 数据
if (pen.point) {
const currentTime = performance.now();
drawElevatorAnimation(ctx, pen, pen.point, currentTime);
}
}
break;
}
default:

View File

@ -7,37 +7,31 @@ import type { MapPen } from '@api/map';
import { MapPointType } from '@api/map';
import type { EditorService } from '@core/editor.service';
import { defineStore } from 'pinia';
import { computed, markRaw, ref } from 'vue';
import { markRaw, ref } from 'vue';
// 电梯状态枚举
export enum ElevatorStatus {
IDLE = 0, // 静止
OPENING = 1, // 开门中
CLOSING = 2, // 关门中
MOVING_UP = 3, // 上行
MOVING_DOWN = 4, // 下行
DOOR_OPEN = 5, // 门已开
DOOR_CLOSED = 6, // 门已关
FAULT = 7, // 故障
OFFLINE = 8, // 离线
}
// 电梯楼层 0-未知)
// 电梯方向 0-未知1-停止2-向上3-向下)
// 电梯前门状态0-未知1-关2-正在开关3-开)
// 注意:这些字段定义直接来自后端,不做任何修改
// 电梯数据接口
// 电梯数据接口(直接使用后端字段)
export interface ElevatorData {
deviceId: string; // 设备ID
status: ElevatorStatus; // 当前状态
floor?: number; // 当前楼层
elevatorFloor: number; // 电梯楼层0-未知)
elevatorDirection: number; // 电梯方向0-未知1-停止2-向上3-向下)
elevatorFrontDoorStatus: number; // 电梯前门状态0-未知1-关2-正在开关3-开)
isConnected: boolean; // 是否连接
lastUpdate: number; // 最后更新时间戳
penId?: string; // 对应的画布点位ID
}
// WebSocket推送的电梯数据接口
// WebSocket推送的电梯数据接口(对应后端格式)
export interface ElevatorWebSocketData {
id: string; // 设备ID
type: 102; // 电梯类型标识
status: ElevatorStatus; // 电梯状态
floor?: number; // 当前楼层
elevatorFloor: number; // 电梯楼层0-未知)
elevatorDirection: number; // 电梯方向0-未知1-停止2-向上3-向下)
elevatorFrontDoorStatus: number; // 电梯前门状态0-未知1-关2-正在开关3-开)
isConnected: boolean; // 连接状态
}
@ -54,49 +48,9 @@ export const useElevatorStore = defineStore('elevator', () => {
const elevatorPoints = ref<Map<string, ElevatorPoint>>(new Map());
const editorService = ref<EditorService | null>(null);
// ========== 计算属性 ==========
// 获取所有电梯数据
const allElevators = computed(() => Array.from(elevators.value.values()));
// 获取在线电梯数量
const onlineElevatorsCount = computed(() =>
allElevators.value.filter(e => e.isConnected).length
);
// 获取离线电梯数量
const offlineElevatorsCount = computed(() =>
allElevators.value.filter(e => !e.isConnected).length
);
// 获取故障电梯数量
const faultElevatorsCount = computed(() =>
allElevators.value.filter(e => e.status === ElevatorStatus.FAULT).length
);
// 按状态分组的电梯
const elevatorsByStatus = computed(() => {
const groups: Record<ElevatorStatus, ElevatorData[]> = {} as any;
// 初始化所有状态
Object.values(ElevatorStatus).forEach(status => {
if (typeof status === 'number') {
groups[status] = [];
}
});
// 分组
allElevators.value.forEach(elevator => {
groups[elevator.status].push(elevator);
});
return groups;
});
// ========== 方法 ==========
/**
*
*
@ -151,7 +105,13 @@ export const useElevatorStore = defineStore('elevator', () => {
* WebSocket推送的电梯数据
*/
const handleElevatorWebSocketData = (data: ElevatorWebSocketData) => {
const { id: deviceId, status, floor, isConnected } = data;
const {
id: deviceId,
elevatorFloor,
elevatorDirection,
elevatorFrontDoorStatus,
isConnected
} = data;
// 获取或创建电梯数据
let elevatorData = elevators.value.get(deviceId);
@ -160,16 +120,19 @@ export const useElevatorStore = defineStore('elevator', () => {
if (!elevatorData) {
elevatorData = {
deviceId,
status: ElevatorStatus.IDLE,
elevatorFloor: 0,
elevatorDirection: 0, // 未知
elevatorFrontDoorStatus: 0, // 未知
isConnected: false,
lastUpdate: Date.now(),
penId: elevatorPoint?.penId
};
}
// 更新数据
elevatorData.status = isConnected ? status : ElevatorStatus.OFFLINE;
elevatorData.floor = floor;
// 更新数据(直接使用后端字段)
elevatorData.elevatorFloor = elevatorFloor;
elevatorData.elevatorDirection = elevatorDirection;
elevatorData.elevatorFrontDoorStatus = elevatorFrontDoorStatus;
elevatorData.isConnected = isConnected;
elevatorData.lastUpdate = Date.now();
@ -180,6 +143,7 @@ export const useElevatorStore = defineStore('elevator', () => {
// 存储到Map
elevators.value.set(deviceId, elevatorData);
// 更新画布上的电梯点显示
if (elevatorData.penId && editorService.value) {
updateElevatorPointDisplay(elevatorData);
@ -187,124 +151,152 @@ export const useElevatorStore = defineStore('elevator', () => {
};
/**
*
* -
*/
const updateElevatorPointDisplay = (elevatorData: ElevatorData) => {
if (!editorService.value || !elevatorData.penId) return;
const penId = elevatorData.penId;
// 获取状态对应的颜色和图标
const { color, iconPath } = getElevatorDisplay(elevatorData.status, elevatorData.isConnected);
// 获取状态对应的颜色、图标和动画配置
const displayConfig = getElevatorDisplay(elevatorData);
// 基础更新数据
const updateData: any = {
// 设置图标
image: iconPath,
image: displayConfig.iconPath,
// 更新点位信息
point: {
...((editorService.value.getPenById(penId) as any)?.point || {}),
elevatorStatus: elevatorData.status,
isConnected: elevatorData.isConnected,
currentFloor: elevatorData.floor,
currentFloor: elevatorData.elevatorFloor,
lastUpdate: elevatorData.lastUpdate,
// 使用颜色区分状态
color
}
// 保存原始后端数据
elevatorDirection: elevatorData.elevatorDirection,
elevatorFrontDoorStatus: elevatorData.elevatorFrontDoorStatus,
// 使用新的颜色系统
color: displayConfig.color,
// 动画配置
animationClass: displayConfig.animationClass,
priority: displayConfig.priority
},
// 增强的样式配置
statusStyle: displayConfig.color
};
try {
// 更新电梯点
editorService.value.updatePen(penId, updateData, false);
} catch (error) {
console.error(`🛗 更新电梯点显示失败 (${elevatorData.deviceId}):`, error);
}
};
/**
*
* 使
*/
const getElevatorDisplay = (status: ElevatorStatus, isConnected: boolean) => {
const getElevatorDisplay = (elevatorData: ElevatorData) => {
// 生成图标路径的辅助函数
const getIconPath = (theme: string) => `${import.meta.env.BASE_URL}/point/elevator-${theme}.svg`;
// 如果离线,显示灰色
if (!isConnected) {
// 现代化颜色配置系统
const colors = {
offline: '#FF3B30', // 红色 - 离线
opening: '#34C759', // 绿色 - 开门中
doorOpen: '#007AFF', // 蓝色 - 门已开
moving: '#AF52DE', // 紫色 - 运行中
upMoving: '#AF52DE', // 紫色 - 上行
downMoving: '#5AC8FA', // 青色 - 下行
stopped: '#FFCC00', // 金色 - 停止
idle: '#8E8E93', // 灰色 - 静止
unknown: '#8E8E93' // 灰色 - 未知
};
// 如果离线,显示红色
if (!elevatorData.isConnected) {
return {
color: '#999999',
color: colors.offline,
iconPath: getIconPath('offline'),
text: '离线'
text: '离线',
priority: 'high',
animationClass: 'pulse-error'
};
}
const statusMap = {
[ElevatorStatus.IDLE]: {
color: '#1890FF',
iconPath: getIconPath('idle'),
text: '静止'
},
[ElevatorStatus.OPENING]: {
color: '#52C41A',
// 根据后端字段判断显示状态
const { elevatorDirection, elevatorFrontDoorStatus, elevatorFloor } = elevatorData;
// 优先判断门状态
switch (elevatorFrontDoorStatus) {
case 2: // 正在开关门
return {
color: colors.opening,
iconPath: getIconPath('opening'),
text: '开门中'
},
[ElevatorStatus.CLOSING]: {
color: '#FA8C16',
iconPath: getIconPath('closing'),
text: '关门中'
},
[ElevatorStatus.MOVING_UP]: {
color: '#722ED1',
iconPath: getIconPath('up'),
text: '上行中'
},
[ElevatorStatus.MOVING_DOWN]: {
color: '#13C2C2',
iconPath: getIconPath('down'),
text: '下行中'
},
[ElevatorStatus.DOOR_OPEN]: {
color: '#52C41A',
text: `开门中 ${elevatorFloor > 0 ? `(${elevatorFloor}F)` : ''}`,
priority: 'high',
animationClass: 'pulse-success'
};
case 3: // 门已开
return {
color: colors.doorOpen,
iconPath: getIconPath('door-open'),
text: '门已开'
},
[ElevatorStatus.DOOR_CLOSED]: {
color: '#1890FF',
iconPath: getIconPath('door-closed'),
text: '门已关'
},
[ElevatorStatus.FAULT]: {
color: '#FF4D4F',
iconPath: getIconPath('fault'),
text: '故障'
},
text: `门已开 ${elevatorFloor > 0 ? `(${elevatorFloor}F)` : ''}`,
priority: 'normal',
animationClass: 'breathe'
};
return statusMap[status] || statusMap[ElevatorStatus.IDLE];
case 1: // 门已关
switch (elevatorDirection) {
case 2: // 向上
return {
color: colors.upMoving,
iconPath: getIconPath('up'),
text: `上行中 ${elevatorFloor > 0 ? `(${elevatorFloor}F)` : ''}`,
priority: 'high',
animationClass: 'flow-up'
};
/**
*
*/
const getElevatorById = (deviceId: string): ElevatorData | undefined => {
return elevators.value.get(deviceId);
case 3: // 向下
return {
color: colors.downMoving,
iconPath: getIconPath('down'),
text: `下行中 ${elevatorFloor > 0 ? `(${elevatorFloor}F)` : ''}`,
priority: 'high',
animationClass: 'flow-down'
};
/**
*
*/
const updateElevatorStatus = (
deviceId: string,
status: ElevatorStatus,
isConnected: boolean = true
) => {
const mockData: ElevatorWebSocketData = {
id: deviceId,
type: 102,
status,
isConnected
case 1: // 停止
return {
color: colors.stopped,
iconPath: getIconPath('stopped'),
text: `停止 ${elevatorFloor > 0 ? `(${elevatorFloor}F)` : ''}`,
priority: 'normal',
animationClass: 'gentle-pulse'
};
handleElevatorWebSocketData(mockData);
default:
break;
}
break;
default:
break;
}
// 未知状态
return {
color: colors.unknown,
iconPath: getIconPath('unknown'),
text: '未知状态',
priority: 'low',
animationClass: 'dim'
};
};
/**
*
@ -320,29 +312,8 @@ export const useElevatorStore = defineStore('elevator', () => {
});
};
/**
*
*/
const clearAllData = () => {
elevators.value.clear();
console.log('🛗 电梯数据已清除');
};
/**
*
*/
const getStatistics = () => {
return {
total: allElevators.value.length,
online: onlineElevatorsCount.value,
offline: offlineElevatorsCount.value,
fault: faultElevatorsCount.value,
byStatus: Object.entries(elevatorsByStatus.value).reduce((acc, [status, list]) => {
acc[Number(status)] = list.length;
return acc;
}, {} as Record<number, number>)
};
};
@ -351,22 +322,10 @@ export const useElevatorStore = defineStore('elevator', () => {
elevators,
elevatorPoints,
editorService,
// 计算属性
allElevators,
onlineElevatorsCount,
offlineElevatorsCount,
faultElevatorsCount,
elevatorsByStatus,
// 方法
setEditorService,
handleElevatorWebSocketData,
getElevatorById,
updateElevatorStatus,
refreshMapping,
clearAllData,
getStatistics,
getElevatorDisplay
};
});

View File

@ -1,129 +0,0 @@
/**
*
*
*/
import { ElevatorStatus,useElevatorStore } from '../stores/elevator.store';
export const createElevatorMockData = () => {
const elevatorStore = useElevatorStore();
// 模拟的设备ID列表与API返回的设备ID格式保持一致
const mockDeviceIds = [
'2',
'4',
'6',
'8'
];
// 缓存上次的状态,避免无意义的更新
const lastStates = new Map<string, { status: ElevatorStatus; floor: number; isConnected: boolean }>();
// 生成模拟数据
const generateMockData = () => {
mockDeviceIds.forEach((deviceId, index) => {
// 为每个电梯生成不同的状态
let status: ElevatorStatus;
let floor: number;
// 70%概率保持当前状态30%概率改变状态
if (Math.random() > 0.7) {
switch (index % 4) {
case 0:
status = ElevatorStatus.MOVING_UP;
break;
case 1:
status = ElevatorStatus.MOVING_DOWN;
break;
case 2:
status = ElevatorStatus.OPENING;
break;
default:
status = ElevatorStatus.IDLE;
}
} else {
// 保持当前状态
const last = lastStates.get(deviceId);
status = last ? last.status : (index % 4 === 0 ? ElevatorStatus.MOVING_UP : index % 4 === 1 ? ElevatorStatus.MOVING_DOWN : index % 4 === 2 ? ElevatorStatus.OPENING : ElevatorStatus.IDLE);
}
// 95%概率在线5%概率离线
const isConnected = Math.random() > 0.05;
// 只有在线时才更新楼层
if (isConnected && Math.random() > 0.5) {
// 30%概率改变楼层
const last = lastStates.get(deviceId);
if (Math.random() > 0.7) {
floor = last ? Math.min(10, Math.max(1, last.floor + (Math.random() > 0.5 ? 1 : -1))) : Math.floor(Math.random() * 10) + 1;
} else {
floor = last ? last.floor : Math.floor(Math.random() * 10) + 1;
}
} else {
floor = lastStates.get(deviceId)?.floor || Math.floor(Math.random() * 10) + 1;
}
// 检查状态是否真的发生了变化
const lastState = lastStates.get(deviceId);
if (lastState &&
lastState.status === status &&
lastState.floor === floor &&
lastState.isConnected === isConnected) {
// 状态没有变化,跳过更新
return;
}
// 更新缓存
lastStates.set(deviceId, { status, floor, isConnected });
// 偶尔模拟故障5%概率)
if (Math.random() > 0.95 && isConnected) {
status = ElevatorStatus.FAULT;
}
elevatorStore.handleElevatorWebSocketData({
id: deviceId,
type: 102,
status: isConnected ? status : ElevatorStatus.OFFLINE,
floor,
isConnected
});
});
};
// 初始生成一次数据
generateMockData();
// 设置定时器每8-12秒更新一次数据降低频率
const interval = setInterval(() => {
generateMockData();
}, Math.random() * 4000 + 8000); // 8-12秒随机间隔
console.log('🛗 电梯模拟数据已启动设备ID:', mockDeviceIds);
// 返回停止函数
return () => {
clearInterval(interval);
lastStates.clear();
console.log('🛗 电梯模拟数据已停止');
};
};
// 创建单个电梯的测试数据
export const createSingleElevatorData = (deviceId: string, status?: ElevatorStatus) => {
const elevatorStore = useElevatorStore();
elevatorStore.handleElevatorWebSocketData({
id: deviceId,
type: 102,
status: status || ElevatorStatus.IDLE,
floor: Math.floor(Math.random() * 10) + 1,
isConnected: true
});
};
// 清除所有电梯数据
export const clearElevatorData = () => {
const elevatorStore = useElevatorStore();
elevatorStore.clearAllData();
};