|
|
@@ -40,58 +40,68 @@
|
|
|
</el-row>
|
|
|
|
|
|
<!-- 左脚配置菜单 -->
|
|
|
- <div v-if="showLeftMenu" class="context-menu" :style="{ left: menuPosition.x + 'px', top: menuPosition.y + 'px' }" @click.stop>
|
|
|
- <div class="menu-title">左脚配置</div>
|
|
|
- <div class="menu-items">
|
|
|
- <div
|
|
|
- v-for="tab in leftTabs"
|
|
|
- :key="tab.id"
|
|
|
- class="menu-item"
|
|
|
- :class="{ active: leftConfigId === tab.id }"
|
|
|
- @click="selectConfig('left', tab.id)"
|
|
|
- >
|
|
|
- {{ leftConfigId === tab.id ? '✓ ' : '' }}{{ tab.mode_name }}
|
|
|
- </div>
|
|
|
+ <Teleport to="body">
|
|
|
+ <div v-if="showLeftMenu" class="context-menu" :style="menuStyle" @click.stop>
|
|
|
+ <div class="menu-title">左脚配置</div>
|
|
|
+ <div class="menu-items" :style="{ maxHeight: menuMaxHeight + 'px' }">
|
|
|
+ <div
|
|
|
+ v-for="tab in leftTabs"
|
|
|
+ :key="tab.id"
|
|
|
+ class="menu-item"
|
|
|
+ :class="{ active: leftConfigId === tab.id }"
|
|
|
+ :title="tab.mode_name"
|
|
|
+ @click="selectConfig('left', tab.id)"
|
|
|
+ >
|
|
|
+ {{ leftConfigId === tab.id ? '✓ ' : '' }}{{ tab.mode_name }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </Teleport>
|
|
|
|
|
|
<!-- 右脚配置菜单 -->
|
|
|
- <div v-if="showRightMenu" class="context-menu" :style="{ left: menuPosition.x + 'px', top: menuPosition.y + 'px' }" @click.stop>
|
|
|
- <div class="menu-title">右脚配置</div>
|
|
|
- <div class="menu-items">
|
|
|
- <div
|
|
|
- v-for="tab in rightTabs"
|
|
|
- :key="tab.id"
|
|
|
- class="menu-item"
|
|
|
- :class="{ active: rightConfigId === tab.id }"
|
|
|
- @click="selectConfig('right', tab.id)"
|
|
|
- >
|
|
|
- {{ rightConfigId === tab.id ? '✓ ' : '' }}{{ tab.mode_name }}
|
|
|
+ <Teleport to="body">
|
|
|
+ <div v-if="showRightMenu" class="context-menu" :style="menuStyle" @click.stop>
|
|
|
+ <div class="menu-title">右脚配置</div>
|
|
|
+ <div class="menu-items" :style="{ maxHeight: menuMaxHeight + 'px' }">
|
|
|
+ <div
|
|
|
+ v-for="tab in rightTabs"
|
|
|
+ :key="tab.id"
|
|
|
+ class="menu-item"
|
|
|
+ :class="{ active: rightConfigId === tab.id }"
|
|
|
+ :title="tab.mode_name"
|
|
|
+ @click="selectConfig('right', tab.id)"
|
|
|
+ >
|
|
|
+ {{ rightConfigId === tab.id ? '✓ ' : '' }}{{ tab.mode_name }}
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </Teleport>
|
|
|
|
|
|
<!-- ========== 拍照配置菜单(仅多相机模式显示)========== -->
|
|
|
- <div v-if="isMultiCameraMode && showPhotoMenu" class="context-menu photo-menu" :style="{ left: menuPosition.x + 'px', top: menuPosition.y + 'px' }" @click.stop>
|
|
|
- <div class="menu-title">拍照配置</div>
|
|
|
- <div class="menu-items">
|
|
|
- <div
|
|
|
- v-for="point in pointList"
|
|
|
- :key="point.key"
|
|
|
- class="menu-item photo-point-item"
|
|
|
- :class="{ active: selectedPoint === point.key }"
|
|
|
- @click="selectPhotoPoint(point.key)"
|
|
|
- >
|
|
|
- <span class="point-name">{{ selectedPoint === point.key ? '✓ ' : '' }}点位 {{ point.key }}</span>
|
|
|
- <el-tag :type="point.connected ? 'success' : 'danger'" size="small">
|
|
|
- {{ point.connected ? '已连接' : '未连接' }}
|
|
|
- </el-tag>
|
|
|
+ <Teleport to="body">
|
|
|
+ <div v-if="isMultiCameraMode && showPhotoMenu" class="context-menu photo-menu" :style="menuStyle" @click.stop>
|
|
|
+ <div class="menu-title">拍照配置</div>
|
|
|
+ <div class="menu-items" :style="{ maxHeight: menuMaxHeight + 'px' }">
|
|
|
+ <div
|
|
|
+ v-for="point in pointList"
|
|
|
+ :key="point.key"
|
|
|
+ class="menu-item photo-point-item"
|
|
|
+ :class="{ active: selectedPoint === point.key }"
|
|
|
+ @click="selectPhotoPoint(point.key)"
|
|
|
+ >
|
|
|
+ <span class="point-name">{{ selectedPoint === point.key ? '✓ ' : '' }}点位 {{ point.key }}</span>
|
|
|
+ <el-tag :type="point.connected ? 'success' : 'danger'" size="small">
|
|
|
+ {{ point.connected ? '已连接' : '未连接' }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </Teleport>
|
|
|
|
|
|
<!-- 点击其他地方关闭菜单 -->
|
|
|
- <div v-if="showLeftMenu || showRightMenu || (isMultiCameraMode && showPhotoMenu)" class="menu-overlay" @click="closeMenus"></div>
|
|
|
+ <Teleport to="body">
|
|
|
+ <div v-if="showLeftMenu || showRightMenu || (isMultiCameraMode && showPhotoMenu)" class="menu-overlay" @click="closeMenus"></div>
|
|
|
+ </Teleport>
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
@@ -204,6 +214,59 @@ const showLeftMenu = ref(false) // 显示左脚菜单
|
|
|
const showRightMenu = ref(false) // 显示右脚菜单
|
|
|
const showPhotoMenu = ref(false) // 显示拍照配置菜单
|
|
|
const menuPosition = ref({ x: 0, y: 0 }) // 菜单位置
|
|
|
+const menuMaxHeight = ref(240)
|
|
|
+
|
|
|
+const menuStyle = computed(() => ({
|
|
|
+ left: menuPosition.value.x + 'px',
|
|
|
+ top: menuPosition.value.y + 'px',
|
|
|
+}))
|
|
|
+
|
|
|
+const MENU_MARGIN = 8
|
|
|
+const MENU_TITLE_HEIGHT = 37
|
|
|
+const MENU_WIDTH = 220
|
|
|
+const MENU_LIST_MAX = 240
|
|
|
+const MENU_LIST_MIN = 80
|
|
|
+
|
|
|
+function placeMenu(event: MouseEvent) {
|
|
|
+ const vw = window.innerWidth
|
|
|
+ const vh = window.innerHeight
|
|
|
+ const cx = event.clientX
|
|
|
+ const cy = event.clientY
|
|
|
+
|
|
|
+ let x = cx
|
|
|
+ if (x + MENU_WIDTH > vw - MENU_MARGIN) {
|
|
|
+ x = Math.max(MENU_MARGIN, vw - MENU_WIDTH - MENU_MARGIN)
|
|
|
+ }
|
|
|
+
|
|
|
+ const spaceBelow = vh - cy - MENU_MARGIN
|
|
|
+ const spaceAbove = cy - MENU_MARGIN
|
|
|
+ const preferred = MENU_TITLE_HEIGHT + MENU_LIST_MAX
|
|
|
+
|
|
|
+ let y = cy
|
|
|
+ let listMax = MENU_LIST_MAX
|
|
|
+
|
|
|
+ if (spaceBelow >= preferred) {
|
|
|
+ y = cy
|
|
|
+ listMax = MENU_LIST_MAX
|
|
|
+ } else if (spaceBelow >= MENU_TITLE_HEIGHT + MENU_LIST_MIN) {
|
|
|
+ y = cy
|
|
|
+ listMax = spaceBelow - MENU_TITLE_HEIGHT
|
|
|
+ } else if (spaceAbove >= MENU_TITLE_HEIGHT + MENU_LIST_MIN) {
|
|
|
+ listMax = Math.min(MENU_LIST_MAX, spaceAbove - MENU_TITLE_HEIGHT)
|
|
|
+ y = cy - MENU_TITLE_HEIGHT - listMax
|
|
|
+ } else if (spaceBelow >= spaceAbove) {
|
|
|
+ listMax = Math.max(48, spaceBelow - MENU_TITLE_HEIGHT)
|
|
|
+ y = cy
|
|
|
+ } else {
|
|
|
+ listMax = Math.max(48, spaceAbove - MENU_TITLE_HEIGHT)
|
|
|
+ y = Math.max(MENU_MARGIN, cy - MENU_TITLE_HEIGHT - listMax)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (y < MENU_MARGIN) y = MENU_MARGIN
|
|
|
+
|
|
|
+ menuPosition.value = { x, y }
|
|
|
+ menuMaxHeight.value = listMax
|
|
|
+}
|
|
|
|
|
|
const runLeft = async () => {
|
|
|
emit('onRemoteControl','left')
|
|
|
@@ -248,7 +311,8 @@ const handleLeftRightClick = (event, type) => {
|
|
|
return
|
|
|
}
|
|
|
event.preventDefault()
|
|
|
- menuPosition.value = { x: event.clientX, y: event.clientY }
|
|
|
+ placeMenu(event)
|
|
|
+ showPhotoMenu.value = false
|
|
|
|
|
|
if (type === 'left') {
|
|
|
showLeftMenu.value = true
|
|
|
@@ -330,7 +394,7 @@ const handlePhotoConfigClick = (event: MouseEvent) => {
|
|
|
return
|
|
|
}
|
|
|
event.preventDefault()
|
|
|
- menuPosition.value = { x: event.clientX, y: event.clientY }
|
|
|
+ placeMenu(event)
|
|
|
|
|
|
// 关闭其他菜单
|
|
|
showLeftMenu.value = false
|
|
|
@@ -413,9 +477,11 @@ const selectPhotoPoint = (pointKey: string) => {
|
|
|
border: 1px solid #e4e7ed;
|
|
|
border-radius: 4px;
|
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
|
- z-index: 2000;
|
|
|
- min-width: 120px;
|
|
|
- max-width: 200px;
|
|
|
+ z-index: 3000;
|
|
|
+ min-width: 160px;
|
|
|
+ max-width: 260px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
}
|
|
|
|
|
|
.menu-title {
|
|
|
@@ -425,11 +491,24 @@ const selectPhotoPoint = (pointKey: string) => {
|
|
|
color: #606266;
|
|
|
border-bottom: 1px solid #ebeef5;
|
|
|
background-color: #f5f7fa;
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
|
.menu-items {
|
|
|
+ max-height: 240px;
|
|
|
+ overflow-x: hidden;
|
|
|
+ overflow-y: auto;
|
|
|
+ min-height: 0;
|
|
|
|
|
|
- max-height: 150px;
|
|
|
- overflow: auto;
|
|
|
+ &::-webkit-scrollbar {
|
|
|
+ width: 6px;
|
|
|
+ }
|
|
|
+ &::-webkit-scrollbar-thumb {
|
|
|
+ background: #c0c4cc;
|
|
|
+ border-radius: 3px;
|
|
|
+ }
|
|
|
+ &::-webkit-scrollbar-track {
|
|
|
+ background: #f5f7fa;
|
|
|
+ }
|
|
|
}
|
|
|
.menu-item {
|
|
|
padding: 8px 12px;
|
|
|
@@ -437,6 +516,8 @@ const selectPhotoPoint = (pointKey: string) => {
|
|
|
color: #606266;
|
|
|
cursor: pointer;
|
|
|
transition: background-color 0.2s;
|
|
|
+ word-break: break-all;
|
|
|
+ line-height: 20px;
|
|
|
|
|
|
&:hover {
|
|
|
background-color: #f5f7fa;
|
|
|
@@ -454,7 +535,7 @@ const selectPhotoPoint = (pointKey: string) => {
|
|
|
left: 0;
|
|
|
right: 0;
|
|
|
bottom: 0;
|
|
|
- z-index: 1999;
|
|
|
+ z-index: 2999;
|
|
|
}
|
|
|
|
|
|
/* 新增:拍照配置样式 */
|