Compare commits
3 Commits
360294c6a1
...
13c1a689b2
Author | SHA1 | Date | |
---|---|---|---|
13c1a689b2 | |||
58c9cad3d3 | |||
080b60ab1b |
70
src/apis/amr/api.ts
Normal file
70
src/apis/amr/api.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import http from '@core/http';
|
||||
|
||||
const enum API {
|
||||
// 控制接口 - 只包含右键菜单需要的
|
||||
控制AMR = '/amr/control',
|
||||
抢占控制权 = '/amr/controlAmr',
|
||||
设置接单状态 = '/amr/acceptTask',
|
||||
}
|
||||
|
||||
// 只保留右键菜单需要的API函数
|
||||
|
||||
/**
|
||||
* 控制AMR - 用于暂停、继续、启动、停止、前往充电、前往停靠、路径导航、重置、诊断、更新
|
||||
*/
|
||||
export async function controlAmr(id: string, action: string): Promise<boolean> {
|
||||
type B = { id: string; action: string };
|
||||
type D = void;
|
||||
try {
|
||||
await http.post<D, B>(API.控制AMR, { id, action });
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('控制AMR失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 抢占控制权 - 需要小车IP
|
||||
*/
|
||||
export async function controlAmrTakeControl(ip: string): Promise<boolean> {
|
||||
try {
|
||||
await http.get(`${API.抢占控制权}/${ip}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('抢占控制权失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置接单状态
|
||||
*/
|
||||
export async function setAcceptTask(id: string, acceptTask: boolean): Promise<boolean> {
|
||||
type B = { acceptTask: number; ids: string };
|
||||
type D = void;
|
||||
try {
|
||||
await http.put<D, B>(API.设置接单状态, {
|
||||
acceptTask: acceptTask ? 1 : 0,
|
||||
ids: id
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('设置接单状态失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置可接单
|
||||
*/
|
||||
export async function setAvailable(id: string): Promise<boolean> {
|
||||
return await setAcceptTask(id, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置不可接单
|
||||
*/
|
||||
export async function setUnavailable(id: string): Promise<boolean> {
|
||||
return await setAcceptTask(id, false);
|
||||
}
|
86
src/apis/amr/constant.ts
Normal file
86
src/apis/amr/constant.ts
Normal file
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* AMR相关常量定义 - 只包含右键菜单需要的常量
|
||||
*/
|
||||
|
||||
import type { AmrAction } from './type';
|
||||
|
||||
// AMR操作常量 - 对应右键菜单的操作
|
||||
export const AMR_ACTIONS: Record<AmrAction, string> = {
|
||||
seize_control: '抢占控制权',
|
||||
enable_orders: '可接单',
|
||||
disable_orders: '不可接单',
|
||||
pause: '暂停',
|
||||
resume: '继续',
|
||||
go_charge: '前往充电',
|
||||
go_dock: '前往停靠',
|
||||
navigate: '路径导航',
|
||||
start: '启动',
|
||||
stop: '停止',
|
||||
reset: '重置',
|
||||
diagnose: '诊断',
|
||||
update: '更新',
|
||||
custom_image: '自定义图片',
|
||||
};
|
||||
|
||||
// AMR操作图标 - 对应右键菜单的图标
|
||||
export const AMR_ACTION_ICONS: Record<AmrAction, string> = {
|
||||
seize_control: '🎮',
|
||||
enable_orders: '✅',
|
||||
disable_orders: '❌',
|
||||
pause: '⏸️',
|
||||
resume: '▶️',
|
||||
go_charge: '🔋',
|
||||
go_dock: '🏠',
|
||||
navigate: '🧭',
|
||||
start: '🚀',
|
||||
stop: '⏹️',
|
||||
reset: '🔄',
|
||||
diagnose: '🔧',
|
||||
update: '📱',
|
||||
custom_image: '🖼️',
|
||||
};
|
||||
|
||||
// AMR操作分组 - 对应右键菜单的分组
|
||||
export const AMR_ACTION_GROUPS = {
|
||||
control: ['seize_control', 'enable_orders', 'disable_orders'] as AmrAction[],
|
||||
runtime: ['pause', 'resume', 'start', 'stop'] as AmrAction[],
|
||||
navigation: ['go_charge', 'go_dock', 'navigate'] as AmrAction[],
|
||||
system: ['reset', 'diagnose', 'update', 'custom_image'] as AmrAction[],
|
||||
};
|
||||
|
||||
// AMR操作分组标题 - 对应右键菜单的分组标题
|
||||
export const AMR_ACTION_GROUP_TITLES = {
|
||||
control: '控制权限',
|
||||
runtime: '运行控制',
|
||||
navigation: '导航控制',
|
||||
system: '系统管理',
|
||||
};
|
||||
|
||||
// AMR操作超时时间(毫秒)
|
||||
export const AMR_ACTION_TIMEOUTS: Record<AmrAction, number> = {
|
||||
seize_control: 5000,
|
||||
enable_orders: 3000,
|
||||
disable_orders: 3000,
|
||||
pause: 3000,
|
||||
resume: 3000,
|
||||
go_charge: 10000,
|
||||
go_dock: 10000,
|
||||
navigate: 15000,
|
||||
start: 5000,
|
||||
stop: 3000,
|
||||
reset: 10000,
|
||||
diagnose: 30000,
|
||||
update: 60000,
|
||||
custom_image: 0, // 自定义图片不需要API调用
|
||||
};
|
||||
|
||||
// AMR操作结果消息
|
||||
export const AMR_ACTION_MESSAGES = {
|
||||
success: '操作成功',
|
||||
failed: '操作失败',
|
||||
timeout: '操作超时',
|
||||
noPermission: '无操作权限',
|
||||
notConnected: 'AMR未连接',
|
||||
busy: 'AMR忙碌中',
|
||||
error: 'AMR故障中',
|
||||
};
|
29
src/apis/amr/index.ts
Normal file
29
src/apis/amr/index.ts
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* AMR API 入口文件
|
||||
* 统一导出右键菜单需要的API、类型和常量
|
||||
*/
|
||||
|
||||
// 导出API函数
|
||||
export * from './api';
|
||||
|
||||
// 导出类型定义
|
||||
export * from './type';
|
||||
|
||||
// 导出常量
|
||||
export * from './constant';
|
||||
|
||||
// 重新导出常用类型,方便使用
|
||||
export type {
|
||||
AmrAction,
|
||||
AmrActionResult,
|
||||
} from './type';
|
||||
|
||||
// 重新导出常用常量,方便使用
|
||||
export {
|
||||
AMR_ACTION_GROUP_TITLES,
|
||||
AMR_ACTION_GROUPS,
|
||||
AMR_ACTION_ICONS,
|
||||
AMR_ACTION_MESSAGES,
|
||||
AMR_ACTION_TIMEOUTS,
|
||||
AMR_ACTIONS,
|
||||
} from './constant';
|
27
src/apis/amr/type.ts
Normal file
27
src/apis/amr/type.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* AMR相关类型定义 - 只包含右键菜单需要的类型
|
||||
*/
|
||||
|
||||
// AMR操作类型 - 对应右键菜单的操作
|
||||
export type AmrAction =
|
||||
| 'seize_control' // 抢占控制权
|
||||
| 'enable_orders' // 可接单
|
||||
| 'disable_orders' // 不可接单
|
||||
| 'pause' // 暂停
|
||||
| 'resume' // 继续
|
||||
| 'go_charge' // 前往充电
|
||||
| 'go_dock' // 前往停靠
|
||||
| 'navigate' // 路径导航
|
||||
| 'start' // 启动
|
||||
| 'stop' // 停止
|
||||
| 'reset' // 重置
|
||||
| 'diagnose' // 诊断
|
||||
| 'update' // 更新
|
||||
| 'custom_image'; // 自定义图片
|
||||
|
||||
// AMR操作结果
|
||||
export interface AmrActionResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
data?: any;
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
:placement="dropdownPlacement"
|
||||
:get-popup-container="getPopupContainer"
|
||||
@open-change="handleOpenChange"
|
||||
z-index="998"
|
||||
:z-index="998"
|
||||
>
|
||||
<div
|
||||
ref="triggerRef"
|
||||
@ -53,7 +53,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, defineAsyncComponent, ref } from 'vue';
|
||||
|
||||
import type { RobotInfo, StorageLocationInfo } from '../../services/context-menu';
|
||||
import type { RobotInfo } from '../../apis/robot';
|
||||
import type { StorageLocationInfo } from '../../services/context-menu';
|
||||
|
||||
// 使用动态导入避免 TypeScript 错误
|
||||
const DefaultMenu = defineAsyncComponent(() => import('./default-menu.vue'));
|
||||
|
@ -3,7 +3,7 @@
|
||||
<!-- 机器人图片设置模态框 -->
|
||||
<RobotImageSettingsModal
|
||||
v-model:open="imageSettingsVisible"
|
||||
:robots="availableRobots"
|
||||
:robots="robotInfo ? [{ name: robotInfo.label, type: 'robot', id: robotInfo.id }] : []"
|
||||
:selected-robot-name="selectedRobotName"
|
||||
@save="handleImageSettingsSave"
|
||||
/>
|
||||
@ -12,30 +12,30 @@
|
||||
<!-- 左侧:机器人信息区域 -->
|
||||
<div v-if="robotInfo" class="robot-info-section">
|
||||
<div class="robot-header">
|
||||
<div class="robot-name">{{ robotInfo.name }}</div>
|
||||
<div class="robot-status" :style="{ color: getStatusColor(robotInfo.status) }">
|
||||
{{ getStatusText(robotInfo.status) }}
|
||||
<div class="robot-name">{{ robotInfo.label }}</div>
|
||||
<div class="robot-status" :style="{ color: getRobotStatusColor(robotInfo.state as any || 'offline') }">
|
||||
{{ getRobotStatusText(robotInfo.state as any || 'offline') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="robot-details">
|
||||
<div v-if="robotInfo.batteryLevel !== undefined" class="detail-item">
|
||||
<div v-if="robotInfo.battery !== undefined" class="detail-item">
|
||||
<span class="detail-label">电量:</span>
|
||||
<div class="battery-container">
|
||||
<div class="battery-bar">
|
||||
<div
|
||||
class="battery-fill"
|
||||
:style="{ width: `${robotInfo.batteryLevel}%` }"
|
||||
:class="getBatteryClass(robotInfo.batteryLevel)"
|
||||
:style="{ width: `${robotInfo.battery}%` }"
|
||||
:class="getBatteryClass(robotInfo.battery)"
|
||||
></div>
|
||||
</div>
|
||||
<span class="battery-text">{{ robotInfo.batteryLevel }}%</span>
|
||||
<span class="battery-text">{{ robotInfo.battery }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="robotInfo.currentTask" class="detail-item">
|
||||
<span class="detail-label">当前任务:</span>
|
||||
<span class="detail-value">{{ robotInfo.currentTask }}</span>
|
||||
<div v-if="robotInfo.targetPoint" class="detail-item">
|
||||
<span class="detail-label">目标点位:</span>
|
||||
<span class="detail-value">{{ robotInfo.targetPoint }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -126,9 +126,10 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { message } from 'ant-design-vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import type { RobotAction, RobotInfo } from '../../services/context-menu/robot-menu.service';
|
||||
import type { RobotInfo } from '../../apis/robot';
|
||||
import type { RobotAction } from '../../services/context-menu/robot-menu.service';
|
||||
import {
|
||||
executeRobotAction,
|
||||
getRobotStatusColor,
|
||||
@ -138,7 +139,6 @@ import RobotImageSettingsModal from '../modal/robot-image-settings-modal.vue';
|
||||
|
||||
interface Props {
|
||||
robotInfo?: RobotInfo;
|
||||
availableRobots?: Array<{ name: string; type: string; id: string }>;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
@ -146,9 +146,7 @@ interface Emits {
|
||||
(e: 'customImage', data: { robotInfo: RobotInfo }): void;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
availableRobots: () => []
|
||||
});
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
@ -156,39 +154,13 @@ const emit = defineEmits<Emits>();
|
||||
const imageSettingsVisible = ref(false);
|
||||
const selectedRobotName = ref('');
|
||||
|
||||
// 生成可用的机器人列表
|
||||
const availableRobots = computed(() => {
|
||||
// 如果父组件传递了机器人列表,使用父组件的
|
||||
if (props.availableRobots && props.availableRobots.length > 0) {
|
||||
return props.availableRobots;
|
||||
}
|
||||
|
||||
// 否则,基于当前机器人信息生成列表
|
||||
if (props.robotInfo) {
|
||||
return [{
|
||||
name: props.robotInfo.name,
|
||||
type: 'robot', // 默认类型
|
||||
id: props.robotInfo.id
|
||||
}];
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
// 定义组件名称
|
||||
defineOptions({
|
||||
name: 'RobotMenu',
|
||||
});
|
||||
|
||||
// 获取状态显示文本
|
||||
const getStatusText = (status: string): string => {
|
||||
return getRobotStatusText(status);
|
||||
};
|
||||
|
||||
// 获取状态颜色
|
||||
const getStatusColor = (status: string): string => {
|
||||
return getRobotStatusColor(status);
|
||||
};
|
||||
// 直接使用服务函数,无需包装
|
||||
|
||||
// 获取电量条样式类
|
||||
const getBatteryClass = (batteryLevel: number) => {
|
||||
@ -197,33 +169,27 @@ const getBatteryClass = (batteryLevel: number) => {
|
||||
return 'battery-low';
|
||||
};
|
||||
|
||||
// 处理自定义图片操作
|
||||
const handleCustomImage = () => {
|
||||
if (!props.robotInfo?.name) {
|
||||
message.error('未找到机器人信息');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('打开机器人图片设置:', props.robotInfo);
|
||||
|
||||
// 打开模态框,不关闭右键菜单
|
||||
selectedRobotName.value = props.robotInfo.name;
|
||||
imageSettingsVisible.value = true;
|
||||
console.log('设置模态框可见性:', imageSettingsVisible.value);
|
||||
|
||||
// 只触发自定义图片事件,不触发操作完成事件
|
||||
emit('customImage', {
|
||||
robotInfo: props.robotInfo
|
||||
});
|
||||
};
|
||||
|
||||
// 处理机器人操作
|
||||
const handleRobotAction = async (action: RobotAction | 'custom_image', actionName: string) => {
|
||||
if (!props.robotInfo) return;
|
||||
|
||||
// 处理自定义图片操作
|
||||
if (action === 'custom_image') {
|
||||
handleCustomImage();
|
||||
if (!props.robotInfo?.label) {
|
||||
message.error('未找到机器人信息');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('打开机器人图片设置:', props.robotInfo);
|
||||
|
||||
// 打开模态框,不关闭右键菜单
|
||||
selectedRobotName.value = props.robotInfo.label;
|
||||
imageSettingsVisible.value = true;
|
||||
|
||||
// 只触发自定义图片事件,不触发操作完成事件
|
||||
emit('customImage', {
|
||||
robotInfo: props.robotInfo
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@
|
||||
<script setup lang="ts">
|
||||
import { UploadOutlined } from '@ant-design/icons-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { computed, inject, onMounted, ref, watch } from 'vue';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import colorConfig from '../../services/color/color-config.service';
|
||||
import { EditorService } from '../../services/editor.service';
|
||||
@ -88,6 +88,7 @@ interface Props {
|
||||
open: boolean;
|
||||
robots: RobotInfo[];
|
||||
selectedRobotName?: string;
|
||||
editor?: EditorService; // 添加editor作为props
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
@ -118,8 +119,7 @@ const formData = ref({
|
||||
});
|
||||
|
||||
// 获取编辑器服务实例
|
||||
const EDITOR_KEY = Symbol('editor-key');
|
||||
const editor = inject<{ value: EditorService }>(EDITOR_KEY);
|
||||
const editor = computed(() => props.editor);
|
||||
|
||||
// 计算属性
|
||||
const availableRobots = computed(() => props.robots);
|
||||
@ -166,6 +166,13 @@ const loadRobotImages = (robotName: string) => {
|
||||
formData.value.images.normal = colorConfig.getRobotCustomImage(robotName, 'normal') || '';
|
||||
};
|
||||
|
||||
// 统一的更新机器人图片函数
|
||||
const updateRobotImage = () => {
|
||||
if (editor.value && typeof editor.value.updateRobotImage === 'function') {
|
||||
editor.value.updateRobotImage(selectedRobot.value);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理图片上传
|
||||
const handleImageUpload = async (file: File, state: 'normal') => {
|
||||
if (!selectedRobot.value) {
|
||||
@ -192,9 +199,7 @@ const handleImageUpload = async (file: File, state: 'normal') => {
|
||||
formData.value.images[state] = colorConfig.getRobotCustomImage(selectedRobot.value, state) || '';
|
||||
|
||||
// 刷新机器人pen元素以显示新图片
|
||||
if (editor?.value && typeof editor.value.updateRobotImage === 'function') {
|
||||
editor.value.updateRobotImage(selectedRobot.value);
|
||||
}
|
||||
updateRobotImage();
|
||||
|
||||
message.success('自定义图片上传成功');
|
||||
} catch (error) {
|
||||
@ -213,12 +218,7 @@ const removeImage = (state: 'normal') => {
|
||||
|
||||
colorConfig.removeRobotCustomImage(selectedRobot.value, state);
|
||||
formData.value.images[state] = '';
|
||||
|
||||
// 刷新机器人pen元素以显示删除后的效果
|
||||
if (editor?.value && typeof editor.value.updateRobotImage === 'function') {
|
||||
editor.value.updateRobotImage(selectedRobot.value);
|
||||
}
|
||||
|
||||
updateRobotImage();
|
||||
message.success('自定义图片已删除');
|
||||
};
|
||||
|
||||
@ -228,12 +228,7 @@ const resetImages = () => {
|
||||
|
||||
colorConfig.removeRobotCustomImage(selectedRobot.value);
|
||||
formData.value.images.normal = '';
|
||||
|
||||
// 刷新机器人pen元素以显示重置后的效果
|
||||
if (editor?.value && typeof editor.value.updateRobotImage === 'function') {
|
||||
editor.value.updateRobotImage(selectedRobot.value);
|
||||
}
|
||||
|
||||
updateRobotImage();
|
||||
message.success('图片已重置');
|
||||
};
|
||||
|
||||
@ -243,12 +238,7 @@ const clearAllImages = () => {
|
||||
|
||||
colorConfig.removeRobotCustomImage(selectedRobot.value);
|
||||
formData.value.images.normal = '';
|
||||
|
||||
// 刷新机器人pen元素以显示清除后的效果
|
||||
if (editor?.value && typeof editor.value.updateRobotImage === 'function') {
|
||||
editor.value.updateRobotImage(selectedRobot.value);
|
||||
}
|
||||
|
||||
updateRobotImage();
|
||||
message.success('图片已清除');
|
||||
};
|
||||
|
||||
|
@ -11,8 +11,7 @@ import { autoDoorSimulationService, type AutoDoorWebSocketData } from '../servic
|
||||
import {
|
||||
type ContextMenuState,
|
||||
createContextMenuManager,
|
||||
handleContextMenuFromPenData,
|
||||
isClickInsideMenu} from '../services/context-menu.service';
|
||||
handleContextMenuFromPenData} from '../services/context-menu.service';
|
||||
import { EditorService } from '../services/editor.service';
|
||||
import { StorageLocationService } from '../services/storage-location.service';
|
||||
import { useViewState } from '../services/useViewState';
|
||||
@ -232,17 +231,6 @@ onMounted(async () => {
|
||||
// 设置编辑器服务
|
||||
if (editor.value) {
|
||||
autoDoorSimulationService.setEditorService(editor.value);
|
||||
// 设置颜色配置服务的编辑器实例
|
||||
// colorConfig.setEditorService(editor.value);
|
||||
|
||||
// 注释掉模拟逻辑,使用真实WebSocket数据
|
||||
// autoDoorSimulationService.startSimulation({
|
||||
// deviceId: 'test01',
|
||||
// label: 'TestAutoDoor01',
|
||||
// interval: 3000,
|
||||
// initialStatus: 0,
|
||||
// enableLogging: true,
|
||||
// });
|
||||
}
|
||||
|
||||
// 订阅右键菜单状态变化
|
||||
@ -267,8 +255,6 @@ onUnmounted(() => {
|
||||
storageLocationService.value?.destroy();
|
||||
// 清理自动门点服务(清空缓冲数据)
|
||||
autoDoorSimulationService.clearBufferedData();
|
||||
// 注释掉模拟相关的清理,只保留WebSocket数据处理的清理
|
||||
// autoDoorSimulationService.stopAllSimulations();
|
||||
|
||||
// 移除EditorService事件监听器
|
||||
if (editor.value) {
|
||||
@ -371,18 +357,11 @@ const backToCards = () => {
|
||||
const handleEditorContextMenu = (penData: Record<string, unknown>) => {
|
||||
console.log('EditorService自定义右键菜单事件:', penData);
|
||||
handleContextMenuFromPenData(penData, contextMenuManager, {
|
||||
storageLocationService: storageLocationService.value
|
||||
storageLocationService: storageLocationService.value,
|
||||
robotService: editor.value // 传递EditorService作为机器人服务
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理原生右键菜单事件(作为备用)
|
||||
* @param event 鼠标事件
|
||||
*/
|
||||
const handleContextMenuEvent = (event: MouseEvent) => {
|
||||
// 这个函数现在作为备用,因为主要逻辑在handleEditorContextMenu中
|
||||
console.log('原生右键菜单事件(备用):', event);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@ -407,16 +386,12 @@ const handleActionComplete = (data: any) => {
|
||||
* @param event 点击事件
|
||||
*/
|
||||
const handleGlobalClick = (event: MouseEvent) => {
|
||||
|
||||
// 检查是否点击了关闭按钮
|
||||
const closeButton = (event.target as Element)?.closest('.close-button');
|
||||
if (closeButton) {
|
||||
// 如果点击了关闭按钮,让按钮自己的点击事件处理关闭
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@ -459,7 +434,7 @@ const handleGlobalKeydown = (event: KeyboardEvent) => {
|
||||
</a-tabs>
|
||||
</a-layout-sider>
|
||||
<a-layout-content>
|
||||
<div ref="container" class="editor-container full" @contextmenu="handleContextMenuEvent"></div>
|
||||
<div ref="container" class="editor-container full"></div>
|
||||
<!-- 自定义地图工具栏(固定右下角,最小侵入) -->
|
||||
<MapToolbar :token="EDITOR_KEY" :container-el="container" />
|
||||
</a-layout-content>
|
||||
@ -491,6 +466,7 @@ const handleGlobalKeydown = (event: KeyboardEvent) => {
|
||||
:menu-type="contextMenuState.menuType"
|
||||
:storage-locations="contextMenuState.storageLocations"
|
||||
:robot-info="contextMenuState.robotInfo"
|
||||
:editor="editor"
|
||||
@close="handleCloseContextMenu"
|
||||
@action-complete="handleActionComplete"
|
||||
/>
|
||||
|
@ -30,92 +30,9 @@ export function parsePenData(penData: Record<string, unknown>): ParsedEventData
|
||||
y: eventInfo?.clientY || 0
|
||||
};
|
||||
|
||||
// 如果有pen数据,优先使用pen信息进行解析
|
||||
// 如果有pen数据,使用统一的解析逻辑
|
||||
if (pen) {
|
||||
console.log('解析pen数据:', pen);
|
||||
const { id, name, tags = [], storageLocation } = pen as {
|
||||
id?: string;
|
||||
name?: string;
|
||||
tags?: string[];
|
||||
storageLocation?: {
|
||||
pointId: string;
|
||||
locationName: string;
|
||||
index: number;
|
||||
occupied: boolean;
|
||||
locked: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
console.log('解析后的数据:', { id, name, tags, storageLocation });
|
||||
|
||||
// 根据tags判断类型 - 统一处理库位相关区域
|
||||
if (tags.includes('storage-background') || tags.includes('storage-location')) {
|
||||
const isBackground = tags.includes('storage-background');
|
||||
|
||||
console.log(`识别为库位相关类型: ${isBackground ? 'storage-background' : 'storage-location'}`);
|
||||
|
||||
// 库位背景区域或单个库位区域 - 都查找该点关联的所有库位
|
||||
const pointId = tags.find((tag: string) => tag.startsWith('point-'))?.replace('point-', '');
|
||||
return {
|
||||
type: 'storage-background', // 统一使用storage-background类型
|
||||
id,
|
||||
name,
|
||||
pointId,
|
||||
storageId: id,
|
||||
tags,
|
||||
target: document.elementFromPoint(position.x, position.y) as HTMLElement,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
|
||||
if (tags.includes('point')) {
|
||||
// 点区域
|
||||
return {
|
||||
type: 'point',
|
||||
id,
|
||||
name,
|
||||
pointId: id,
|
||||
tags,
|
||||
target: document.elementFromPoint(position.x, position.y) as HTMLElement,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
|
||||
if (tags.includes('robot')) {
|
||||
// 机器人区域
|
||||
// 机器人的真实名称存储在text字段中,而不是name字段
|
||||
const robotName = (pen?.text as string) || name || `机器人-${id}`;
|
||||
console.log('解析pen数据中的机器人信息:', {
|
||||
id,
|
||||
originalName: name,
|
||||
penText: pen?.text,
|
||||
finalName: robotName
|
||||
});
|
||||
return {
|
||||
type: 'robot',
|
||||
id,
|
||||
name: robotName,
|
||||
tags,
|
||||
target: document.elementFromPoint(position.x, position.y) as HTMLElement,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
|
||||
if (tags.includes('area')) {
|
||||
// 区域
|
||||
return {
|
||||
type: 'area',
|
||||
id,
|
||||
name,
|
||||
tags,
|
||||
target: document.elementFromPoint(position.x, position.y) as HTMLElement,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
return parsePenObject(pen, position);
|
||||
}
|
||||
|
||||
// 默认情况
|
||||
@ -127,6 +44,110 @@ export function parsePenData(penData: Record<string, unknown>): ParsedEventData
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一的pen对象解析逻辑
|
||||
* @param pen pen对象数据
|
||||
* @param position 位置信息
|
||||
* @returns 解析后的事件数据
|
||||
*/
|
||||
function parsePenObject(pen: Record<string, unknown>, position: { x: number; y: number }): ParsedEventData {
|
||||
console.log('解析pen数据:', pen);
|
||||
const { id, name, tags = [], storageLocation } = pen as {
|
||||
id?: string;
|
||||
name?: string;
|
||||
tags?: string[];
|
||||
storageLocation?: {
|
||||
pointId: string;
|
||||
locationName: string;
|
||||
index: number;
|
||||
occupied: boolean;
|
||||
locked: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
console.log('解析后的数据:', { id, name, tags, storageLocation });
|
||||
|
||||
const target = document.elementFromPoint(position.x, position.y) as HTMLElement;
|
||||
|
||||
// 根据tags判断类型 - 统一处理库位相关区域
|
||||
if (tags.includes('storage-background') || tags.includes('storage-location')) {
|
||||
const isBackground = tags.includes('storage-background');
|
||||
|
||||
console.log(`识别为库位相关类型: ${isBackground ? 'storage-background' : 'storage-location'}`);
|
||||
|
||||
// 库位背景区域或单个库位区域 - 都查找该点关联的所有库位
|
||||
const pointId = tags.find((tag: string) => tag.startsWith('point-'))?.replace('point-', '');
|
||||
return {
|
||||
type: 'storage-background', // 统一使用storage-background类型
|
||||
id,
|
||||
name: isBackground ? name : (storageLocation?.locationName || name),
|
||||
pointId,
|
||||
storageId: id,
|
||||
tags,
|
||||
target,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
|
||||
if (tags.includes('point')) {
|
||||
// 点区域
|
||||
return {
|
||||
type: 'point',
|
||||
id,
|
||||
name,
|
||||
pointId: id,
|
||||
tags,
|
||||
target,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
|
||||
if (tags.includes('robot')) {
|
||||
// 机器人区域
|
||||
// 机器人的真实名称存储在text字段中,而不是name字段
|
||||
const robotName = (pen?.text as string) || name || `机器人-${id}`;
|
||||
console.log('解析pen数据中的机器人信息:', {
|
||||
id,
|
||||
originalName: name,
|
||||
penText: pen?.text,
|
||||
finalName: robotName
|
||||
});
|
||||
return {
|
||||
type: 'robot',
|
||||
id,
|
||||
name: robotName,
|
||||
tags,
|
||||
target,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
|
||||
if (tags.includes('area')) {
|
||||
// 区域
|
||||
return {
|
||||
type: 'area',
|
||||
id,
|
||||
name,
|
||||
tags,
|
||||
target,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
|
||||
// 默认情况
|
||||
console.log('未识别到特定类型,使用默认类型');
|
||||
return {
|
||||
type: 'default',
|
||||
target,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析事件数据 - 纯函数,无副作用
|
||||
* @param event 鼠标事件或指针事件
|
||||
@ -138,105 +159,9 @@ export function parseEventData(event: MouseEvent | PointerEvent): ParsedEventDat
|
||||
// 从事件对象中获取pen数据(如果存在)
|
||||
const pen = (event as MouseEvent & { pen?: Record<string, unknown> }).pen;
|
||||
|
||||
// 如果有pen数据,优先使用pen信息进行解析
|
||||
// 如果有pen数据,使用统一的解析逻辑
|
||||
if (pen) {
|
||||
console.log('解析pen数据:', pen);
|
||||
const { id, name, tags = [], storageLocation } = pen as {
|
||||
id?: string;
|
||||
name?: string;
|
||||
tags?: string[];
|
||||
storageLocation?: {
|
||||
pointId: string;
|
||||
locationName: string;
|
||||
index: number;
|
||||
occupied: boolean;
|
||||
locked: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
console.log('解析后的数据:', { id, name, tags, storageLocation });
|
||||
// 根据tags判断类型
|
||||
if (tags.includes('storage-background')) {
|
||||
console.log('识别为storage-background类型');
|
||||
// 库位背景区域
|
||||
const pointId = tags.find((tag: string) => tag.startsWith('point-'))?.replace('point-', '');
|
||||
return {
|
||||
type: 'storage-background',
|
||||
id,
|
||||
name,
|
||||
pointId,
|
||||
storageId: id,
|
||||
tags,
|
||||
target,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
|
||||
if (tags.includes('storage-location')) {
|
||||
console.log('识别为storage-location类型');
|
||||
// 库位区域 - 使用storageLocation中的详细信息
|
||||
const pointId = tags.find((tag: string) => tag.startsWith('point-'))?.replace('point-', '');
|
||||
return {
|
||||
type: 'storage',
|
||||
id,
|
||||
name: storageLocation?.locationName || name,
|
||||
pointId,
|
||||
storageId: id,
|
||||
tags,
|
||||
target,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
|
||||
if (tags.includes('point')) {
|
||||
// 点区域
|
||||
return {
|
||||
type: 'point',
|
||||
id,
|
||||
name,
|
||||
pointId: id,
|
||||
tags,
|
||||
target,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
|
||||
if (tags.includes('robot')) {
|
||||
// 机器人区域
|
||||
// 机器人的真实名称存储在text字段中,而不是name字段
|
||||
const robotName = (pen?.text as string) || name || `机器人-${id}`;
|
||||
console.log('解析机器人信息:', {
|
||||
id,
|
||||
originalName: name,
|
||||
penText: pen?.text,
|
||||
finalName: robotName
|
||||
});
|
||||
return {
|
||||
type: 'robot',
|
||||
id,
|
||||
name: robotName,
|
||||
tags,
|
||||
target,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
|
||||
if (tags.includes('area')) {
|
||||
// 区域
|
||||
return {
|
||||
type: 'area',
|
||||
id,
|
||||
name,
|
||||
tags,
|
||||
target,
|
||||
position,
|
||||
pen,
|
||||
};
|
||||
}
|
||||
return parsePenObject(pen, position);
|
||||
}
|
||||
|
||||
// 回退到DOM元素检查
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
import type { ParsedEventData } from './event-parser';
|
||||
import { parseEventData, parsePenData } from './event-parser';
|
||||
import { getRobotMenuConfig, type RobotMenuConfig } from './robot-menu.service';
|
||||
import type { RobotMenuConfig } from './robot-menu.service';
|
||||
import { getStorageMenuConfig, type StorageMenuConfig } from './storage-menu.service';
|
||||
|
||||
export interface PointMenuConfig {
|
||||
@ -51,9 +51,19 @@ export function getMenuConfig(
|
||||
// 库位相关类型,包括点区域(如果是动作点且有库位信息)
|
||||
return getStorageMenuConfig(type, data, services?.storageLocationService);
|
||||
|
||||
case 'robot':
|
||||
// 机器人类型
|
||||
return getRobotMenuConfig(data, services?.robotService);
|
||||
case 'robot': {
|
||||
// 机器人类型 - 直接获取机器人信息
|
||||
const robotInfo = services?.robotService?.getRobotById?.(data.id);
|
||||
if (robotInfo) {
|
||||
return {
|
||||
menuType: 'robot' as const,
|
||||
robotInfo,
|
||||
} as RobotMenuConfig;
|
||||
}
|
||||
return {
|
||||
menuType: 'default' as const,
|
||||
};
|
||||
}
|
||||
|
||||
case 'area':
|
||||
// 区域类型
|
||||
@ -90,6 +100,36 @@ function getAreaMenuConfig(data: ParsedEventData): AreaMenuConfig {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理右键菜单的核心逻辑 - 统一处理函数
|
||||
* @param parsedData 解析后的事件数据
|
||||
* @param manager 状态管理器
|
||||
* @param services 服务实例集合(可选)
|
||||
*/
|
||||
function processContextMenu(
|
||||
parsedData: ParsedEventData,
|
||||
manager: any,
|
||||
services?: {
|
||||
storageLocationService?: any;
|
||||
robotService?: any;
|
||||
}
|
||||
) {
|
||||
// 获取菜单配置
|
||||
const menuConfig = getMenuConfig(parsedData.type, parsedData, services);
|
||||
|
||||
// 更新状态
|
||||
manager.setState({
|
||||
visible: true,
|
||||
x: parsedData.position.x,
|
||||
y: parsedData.position.y,
|
||||
eventData: parsedData,
|
||||
isRightClickActive: true, // 标记右键菜单正在显示
|
||||
...menuConfig, // 展开具体配置
|
||||
});
|
||||
|
||||
console.log('右键菜单事件数据:', parsedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理右键事件 - 组合函数
|
||||
* @param event 鼠标事件或指针事件
|
||||
@ -108,23 +148,9 @@ export function handleContextMenu(
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// 1. 解析事件数据
|
||||
// 解析事件数据并处理
|
||||
const parsedData = parseEventData(event);
|
||||
|
||||
// 2. 获取菜单配置
|
||||
const menuConfig = getMenuConfig(parsedData.type, parsedData, services);
|
||||
|
||||
// 3. 更新状态
|
||||
manager.setState({
|
||||
visible: true,
|
||||
x: parsedData.position.x,
|
||||
y: parsedData.position.y,
|
||||
eventData: parsedData,
|
||||
isRightClickActive: true, // 标记右键菜单正在显示
|
||||
...menuConfig, // 展开具体配置
|
||||
});
|
||||
|
||||
console.log('右键菜单事件数据:', parsedData);
|
||||
processContextMenu(parsedData, manager, services);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,21 +167,7 @@ export function handleContextMenuFromPenData(
|
||||
robotService?: any;
|
||||
}
|
||||
) {
|
||||
// 1. 解析penData
|
||||
// 解析penData并处理
|
||||
const parsedData = parsePenData(penData);
|
||||
|
||||
// 2. 获取菜单配置
|
||||
const menuConfig = getMenuConfig(parsedData.type, parsedData, services);
|
||||
|
||||
// 3. 更新状态
|
||||
manager.setState({
|
||||
visible: true,
|
||||
x: parsedData.position.x,
|
||||
y: parsedData.position.y,
|
||||
eventData: parsedData,
|
||||
isRightClickActive: true, // 标记右键菜单正在显示
|
||||
...menuConfig, // 展开具体配置
|
||||
});
|
||||
|
||||
console.log('右键菜单事件数据:', parsedData);
|
||||
processContextMenu(parsedData, manager, services);
|
||||
}
|
||||
|
@ -3,26 +3,16 @@
|
||||
* 处理机器人相关的菜单逻辑和操作
|
||||
*/
|
||||
|
||||
import type { ParsedEventData } from './event-parser';
|
||||
|
||||
export interface RobotInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
status: 'online' | 'offline' | 'busy' | 'idle' | 'error';
|
||||
batteryLevel?: number;
|
||||
currentTask?: string;
|
||||
position?: { x: number; y: number };
|
||||
canAcceptOrders?: boolean; // 是否可接单
|
||||
isControlled?: boolean; // 是否被控制
|
||||
isPaused?: boolean; // 是否暂停
|
||||
}
|
||||
import * as AmrApi from '../../apis/amr';
|
||||
import type { RobotInfo } from '../../apis/robot';
|
||||
import { RobotState } from '../../apis/robot';
|
||||
|
||||
export interface RobotMenuConfig {
|
||||
menuType: 'robot' | 'default';
|
||||
robotInfo?: RobotInfo;
|
||||
}
|
||||
|
||||
// 机器人操作类型
|
||||
// 机器人操作类型 - 只包含右键菜单实际需要的操作
|
||||
export type RobotAction =
|
||||
| 'seize_control' // 抢占控制权
|
||||
| 'enable_orders' // 可接单
|
||||
@ -39,101 +29,32 @@ export type RobotAction =
|
||||
| 'update' // 更新
|
||||
| 'custom_image'; // 自定义图片
|
||||
|
||||
/**
|
||||
* 获取机器人信息
|
||||
* @param data 事件数据
|
||||
* @param robotService 机器人服务实例(可选)
|
||||
* @returns 机器人信息
|
||||
*/
|
||||
export function getRobotInfo(data: ParsedEventData, robotService?: any): RobotInfo | undefined {
|
||||
const robotId = data.id;
|
||||
|
||||
if (!robotId) {
|
||||
console.warn('无法获取机器人ID');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// 如果提供了机器人服务,尝试使用它获取真实数据
|
||||
if (robotService && typeof robotService.getRobotById === 'function') {
|
||||
try {
|
||||
const robot = robotService.getRobotById(robotId);
|
||||
if (robot) {
|
||||
console.log('从RobotService获取到机器人信息:', robot);
|
||||
return {
|
||||
id: robot.id || robotId,
|
||||
name: robot.name || robot.robotName || '未知机器人',
|
||||
status: robot.status || 'offline',
|
||||
batteryLevel: robot.batteryLevel || robot.battery,
|
||||
currentTask: robot.currentTask || robot.task,
|
||||
position: robot.position || { x: 0, y: 0 },
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取机器人信息失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 回退到模拟数据
|
||||
console.log('使用模拟机器人数据');
|
||||
return generateMockRobotData(robotId, data.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取机器人菜单配置 - 纯函数
|
||||
* @param data 事件数据
|
||||
* @param robotService 机器人服务实例(可选)
|
||||
* @returns 菜单配置
|
||||
*/
|
||||
export function getRobotMenuConfig(data: ParsedEventData, robotService?: any): RobotMenuConfig {
|
||||
console.log(`处理机器人类型,机器人ID: ${data.id}`);
|
||||
|
||||
const robotInfo = getRobotInfo(data, robotService);
|
||||
|
||||
if (robotInfo) {
|
||||
console.log('找到机器人信息:', robotInfo);
|
||||
return {
|
||||
menuType: 'robot' as const,
|
||||
robotInfo,
|
||||
};
|
||||
} else {
|
||||
console.log('未找到机器人信息,显示默认菜单');
|
||||
return {
|
||||
menuType: 'default' as const,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行机器人操作
|
||||
* @param action 操作类型
|
||||
* @param robotInfo 机器人信息
|
||||
* @param robotService 机器人服务实例(可选)
|
||||
* @returns 操作结果
|
||||
*/
|
||||
export async function executeRobotAction(
|
||||
action: RobotAction,
|
||||
robotInfo: RobotInfo,
|
||||
robotService?: any
|
||||
robotInfo: RobotInfo
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
console.log(`执行机器人操作: ${action}`, robotInfo);
|
||||
|
||||
// 如果提供了机器人服务,使用真实API
|
||||
if (robotService && typeof robotService.executeAction === 'function') {
|
||||
const result = await robotService.executeAction(action, robotInfo.id);
|
||||
// 处理自定义图片操作
|
||||
if (action === 'custom_image') {
|
||||
return {
|
||||
success: result.success || true,
|
||||
message: result.message || `${action}操作成功`,
|
||||
success: true,
|
||||
message: '打开自定义图片设置',
|
||||
};
|
||||
}
|
||||
|
||||
// 模拟操作
|
||||
await new Promise(resolve => setTimeout(resolve, 500)); // 模拟网络延迟
|
||||
// 使用AMR API执行操作
|
||||
const result = await executeAmrAction(action, robotInfo);
|
||||
return result;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `机器人${robotInfo.name}的${action}操作成功`,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`机器人${action}操作失败:`, error);
|
||||
return {
|
||||
@ -143,12 +64,141 @@ export async function executeRobotAction(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行AMR操作 - 只对接右键菜单实际需要的API
|
||||
* @param action 操作类型
|
||||
* @param robotInfo 机器人信息
|
||||
* @returns 操作结果
|
||||
*/
|
||||
async function executeAmrAction(
|
||||
action: RobotAction,
|
||||
robotInfo: RobotInfo
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
let result: boolean = false;
|
||||
|
||||
switch (action) {
|
||||
case 'seize_control':
|
||||
// 抢占控制权 - 有对应API,需要IP地址
|
||||
if (!robotInfo.ip) {
|
||||
console.error('抢占控制权失败: 机器人IP地址未提供');
|
||||
return {
|
||||
success: false,
|
||||
message: '抢占控制权失败: 机器人IP地址未提供',
|
||||
};
|
||||
}
|
||||
result = await AmrApi.controlAmrTakeControl(robotInfo.ip);
|
||||
break;
|
||||
|
||||
case 'enable_orders':
|
||||
// 可接单 - 有对应API
|
||||
result = await AmrApi.setAvailable(robotInfo.id);
|
||||
break;
|
||||
|
||||
case 'disable_orders':
|
||||
// 不可接单 - 有对应API
|
||||
result = await AmrApi.setUnavailable(robotInfo.id);
|
||||
break;
|
||||
|
||||
case 'pause':
|
||||
// 暂停 - 有对应API
|
||||
result = await AmrApi.controlAmr(robotInfo.id, 'pause');
|
||||
break;
|
||||
|
||||
case 'resume':
|
||||
// 继续 - 有对应API
|
||||
result = await AmrApi.controlAmr(robotInfo.id, 'resume');
|
||||
break;
|
||||
|
||||
case 'start':
|
||||
// 启动 - 有对应API
|
||||
result = await AmrApi.controlAmr(robotInfo.id, 'start');
|
||||
break;
|
||||
|
||||
case 'stop':
|
||||
// 停止 - 有对应API
|
||||
result = await AmrApi.controlAmr(robotInfo.id, 'stop');
|
||||
break;
|
||||
|
||||
case 'go_charge':
|
||||
// 前往充电 - 有对应API
|
||||
result = await AmrApi.controlAmr(robotInfo.id, 'go_charge');
|
||||
break;
|
||||
|
||||
case 'go_dock':
|
||||
// 前往停靠 - 有对应API
|
||||
result = await AmrApi.controlAmr(robotInfo.id, 'go_dock');
|
||||
break;
|
||||
|
||||
case 'navigate':
|
||||
// 路径导航 - 有对应API
|
||||
result = await AmrApi.controlAmr(robotInfo.id, 'navigate');
|
||||
break;
|
||||
|
||||
case 'reset':
|
||||
// 重置 - 有对应API
|
||||
result = await AmrApi.controlAmr(robotInfo.id, 'reset');
|
||||
break;
|
||||
|
||||
case 'diagnose':
|
||||
// 诊断 - 有对应API
|
||||
result = await AmrApi.controlAmr(robotInfo.id, 'diagnose');
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
// 更新 - 有对应API
|
||||
result = await AmrApi.controlAmr(robotInfo.id, 'update');
|
||||
break;
|
||||
|
||||
case 'custom_image':
|
||||
// 自定义图片 - 没有对应API,保持原有逻辑
|
||||
console.log('自定义图片功能:打开图片设置模态框');
|
||||
return {
|
||||
success: true,
|
||||
message: '打开自定义图片设置',
|
||||
};
|
||||
|
||||
default:
|
||||
// 其他操作 - 没有对应API,保持原有打印信息逻辑
|
||||
console.log(`执行机器人操作: ${action} (${robotInfo.id}) - 仅打印信息`);
|
||||
return {
|
||||
success: true,
|
||||
message: `${action}操作(仅打印信息)`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: result,
|
||||
message: result ? `${action}操作成功` : `${action}操作失败`,
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error(`AMR操作失败 (${action}):`, error);
|
||||
return {
|
||||
success: false,
|
||||
message: `操作失败: ${error}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取机器人状态显示文本
|
||||
* @param status 机器人状态
|
||||
* @returns 状态显示文本
|
||||
*/
|
||||
export function getRobotStatusText(status: string): string {
|
||||
export function getRobotStatusText(status: string | number): string {
|
||||
// 处理RobotState枚举值
|
||||
if (typeof status === 'number') {
|
||||
const statusMap: Record<number, string> = {
|
||||
[RobotState.任务执行中]: '任务执行中',
|
||||
[RobotState.充电中]: '充电中',
|
||||
[RobotState.停靠中]: '停靠中',
|
||||
[RobotState.空闲中]: '空闲中',
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
}
|
||||
|
||||
// 处理字符串状态(兼容旧格式)
|
||||
const statusMap: Record<string, string> = {
|
||||
'online': '在线',
|
||||
'offline': '离线',
|
||||
@ -165,7 +215,19 @@ export function getRobotStatusText(status: string): string {
|
||||
* @param status 机器人状态
|
||||
* @returns 状态颜色
|
||||
*/
|
||||
export function getRobotStatusColor(status: string): string {
|
||||
export function getRobotStatusColor(status: string | number): string {
|
||||
// 处理RobotState枚举值
|
||||
if (typeof status === 'number') {
|
||||
const colorMap: Record<number, string> = {
|
||||
[RobotState.任务执行中]: '#1890ff', // 蓝色 - 忙碌
|
||||
[RobotState.充电中]: '#faad14', // 橙色 - 充电
|
||||
[RobotState.停靠中]: '#52c41a', // 绿色 - 正常
|
||||
[RobotState.空闲中]: '#52c41a', // 绿色 - 正常
|
||||
};
|
||||
return colorMap[status] || '#d9d9d9';
|
||||
}
|
||||
|
||||
// 处理字符串状态(兼容旧格式)
|
||||
const colorMap: Record<string, string> = {
|
||||
'online': '#52c41a',
|
||||
'offline': '#d9d9d9',
|
||||
@ -177,35 +239,6 @@ export function getRobotStatusColor(status: string): string {
|
||||
return colorMap[status] || '#d9d9d9';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成模拟机器人数据
|
||||
* @param robotId 机器人ID
|
||||
* @param robotName 机器人名称
|
||||
* @returns 模拟的机器人信息
|
||||
*/
|
||||
export function generateMockRobotData(robotId: string, robotName?: string): RobotInfo {
|
||||
const statuses: Array<'online' | 'offline' | 'busy' | 'idle' | 'error'> = ['online', 'busy', 'idle', 'offline'];
|
||||
const tasks = ['空闲', '运输中', '充电中', '维护中', '等待指令'];
|
||||
|
||||
const randomStatus = statuses[Math.floor(Math.random() * statuses.length)];
|
||||
const randomTask = tasks[Math.floor(Math.random() * tasks.length)];
|
||||
const batteryLevel = Math.floor(Math.random() * 100) + 1;
|
||||
|
||||
return {
|
||||
id: robotId,
|
||||
name: robotName || `机器人-${robotId}`,
|
||||
status: randomStatus,
|
||||
batteryLevel,
|
||||
currentTask: randomTask,
|
||||
position: {
|
||||
x: Math.floor(Math.random() * 1000),
|
||||
y: Math.floor(Math.random() * 1000)
|
||||
},
|
||||
canAcceptOrders: Math.random() > 0.3, // 70%概率可接单
|
||||
isControlled: Math.random() > 0.7, // 30%概率被控制
|
||||
isPaused: Math.random() > 0.8, // 20%概率暂停
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取机器人操作显示文本
|
||||
@ -227,6 +260,7 @@ export function getRobotActionText(action: RobotAction): string {
|
||||
'reset': '重置',
|
||||
'diagnose': '诊断',
|
||||
'update': '更新',
|
||||
'custom_image': '自定义图片',
|
||||
};
|
||||
|
||||
return actionMap[action] || action;
|
||||
@ -252,6 +286,7 @@ export function getRobotActionIcon(action: RobotAction): string {
|
||||
'reset': '🔄',
|
||||
'diagnose': '🔧',
|
||||
'update': '📱',
|
||||
'custom_image': '🖼️',
|
||||
};
|
||||
|
||||
return iconMap[action] || '⚙️';
|
||||
|
Loading…
x
Reference in New Issue
Block a user