Compare commits
2 Commits
b237ba834e
...
02654b29e0
Author | SHA1 | Date | |
---|---|---|---|
02654b29e0 | |||
53075dbb39 |
@ -8,11 +8,7 @@
|
||||
@open-change="handleOpenChange"
|
||||
:z-index="998"
|
||||
>
|
||||
<div
|
||||
ref="triggerRef"
|
||||
class="context-menu-trigger"
|
||||
:style="triggerStyle"
|
||||
/>
|
||||
<div ref="triggerRef" class="context-menu-trigger" :style="triggerStyle" />
|
||||
<template #overlay>
|
||||
<div class="context-menu-overlay">
|
||||
<div class="context-menu-header">
|
||||
@ -41,10 +37,7 @@
|
||||
/>
|
||||
|
||||
<!-- 默认菜单 -->
|
||||
<DefaultMenu
|
||||
v-else
|
||||
@action-complete="handleActionComplete"
|
||||
/>
|
||||
<DefaultMenu v-else @action-complete="handleActionComplete" />
|
||||
</div>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
@ -130,7 +123,7 @@ const handleOpenChange = (open: boolean) => {
|
||||
// 处理操作完成事件
|
||||
const handleActionComplete = (data: any) => {
|
||||
console.log('菜单操作完成:', data);
|
||||
|
||||
|
||||
// 根据操作结果显示相应的提示消息
|
||||
if (data.success) {
|
||||
const actionName = getActionDisplayName(data.action);
|
||||
@ -139,7 +132,7 @@ const handleActionComplete = (data: any) => {
|
||||
const actionName = getActionDisplayName(data.action);
|
||||
message.error(`${actionName}操作失败`);
|
||||
}
|
||||
|
||||
|
||||
emit('actionComplete', data);
|
||||
// 所有操作都不关闭菜单,只有关闭按钮才能关闭
|
||||
};
|
||||
@ -152,37 +145,37 @@ const handleActionComplete = (data: any) => {
|
||||
const getActionDisplayName = (action: string): string => {
|
||||
const actionMap: Record<string, string> = {
|
||||
// 机器人操作
|
||||
'seize_control': '抢占控制权',
|
||||
'enable_orders': '可接单',
|
||||
'disable_orders': '不可接单',
|
||||
'pause': '暂停',
|
||||
'resume': '继续',
|
||||
'go_charge': '前往充电',
|
||||
'go_dock': '前往停靠',
|
||||
'navigate': '路径导航',
|
||||
'start': '启动',
|
||||
'stop': '停止',
|
||||
'reset': '重置',
|
||||
'diagnose': '诊断',
|
||||
'update': '更新',
|
||||
'custom_image': '自定义图片',
|
||||
|
||||
seize_control: '抢占控制权',
|
||||
enable_orders: '可接单',
|
||||
disable_orders: '不可接单',
|
||||
pause: '暂停',
|
||||
resume: '继续',
|
||||
go_charge: '前往充电',
|
||||
go_dock: '前往停靠',
|
||||
navigate: '路径导航',
|
||||
start: '启动',
|
||||
stop: '停止',
|
||||
reset: '重置',
|
||||
diagnose: '诊断',
|
||||
update: '更新',
|
||||
custom_image: '自定义图片',
|
||||
|
||||
// 库位操作
|
||||
'lock': '锁定',
|
||||
'unlock': '解锁',
|
||||
'disable': '禁用',
|
||||
'enable': '启用',
|
||||
'occupy': '占用',
|
||||
'release': '释放',
|
||||
'set_empty_tray': '设置空托盘',
|
||||
'clear_empty_tray': '清除空托盘',
|
||||
|
||||
lock: '锁定',
|
||||
unlock: '解锁',
|
||||
disable: '禁用',
|
||||
enable: '启用',
|
||||
occupy: '占用',
|
||||
release: '释放',
|
||||
set_empty_tray: '设置空托盘',
|
||||
clear_empty_tray: '清除空托盘',
|
||||
|
||||
// 默认操作
|
||||
'refresh': '刷新',
|
||||
'view_info': '查看信息',
|
||||
'settings': '设置',
|
||||
refresh: '刷新',
|
||||
view_info: '查看信息',
|
||||
settings: '设置',
|
||||
};
|
||||
|
||||
|
||||
return actionMap[action] || action;
|
||||
};
|
||||
|
||||
@ -299,5 +292,4 @@ const handleCloseMenu = () => {
|
||||
:root[theme='dark'] .close-icon {
|
||||
color: #ffffffd9;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -7,24 +7,24 @@
|
||||
:selected-robot-name="selectedRobotName"
|
||||
@save="handleImageSettingsSave"
|
||||
/>
|
||||
|
||||
|
||||
<div class="menu-container">
|
||||
<!-- 左侧:机器人信息区域 -->
|
||||
<div v-if="robotInfo" class="robot-info-section">
|
||||
<div class="robot-header">
|
||||
<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 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.battery !== undefined" class="detail-item">
|
||||
<span class="detail-label">电量:</span>
|
||||
<div class="battery-container">
|
||||
<div class="battery-bar">
|
||||
<div
|
||||
class="battery-fill"
|
||||
<div
|
||||
class="battery-fill"
|
||||
:style="{ width: `${robotInfo.battery}%` }"
|
||||
:class="getBatteryClass(robotInfo.battery)"
|
||||
></div>
|
||||
@ -32,7 +32,7 @@
|
||||
<span class="battery-text">{{ robotInfo.battery }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div v-if="robotInfo.targetPoint" class="detail-item">
|
||||
<span class="detail-label">目标点位:</span>
|
||||
<span class="detail-value">{{ robotInfo.targetPoint }}</span>
|
||||
@ -98,6 +98,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 视角控制 -->
|
||||
<div class="action-group">
|
||||
<div class="action-group-title">视角控制</div>
|
||||
<div v-if="!isFollowing" class="action-item" @click="handleRobotAction('follow_view', '视角跟随')">
|
||||
<span class="action-icon">👁️</span>
|
||||
<span>视角跟随</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="isFollowing"
|
||||
class="action-item follow-active"
|
||||
@click="handleRobotAction('stop_follow_view', '停止跟随')"
|
||||
>
|
||||
<span class="action-icon">👁️🗨️</span>
|
||||
<span>停止跟随</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 系统管理 -->
|
||||
<div class="action-group">
|
||||
<div class="action-group-title">系统管理</div>
|
||||
@ -126,14 +143,17 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { message } from 'ant-design-vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, onUnmounted, ref } from 'vue';
|
||||
|
||||
import type { RobotInfo } from '../../apis/robot';
|
||||
import type { RobotAction } from '../../services/context-menu/robot-menu.service';
|
||||
import {
|
||||
import {
|
||||
executeRobotAction,
|
||||
getGlobalFollowState,
|
||||
getRobotStatusColor,
|
||||
getRobotStatusText
|
||||
getRobotStatusText,
|
||||
startGlobalFollow,
|
||||
stopGlobalFollow,
|
||||
} from '../../services/context-menu/robot-menu.service';
|
||||
import { editorStore } from '../../stores/editor.store';
|
||||
import RobotImageSettingsModal from '../modal/robot-image-settings-modal.vue';
|
||||
@ -161,6 +181,11 @@ const robotInfo = computed<RobotInfo | null>(() => {
|
||||
const imageSettingsVisible = ref(false);
|
||||
const selectedRobotName = ref('');
|
||||
|
||||
// 视角跟随状态 - 使用全局状态
|
||||
const isFollowing = computed(() => {
|
||||
const globalState = getGlobalFollowState();
|
||||
return globalState.isFollowing;
|
||||
});
|
||||
|
||||
// 定义组件名称
|
||||
defineOptions({
|
||||
@ -176,51 +201,81 @@ const getBatteryClass = (batteryLevel: number) => {
|
||||
return 'battery-low';
|
||||
};
|
||||
|
||||
// 开始视角跟随
|
||||
const startFollowView = () => {
|
||||
if (!robotInfo.value || !editorStore.hasEditor()) return;
|
||||
|
||||
const editor = editorStore.getEditorValue();
|
||||
if (!editor) return;
|
||||
|
||||
// 使用全局跟随功能(内部会处理切换逻辑)
|
||||
startGlobalFollow(robotInfo.value.id, editor);
|
||||
message.success('开始视角跟随');
|
||||
};
|
||||
|
||||
// 停止视角跟随
|
||||
const stopFollowView = () => {
|
||||
// 使用全局停止功能
|
||||
stopGlobalFollow();
|
||||
message.success('停止视角跟随');
|
||||
};
|
||||
|
||||
// 处理机器人操作
|
||||
const handleRobotAction = async (action: RobotAction | 'custom_image', actionName: string) => {
|
||||
if (!robotInfo.value) return;
|
||||
|
||||
|
||||
// 处理自定义图片操作
|
||||
if (action === 'custom_image') {
|
||||
if (!robotInfo.value?.label) {
|
||||
message.error('未找到机器人信息');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
console.log('打开机器人图片设置:', robotInfo.value);
|
||||
|
||||
|
||||
// 打开模态框,不关闭右键菜单
|
||||
selectedRobotName.value = robotInfo.value.label;
|
||||
imageSettingsVisible.value = true;
|
||||
|
||||
|
||||
// 只触发自定义图片事件,不触发操作完成事件
|
||||
emit('customImage', {
|
||||
robotInfo: robotInfo.value
|
||||
robotInfo: robotInfo.value,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 处理视角跟随操作
|
||||
if (action === 'follow_view') {
|
||||
startFollowView();
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === 'stop_follow_view') {
|
||||
stopFollowView();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log(`执行机器人操作: ${action} (${actionName})`, robotInfo.value);
|
||||
|
||||
|
||||
// 使用服务函数执行操作
|
||||
const result = await executeRobotAction(action, robotInfo.value);
|
||||
|
||||
|
||||
console.log('机器人操作结果:', result);
|
||||
|
||||
|
||||
// 发送操作完成事件
|
||||
emit('actionComplete', {
|
||||
action,
|
||||
robot: robotInfo.value,
|
||||
success: result.success
|
||||
success: result.success,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`机器人${actionName}操作失败:`, error);
|
||||
|
||||
|
||||
emit('actionComplete', {
|
||||
action,
|
||||
robot: robotInfo.value,
|
||||
success: false
|
||||
success: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -229,10 +284,15 @@ const handleRobotAction = async (action: RobotAction | 'custom_image', actionNam
|
||||
const handleImageSettingsSave = (data: any) => {
|
||||
console.log('机器人图片设置保存:', data);
|
||||
message.success('机器人图片设置保存成功');
|
||||
|
||||
|
||||
// 可以在这里添加额外的保存后处理逻辑
|
||||
// 比如通知父组件更新机器人显示等
|
||||
};
|
||||
|
||||
// 组件卸载时不需要清理,因为使用全局状态管理
|
||||
onUnmounted(() => {
|
||||
// 全局跟随状态由服务层管理,组件卸载时不需要清理
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -355,7 +415,7 @@ const handleImageSettingsSave = (data: any) => {
|
||||
|
||||
.actions-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 8px;
|
||||
height: 100%;
|
||||
}
|
||||
@ -396,6 +456,16 @@ const handleImageSettingsSave = (data: any) => {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.action-item.follow-active {
|
||||
background-color: #e6f7ff;
|
||||
border: 1px solid #91d5ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.action-item.follow-active:hover {
|
||||
background-color: #bae7ff;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
font-size: 12px;
|
||||
width: 14px;
|
||||
@ -408,13 +478,13 @@ const handleImageSettingsSave = (data: any) => {
|
||||
.menu-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
.robot-info-section {
|
||||
width: 100%;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
|
||||
.actions-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
@ -3,10 +3,19 @@
|
||||
* 处理机器人相关的菜单逻辑和操作
|
||||
*/
|
||||
|
||||
import { reactive } from 'vue';
|
||||
|
||||
import * as AmrApi from '../../apis/amr';
|
||||
import type { RobotInfo } from '../../apis/robot';
|
||||
import { RobotState } from '../../apis/robot';
|
||||
|
||||
// 全局跟随状态管理 - 使用Vue响应式系统
|
||||
const globalFollowState = reactive({
|
||||
isFollowing: false,
|
||||
robotId: '',
|
||||
timer: null as NodeJS.Timeout | null,
|
||||
});
|
||||
|
||||
export interface RobotMenuConfig {
|
||||
menuType: 'robot' | 'default';
|
||||
robotInfo?: RobotInfo;
|
||||
@ -27,7 +36,9 @@ export type RobotAction =
|
||||
| 'reset' // 重置
|
||||
| 'diagnose' // 诊断
|
||||
| 'update' // 更新
|
||||
| 'custom_image'; // 自定义图片
|
||||
| 'custom_image' // 自定义图片
|
||||
| 'follow_view' // 视角跟随
|
||||
| 'stop_follow_view'; // 停止视角跟随
|
||||
|
||||
|
||||
/**
|
||||
@ -158,6 +169,22 @@ async function executeAmrAction(
|
||||
message: '打开自定义图片设置',
|
||||
};
|
||||
|
||||
case 'follow_view':
|
||||
// 视角跟随 - 前端功能,不需要API调用
|
||||
console.log('视角跟随功能:开始跟随机器人');
|
||||
return {
|
||||
success: true,
|
||||
message: '开始视角跟随',
|
||||
};
|
||||
|
||||
case 'stop_follow_view':
|
||||
// 停止视角跟随 - 前端功能,不需要API调用
|
||||
console.log('停止视角跟随功能');
|
||||
return {
|
||||
success: true,
|
||||
message: '停止视角跟随',
|
||||
};
|
||||
|
||||
default:
|
||||
// 其他操作 - 没有对应API,保持原有打印信息逻辑
|
||||
console.log(`执行机器人操作: ${action} (${robotInfo.id}) - 仅打印信息`);
|
||||
@ -261,6 +288,8 @@ export function getRobotActionText(action: RobotAction): string {
|
||||
'diagnose': '诊断',
|
||||
'update': '更新',
|
||||
'custom_image': '自定义图片',
|
||||
'follow_view': '视角跟随',
|
||||
'stop_follow_view': '停止跟随',
|
||||
};
|
||||
|
||||
return actionMap[action] || action;
|
||||
@ -287,6 +316,8 @@ export function getRobotActionIcon(action: RobotAction): string {
|
||||
'diagnose': '🔧',
|
||||
'update': '📱',
|
||||
'custom_image': '🖼️',
|
||||
'follow_view': '👁️',
|
||||
'stop_follow_view': '👁️🗨️',
|
||||
};
|
||||
|
||||
return iconMap[action] || '⚙️';
|
||||
@ -314,4 +345,53 @@ export function getRobotMenuConfig(robotInfo: RobotInfo): RobotMenuConfig {
|
||||
menuType: 'robot',
|
||||
robotInfo,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始全局视角跟随
|
||||
* @param robotId 机器人ID
|
||||
* @param editor 编辑器实例
|
||||
*/
|
||||
export function startGlobalFollow(robotId: string, editor: any): void {
|
||||
// 如果已经在跟随其他机器人,先停止
|
||||
if (globalFollowState.isFollowing && globalFollowState.robotId !== robotId) {
|
||||
stopGlobalFollow();
|
||||
}
|
||||
|
||||
globalFollowState.isFollowing = true;
|
||||
globalFollowState.robotId = robotId;
|
||||
|
||||
// 立即执行一次聚焦
|
||||
editor.gotoById(robotId);
|
||||
|
||||
// 设置定时器,每36毫秒执行一次(约27.8fps)
|
||||
globalFollowState.timer = setInterval(() => {
|
||||
if (globalFollowState.isFollowing) {
|
||||
editor.gotoById(robotId);
|
||||
}
|
||||
}, 10);
|
||||
|
||||
console.log('开始全局视角跟随:', robotId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止全局视角跟随
|
||||
*/
|
||||
export function stopGlobalFollow(): void {
|
||||
globalFollowState.isFollowing = false;
|
||||
globalFollowState.robotId = '';
|
||||
|
||||
if (globalFollowState.timer) {
|
||||
clearInterval(globalFollowState.timer);
|
||||
globalFollowState.timer = null;
|
||||
}
|
||||
|
||||
console.log('停止全局视角跟随');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全局跟随状态
|
||||
*/
|
||||
export function getGlobalFollowState() {
|
||||
return globalFollowState;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user