feat: 更新机器人操作菜单,新增控制权限和运行控制项,优化操作文本和图标,增强机器人状态管理功能

This commit is contained in:
xudan 2025-09-09 17:12:30 +08:00
parent d8622aafbe
commit db42f4a6d8
4 changed files with 170 additions and 44 deletions

View File

@ -35,7 +35,31 @@
<!-- 机器人操作菜单 -->
<div class="robot-actions">
<div class="action-group">
<div class="action-group-title">基本操作</div>
<div class="action-group-title">控制权限</div>
<div class="action-item" @click="handleRobotAction('seize_control', '抢占控制权')">
<span class="action-icon">🎮</span>
<span>抢占控制权</span>
</div>
<div class="action-item" @click="handleRobotAction('enable_orders', '可接单')">
<span class="action-icon"></span>
<span>可接单</span>
</div>
<div class="action-item" @click="handleRobotAction('disable_orders', '不可接单')">
<span class="action-icon"></span>
<span>不可接单</span>
</div>
</div>
<div class="action-group">
<div class="action-group-title">运行控制</div>
<div class="action-item" @click="handleRobotAction('pause', '暂停')">
<span class="action-icon"></span>
<span>暂停</span>
</div>
<div class="action-item" @click="handleRobotAction('resume', '继续')">
<span class="action-icon"></span>
<span>继续</span>
</div>
<div class="action-item" @click="handleRobotAction('start', '启动')">
<span class="action-icon">🚀</span>
<span>启动</span>
@ -44,34 +68,26 @@
<span class="action-icon"></span>
<span>停止</span>
</div>
<div class="action-item" @click="handleRobotAction('pause', '暂停')">
<span class="action-icon"></span>
<span>暂停</span>
</div>
<div class="action-item" @click="handleRobotAction('resume', '恢复')">
<span class="action-icon"></span>
<span>恢复</span>
</div>
</div>
<div class="action-group">
<div class="action-group-title">导航控制</div>
<div class="action-item" @click="handleRobotAction('go_home', '回原点')">
<span class="action-icon">🏠</span>
<span>回原点</span>
</div>
<div class="action-item" @click="handleRobotAction('charge', '充电')">
<div class="action-item" @click="handleRobotAction('go_charge', '前往充电')">
<span class="action-icon">🔋</span>
<span>充电</span>
<span>前往充电</span>
</div>
<div class="action-item" @click="handleRobotAction('emergency_stop', '紧急停止')">
<span class="action-icon">🛑</span>
<span>紧急停止</span>
<div class="action-item" @click="handleRobotAction('go_dock', '前往停靠')">
<span class="action-icon">🏠</span>
<span>前往停靠</span>
</div>
<div class="action-item" @click="handleRobotAction('navigate', '路径导航')">
<span class="action-icon">🧭</span>
<span>路径导航</span>
</div>
</div>
<div class="action-group">
<div class="action-group-title">状态管理</div>
<div class="action-group-title">系统管理</div>
<div class="action-item" @click="handleRobotAction('reset', '重置')">
<span class="action-icon">🔄</span>
<span>重置</span>
@ -91,15 +107,21 @@
<script setup lang="ts">
import { computed } from 'vue';
import type { RobotInfo } from '../../services/context-menu';
import { getRobotStatusText, getRobotStatusColor } from '../../services/context-menu';
import type { RobotAction,RobotInfo } from '../../services/context-menu/robot-menu.service';
import {
executeRobotAction,
getRobotActionIcon,
getRobotActionText,
getRobotStatusColor,
getRobotStatusText} from '../../services/context-menu/robot-menu.service';
interface Props {
robotInfo?: RobotInfo;
}
interface Emits {
(e: 'actionComplete', data: { action: string; robot: RobotInfo; success: boolean }): void;
(e: 'actionComplete', data: { action: RobotAction; robot: RobotInfo; success: boolean }): void;
}
const props = defineProps<Props>();
@ -128,20 +150,22 @@ const getBatteryClass = (batteryLevel: number) => {
};
//
const handleRobotAction = async (action: string, actionName: string) => {
const handleRobotAction = async (action: RobotAction, actionName: string) => {
if (!props.robotInfo) return;
try {
console.log(`执行机器人操作: ${action}`, props.robotInfo);
console.log(`执行机器人操作: ${action} (${actionName})`, props.robotInfo);
//
await new Promise(resolve => setTimeout(resolve, 500));
// 使
const result = await executeRobotAction(action, props.robotInfo);
console.log('机器人操作结果:', result);
//
emit('actionComplete', {
action,
robot: props.robotInfo,
success: true
success: result.success
});
} catch (error) {
console.error(`机器人${actionName}操作失败:`, error);

View File

@ -12,6 +12,9 @@ export interface RobotInfo {
batteryLevel?: number;
currentTask?: string;
position?: { x: number; y: number };
canAcceptOrders?: boolean; // 是否可接单
isControlled?: boolean; // 是否被控制
isPaused?: boolean; // 是否暂停
}
export interface RobotMenuConfig {
@ -19,6 +22,22 @@ export interface RobotMenuConfig {
robotInfo?: RobotInfo;
}
// 机器人操作类型
export type RobotAction =
| 'seize_control' // 抢占控制权
| 'enable_orders' // 可接单
| 'disable_orders' // 不可接单
| 'pause' // 暂停
| 'resume' // 继续
| 'go_charge' // 前往充电
| 'go_dock' // 前往停靠
| 'navigate' // 路径导航
| 'start' // 启动
| 'stop' // 停止
| 'reset' // 重置
| 'diagnose' // 诊断
| 'update'; // 更新
/**
*
* @param data
@ -55,14 +74,7 @@ export function getRobotInfo(data: ParsedEventData, robotService?: any): RobotIn
// 回退到模拟数据
console.log('使用模拟机器人数据');
return {
id: robotId,
name: data.name || `机器人-${robotId}`,
status: 'online',
batteryLevel: 85,
currentTask: '空闲',
position: { x: 0, y: 0 },
};
return generateMockRobotData(robotId, data.name);
}
/**
@ -98,7 +110,7 @@ export function getRobotMenuConfig(data: ParsedEventData, robotService?: any): R
* @returns
*/
export async function executeRobotAction(
action: string,
action: RobotAction,
robotInfo: RobotInfo,
robotService?: any
): Promise<{ success: boolean; message: string }> {
@ -163,3 +175,83 @@ 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%概率暂停
};
}
/**
*
* @param action
* @returns
*/
export function getRobotActionText(action: RobotAction): string {
const actionMap: Record<RobotAction, string> = {
'seize_control': '抢占控制权',
'enable_orders': '可接单',
'disable_orders': '不可接单',
'pause': '暂停',
'resume': '继续',
'go_charge': '前往充电',
'go_dock': '前往停靠',
'navigate': '路径导航',
'start': '启动',
'stop': '停止',
'reset': '重置',
'diagnose': '诊断',
'update': '更新',
};
return actionMap[action] || action;
}
/**
*
* @param action
* @returns
*/
export function getRobotActionIcon(action: RobotAction): string {
const iconMap: Record<RobotAction, string> = {
'seize_control': '🎮',
'enable_orders': '✅',
'disable_orders': '❌',
'pause': '⏸️',
'resume': '▶️',
'go_charge': '🔋',
'go_dock': '🏠',
'navigate': '🧭',
'start': '🚀',
'stop': '⏹️',
'reset': '🔄',
'diagnose': '🔧',
'update': '📱',
};
return iconMap[action] || '⚙️';
}

View File

@ -1125,6 +1125,9 @@ export class EditorService extends Meta2d {
await this.addPen(pen, false, true, true);
}),
);
// 机器人初始化完成后,确保层级正确(机器人应在最顶层)
this.#ensureCorrectLayerOrder();
}
public refreshRobot(id: RobotInfo['id'], info: Partial<RobotRealtimeInfo>): void {
@ -1148,6 +1151,9 @@ export class EditorService extends Meta2d {
{ render: true, history: false, doEvent: false },
);
}
// 机器人位置更新后,确保层级正确(机器人应在最顶层)
this.#ensureCorrectLayerOrder();
}
/**
@ -1175,7 +1181,7 @@ export class EditorService extends Meta2d {
#mapRobotImage(
type: RobotType,
active?: boolean,
): Required<Pick<MapPen, 'image' | 'iconWidth' | 'iconHeight' | 'iconTop'>> {
): Required<Pick<MapPen, 'image' | 'iconWidth' | 'iconHeight' | 'iconTop' | 'canvasLayer'>> {
const theme = this.data().theme;
const image =
import.meta.env.BASE_URL + (active ? `/robot/${type}-active-${theme}.png` : `/robot/${type}-${theme}.png`);
@ -1189,7 +1195,7 @@ export class EditorService extends Meta2d {
// 使用优化的像素对齐算法,确保小车和光圈精确重合
const iconTop = this.#calculatePixelAlignedOffset(-16);
return { image, iconWidth, iconHeight, iconTop };
return { image, iconWidth, iconHeight, iconTop, canvasLayer: CanvasLayer.CanvasImage };
}
//#endregion

View File

@ -49,10 +49,14 @@ export class LayerManagerService {
this.editor.top(storageLocations);
}
// 将点位和机器人都移到最上层(在路线之上)
const topElements = [...points, ...robots];
if (topElements.length > 0) {
this.editor.top(topElements);
// 将点位移到上层
if (points.length > 0) {
this.editor.top(points);
}
// 将机器人移到最上层(最高优先级)
if (robots.length > 0) {
this.editor.top(robots);
}
}
@ -64,7 +68,7 @@ export class LayerManagerService {
public movePointsToTop(points: MapPen[]): void {
if (points.length > 0) {
this.editor.top(points);
// 确保机器人仍然与点位在同一层级
// 确保机器人仍然在最高层级
this.ensureRobotsAtTop();
}
}
@ -95,7 +99,7 @@ export class LayerManagerService {
/**
*
*
*
*/
public ensureRobotsAtTop(): void {
const robots = this.editor.find('robot');