293 lines
5.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="point-menu">
<!-- 菜单头部显示站点信息 -->
<div class="menu-header">
<div class="point-info">
<div class="point-details">
<div class="point-name">{{ pointInfo?.name || '未知站点' }}</div>
<div class="point-id">ID: {{ pointInfo?.id }}</div>
</div>
</div>
</div>
<!-- 菜单分割线 -->
<a-divider style="margin: 6px 0;" />
<!-- 菜单选项 -->
<a-menu
:selectable="false"
class="menu-options"
>
<a-menu-item
key="navigate"
:disabled="!canNavigate"
@click="handleNavigateToPoint"
>
导航至此站点
</a-menu-item>
</a-menu>
<!-- 机器人选择弹窗 -->
<RobotSelectorModal
v-model:open="showRobotSelector"
:target-point-name="pointInfo?.name"
@confirm="handleRobotSelected"
@cancel="handleRobotSelectorCancel"
/>
</div>
</template>
<script setup lang="ts">
import { message } from 'ant-design-vue';
import { computed, ref } from 'vue';
import type { RobotInfo } from '../../apis/robot';
import { executeNavigateToPoint } from '../../services/context-menu/point-menu.service';
import RobotSelectorModal from '../modal/robot-selector-modal.vue';
interface Props {
menuType?: 'point' | 'default';
pointInfo?: {
id: string;
name: string;
type: string;
};
}
interface Emits {
(e: 'actionComplete', data: {
action: string;
point: any;
success: boolean;
message?: string;
}): void;
}
const props = defineProps<Props>();
const emit = defineEmits<Emits>();
// 响应式数据
const showRobotSelector = ref(false);
const loading = ref(false);
// 计算属性
const pointInfo = computed(() => props.pointInfo);
// 判断是否可以进行导航
const canNavigate = computed(() => {
return pointInfo.value?.name && pointInfo.value?.id;
});
// 处理导航到站点
const handleNavigateToPoint = async () => {
if (!canNavigate.value) {
message.warning('站点信息不完整,无法导航');
return;
}
// 打开机器人选择弹窗
showRobotSelector.value = true;
};
// 处理机器人选择
const handleRobotSelected = async (data: { robot: RobotInfo; targetPointName: string }) => {
if (!pointInfo.value) {
message.error('站点信息丢失,无法执行导航');
return;
}
loading.value = true;
try {
console.log('选择机器人进行导航:', {
robot: data.robot.label,
targetPoint: data.targetPointName,
});
// 调用导航API
const result = await executeNavigateToPoint(
pointInfo.value.id,
pointInfo.value.name,
data.robot.label,
data.robot.label
);
// 发送操作完成事件
emit('actionComplete', {
action: 'navigateToPoint',
point: pointInfo.value,
success: result.success,
message: result.message,
});
// 显示结果提示
if (result.success) {
message.success(`已为机器人 ${data.robot.label} 下发导航任务到 ${data.targetPointName}`);
}
} catch (error) {
console.error('导航操作失败:', error);
// 发送失败事件
emit('actionComplete', {
action: 'navigateToPoint',
point: pointInfo.value,
success: false,
message: undefined, // 让全局错误处理显示
});
message.error('导航操作失败');
} finally {
loading.value = false;
}
};
// 处理机器人选择弹窗取消
const handleRobotSelectorCancel = () => {
showRobotSelector.value = false;
console.log('用户取消了机器人选择');
};
</script>
<style scoped>
.point-menu {
min-width: 280px;
max-width: 320px;
padding: 4px;
}
/* 菜单头部 */
.menu-header {
padding: 4px 8px;
}
.point-info {
display: flex;
align-items: center;
gap: 8px;
}
.point-icon {
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
}
.point-details {
flex: 1;
display: flex;
align-items: center;
gap: 8px;
}
.point-name {
font-size: 13px;
font-weight: 500;
}
.point-id {
font-size: 11px;
color: var(--text-color-secondary, #8c8c8c);
}
/* 菜单选项 */
.menu-options {
padding: 0 2px;
}
.menu-item {
padding: 6px 8px;
margin: 2px 0;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
}
.menu-item:hover:not(.disabled) {
background-color: var(--hover-bg, #f5f5f5);
}
/* 确保菜单项不超出容器宽度 */
.menu-item-content {
box-sizing: border-box;
width: 100%;
}
.menu-item.disabled {
opacity: 0.6;
cursor: not-allowed;
}
.menu-item-content {
display: flex;
align-items: center;
gap: 8px;
}
.menu-icon {
font-size: 14px;
width: 16px;
text-align: center;
}
.menu-text {
font-size: 13px;
font-weight: 500;
color: var(--text-color, #262626);
}
/* 深色主题样式 */
:root([data-theme='dark']) .point-menu {
background-color: #1f1f1f;
}
:root([data-theme='dark']) .point-name {
color: #ffffff;
}
:root([data-theme='dark']) .point-id {
color: #a0a0a0;
}
:root([data-theme='dark']) .menu-item:hover:not(.disabled) {
background-color: #333333;
}
:root([data-theme='dark']) .menu-text {
color: #ffffff;
}
/* 兼容其他可能的深色主题类名 */
.dark .point-menu,
[data-theme="dark"] .point-menu,
.theme-dark .point-menu {
background-color: #1f1f1f;
}
.dark .point-name,
[data-theme="dark"] .point-name,
.theme-dark .point-name {
color: #ffffff;
}
.dark .point-id,
[data-theme="dark"] .point-id,
.theme-dark .point-id {
color: #a0a0a0;
}
.dark .menu-item:hover:not(.disabled),
[data-theme="dark"] .menu-item:hover:not(.disabled),
.theme-dark .menu-item:hover:not(.disabled) {
background-color: #333333;
}
.dark .menu-text,
[data-theme="dark"] .menu-text,
.theme-dark .menu-text {
color: #ffffff;
}
</style>