Просмотр исходного кода

feat(photography): 实现多相机模式下的动态点位配置功能

- 在editRow.vue中替换静态点位选项为动态获取的已配置点位列表
- 新增refreshPointList方法通过getAllUserConfigs API获取已绑定相机的点位
- 在RemoteControl.vue中添加拍照配置按钮和动态点位选择菜单
- 实现点位连接状态检测和本地存储的点位配置管理
- 修改onRemoteControl方法以支持动态点位参数传递
- 优化点位验证逻辑确保只显示已绑定相机的有效点位
panqiuyao 18 часов назад
Родитель
Сommit
97aedf1507

+ 35 - 7
frontend/src/views/Photography/components/editRow.vue

@@ -12,10 +12,8 @@
 
       <!-- 多相机模式 - 拍摄点位 -->
       <el-form-item label="拍摄点位" v-if="isMultiCameraMode">
-        <el-select v-model="editRowData.point_name" placeholder="请选择点位" style="width: 170px;">
-          <el-option label="点位 A" value="A"/>
-          <el-option label="点位 B" value="B"/>
-          <el-option label="点位 C" value="C"/>
+        <el-select v-model="editRowData.point_name" placeholder="请选择点位" style="width: 170px;" @focus="refreshPointList">
+          <el-option v-for="point in availablePoints" :key="point" :label="'点位 ' + point" :value="point"/>
         </el-select>
       </el-form-item>
 
@@ -100,7 +98,7 @@ import { ref, defineProps, defineEmits , watch, onMounted, computed } from 'vue'
 import icpList from '@/utils/ipc';
 import { digiCamControlWEB } from  '@/utils/appconfig'
 import {ElMessage} from "element-plus";
-import  { getDeviceConfigDetail,getDeviceConfigDetailQuery,saveDeviceConfig } from '@/apis/setting'
+import  { getDeviceConfigDetail,getDeviceConfigDetailQuery,saveDeviceConfig, getAllUserConfigs } from '@/apis/setting'
 import client from "@/stores/modules/client";
 import useUserInfo from '@/stores/modules/user';
 
@@ -115,6 +113,28 @@ const userInfoStore = useUserInfo();
 // 是否为多相机模式
 const isMultiCameraMode = computed(() => userInfoStore.isMultiCameraMode)
 
+// 已配置相机的点位列表
+const availablePoints = ref<string[]>(['A'])
+
+// 刷新点位列表(从 getAllUserConfigs API 获取已绑定相机的点位)
+const refreshPointList = async () => {
+  try {
+    const result = await getAllUserConfigs({})
+    if (result.code == 0 && result.data?.configs?.camera_configs?.iso_config) {
+      const isoConfig = result.data.configs.camera_configs.iso_config
+      availablePoints.value = Object.keys(isoConfig).filter(key => {
+        const config = isoConfig[key]
+        return config && config.CameraKey && config.CameraKey !== ''
+      })
+    }
+  } catch (err) {
+    console.error('获取点位配置失败:', err)
+  }
+  if (availablePoints.value.length === 0) {
+    availablePoints.value = ['A']
+  }
+}
+
 // 定义 props
 const props = defineProps({
   id:{
@@ -147,13 +167,18 @@ watch(() => editRowData.value.point_name, (newPoint, oldPoint) => {
 
 onMounted(async ()=>{
   console.log('editrow')
+  // 刷新点位列表
+  refreshPointList()
   if(props.addRowData.mode_type){
     console.log(props.addRowData);
     initStatus.value = true
     editRowData.value = props.addRowData;
     // 多相机模式默认值
     if (isMultiCameraMode.value) {
-      editRowData.value.point_name = editRowData.value.point_name || 'A'
+      // 验证点位是否有效,无效则使用第一个可用点位
+      if (!availablePoints.value.includes(editRowData.value.point_name)) {
+        editRowData.value.point_name = availablePoints.value[0] || 'A'
+      }
       editRowData.value.is_move_device = editRowData.value.is_move_device ?? 1
     }
     testShoesFlip()
@@ -180,7 +205,10 @@ onMounted(async ()=>{
     editRowData.value = result.data;
     // 多相机模式默认值处理
     if (isMultiCameraMode.value) {
-      editRowData.value.point_name = editRowData.value.point_name || 'A'
+      // 验证点位是否有效,无效则使用第一个可用点位
+      if (!availablePoints.value.includes(editRowData.value.point_name)) {
+        editRowData.value.point_name = availablePoints.value[0] || 'A'
+      }
       editRowData.value.is_move_device = editRowData.value.is_move_device ?? 1
     }
     initStatus.value = true;

+ 68 - 28
frontend/src/views/Photography/mixin/usePhotography.ts

@@ -12,6 +12,7 @@ import { useUuidStore } from '@/stores/modules/uuid'
 import useUserInfo from "@/stores/modules/user";
 import configInfo from '@/stores/modules/config';
 import tokenInfo from "@/stores/modules/token";
+import { getAllUserConfigs } from '@/apis/setting';
 
 export default function usePhotography() {
     const loading = ref(false)
@@ -49,6 +50,40 @@ export default function usePhotography() {
     const tokenInfoStore = tokenInfo();
     const checkInfoStore = checkInfo()
 
+    // 拍照点位配置
+    const STORAGE_KEY = 'photo_point_name'
+
+    // 获取已配置相机的点位列表(从 getAllUserConfigs API 获取,仅返回已绑定相机的点位)
+    const getPointList = async (): Promise<string[]> => {
+      try {
+        const result = await getAllUserConfigs({})
+        if (result.code == 0 && result.data?.configs?.camera_configs?.iso_config) {
+          const isoConfig = result.data.configs.camera_configs.iso_config
+          const points = Object.keys(isoConfig).filter(key => {
+            const config = isoConfig[key]
+            return config && config.CameraKey && config.CameraKey !== ''
+          })
+          return points.length > 0 ? points : ['A']
+        }
+      } catch (err) {
+        console.error('获取点位配置失败:', err)
+      }
+      return ['A']
+    }
+
+    // 获取保存的点位(单相机模式默认A,多相机模式验证点位合法性)
+    const getPhotoPointName = async (): Promise<string> => {
+      if (!useUserInfoStore.isMultiCameraMode) {
+        return 'A'
+      }
+      const pointList = await getPointList()
+      const savedPoint = localStorage.getItem(STORAGE_KEY)
+      if (savedPoint && pointList.includes(savedPoint)) {
+        return savedPoint
+      }
+      return pointList[0] || 'A'
+    }
+
     // 抠图请求去重与延迟队列(key: goods_art_no, value: timeoutId)
     const segmentQueue = new Map<string, ReturnType<typeof setTimeout>>()
 
@@ -602,42 +637,47 @@ export default function usePhotography() {
 
     })
 
-    const onRemoteControl = (type) => {
-      if (type == 'take_picture') {
-        // 埋点:手动拍照
-        clickLog({ describe: { action: '点击遥控器拍照按钮' } }, route);
+const onRemoteControl = async (type) => {
+  const pointName = await getPhotoPointName()
 
-        if (runLoading.value || takePictureLoading.value) {
-          ElMessage.error('拍摄程序正在运行,请稍候')
-          return
-        }
+  if (type == 'take_picture') {
+    // 埋点:手动拍照
+    clickLog({ describe: { action: '点击遥控器拍照按钮', point_name: pointName } }, route);
 
-        ElMessage.success('正在拍摄中,请稍候')
-        takePictureLoading.value = true;
-        socketStore.sendMessage({
-          type: 'handler_take_picture',
-        })
-        return;
+    if (runLoading.value || takePictureLoading.value) {
+      ElMessage.error('拍摄程序正在运行,请稍候')
+      return
+    }
+
+    ElMessage.success('正在拍摄中,请稍候')
+    takePictureLoading.value = true;
+    socketStore.sendMessage({
+      type: 'handler_take_picture',
+      data: {
+        point_name: pointName
       }
+    })
+    return;
+  }
 
 
-      if (!goods_art_no.value) {
-        ElMessage.error('请在左侧第一步中,先扫描货号或者手动输入货号!')
-        goodsArtNo.value?.focus() // 聚焦输入框
-        return;
-      }
-      let action = '执行左脚程序'
-      if (type === 'right') action = '执行右脚程序'
+  if (!goods_art_no.value) {
+    ElMessage.error('请在左侧第一步中,先扫描货号或者手动输入货号!')
+    goodsArtNo.value?.focus() // 聚焦输入框
+    return;
+  }
+  let action = '执行左脚程序'
+  if (type === 'right') action = '执行右脚程序'
 
-      // 埋点:遥控器启动拍摄
-      clickLog({ describe: { action: `点击遥控器${type === 'left' ? '左脚' : '右脚'}按钮`, goods_art_no: goods_art_no.value } }, route);
+  // 埋点:遥控器启动拍摄
+  clickLog({ describe: { action: `点击遥控器${type === 'left' ? '左脚' : '右脚'}按钮`, goods_art_no: goods_art_no.value, point_name: pointName } }, route);
 
-      runGoods({
-        "action": action,
-        "goods_art_no": goods_art_no.value,
-      })
+  runGoods({
+    "action": action,
+    "goods_art_no": goods_art_no.value
+  })
 
-    }
+}
 
     // 初始化事件监听
     const initEventListeners = () => {

+ 165 - 13
frontend/src/views/RemoteControl/index.vue

@@ -1,10 +1,5 @@
 <template>
 
-<!--
-  <headerBar
-    title="遥控模拟器"
-  />-->
-
   <div class="remote-control_main-container">
     <div class="te-c"  style="color: #8C92A7">遥控器模拟器</div>
 
@@ -15,13 +10,21 @@
       <el-col :span="6"><div class="button up" title="单击鼠标右键可切换配置" @click="runRight">右脚</div></el-col>
       <el-col :span="3"></el-col>
     </el-row>
-    <el-row align="middle" >
+
+    <!-- 左脚配置、拍照配置、右脚配置按钮行 -->
+    <el-row align="middle">
       <el-col :span="3"></el-col>
-      <el-col :span="6"><div class="button up"  :class="{ disabled: canStop }"   @click="handleLeftRightClick($event, 'left')"><span style="font-size: 12px;">左脚配置</span></div></el-col>
-      <el-col :span="6"></el-col>
-      <el-col :span="6"><div class="button up"  :class="{ disabled: canStop }"   @click="handleLeftRightClick($event, 'right')"><span style="font-size: 12px;">右脚配置</span></div></el-col>
+      <el-col :span="6"><div class="button up" :class="{ disabled: canStop }" @click="handleLeftRightClick($event, 'left')"><span style="font-size: 12px;">左脚配置</span></div></el-col>
+      <!-- ========== 新增:拍照配置按钮 ========== -->
+      <el-col :span="6">
+        <div class="button up photo-config-btn" :class="{ disabled: canStop, active: showPhotoMenu }" @click="handlePhotoConfigClick">
+          <span style="font-size: 12px;">拍照配置</span>
+        </div>
+      </el-col>
+      <el-col :span="6"><div class="button up" :class="{ disabled: canStop }" @click="handleLeftRightClick($event, 'right')"><span style="font-size: 12px;">右脚配置</span></div></el-col>
       <el-col :span="3"></el-col>
     </el-row>
+
     <el-row align="middle">
       <el-col :span="3"></el-col>
       <el-col :span="6">
@@ -68,20 +71,42 @@
       </div>
     </div>
 
+    <!-- ========== 新增:拍照配置菜单 ========== -->
+    <div v-if="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>
+        </div>
+      </div>
+    </div>
+
     <!-- 点击其他地方关闭菜单 -->
-    <div v-if="showLeftMenu || showRightMenu" class="menu-overlay" @click="closeMenus"></div>
+    <div v-if="showLeftMenu || showRightMenu || showPhotoMenu" class="menu-overlay" @click="closeMenus"></div>
   </div>
 
 </template>
 
 <script setup lang="ts">
-import { defineEmits, defineProps, ref } from 'vue'
+import { defineEmits, defineProps, ref, onMounted } from 'vue'
 import socket from "@/stores/modules/socket";
-import { getTopTabs, setLeftRightConfig } from '@/apis/setting';
+import { getTopTabs, setLeftRightConfig, getAllUserConfigs } from '@/apis/setting';
 import { ElMessage } from 'element-plus';
+import client from "@/stores/modules/client";
+import icpList from '@/utils/ipc';
 
 // 初始化 WebSocket 状态管理
 const socketStore = socket()
+const clientStore = client()
 
 const props = defineProps<{
   canStop: boolean
@@ -89,13 +114,90 @@ const props = defineProps<{
 
 const emit = defineEmits(['onRemoteControl'])
 
-// 配置切换相关
+// ========== 新增:点位配置 ==========
+const STORAGE_KEY = 'photo_point_name'
+const selectedPoint = ref('A')
+
+// 相机列表和点位列表
+const cameraList = ref<any[]>([])
+const pointList = ref<{ key: string; connected: boolean; cameraName: string }[]>([])
+
+// 初始化从 localStorage 读取配置
+onMounted(() => {
+  const savedPoint = localStorage.getItem(STORAGE_KEY)
+  if (savedPoint && ['A', 'B', 'C'].includes(savedPoint)) {
+    selectedPoint.value = savedPoint
+  }
+  // 获取相机列表
+  fetchCameraList()
+})
+
+// 获取相机列表并更新点位连接状态
+const fetchCameraList = () => {
+  clientStore.ipc.invoke(icpList.camera.getCameraList).then(result => {
+    if (result && result.CameraLists && Array.isArray(result.CameraLists)) {
+      cameraList.value = result.CameraLists
+      updatePointConnectionStatus()
+    }
+  }).catch(err => {
+    console.error('获取相机列表失败:', err)
+  })
+}
+
+// 更新点位连接状态(仅显示已绑定相机的点位)
+const updatePointConnectionStatus = async () => {
+  let isoConfig: any = {}
+  try {
+    const result = await getAllUserConfigs({})
+    if (result.code == 0 && result.data?.configs?.camera_configs?.iso_config) {
+      isoConfig = result.data.configs.camera_configs.iso_config
+    }
+  } catch (err) {
+    console.error('获取点位配置失败:', err)
+  }
+  // 只保留已绑定相机的点位
+  const validPoints = Object.keys(isoConfig).filter(key => {
+    const config = isoConfig[key]
+    return config && config.CameraKey && config.CameraKey !== ''
+  })
+  if (validPoints.length === 0) {
+    validPoints.push('A')
+  }
+  pointList.value = validPoints.map(key => {
+    const cameraKey = isoConfig[key]?.CameraKey || ''
+    let connected = false
+    let cameraName = ''
+
+    if (cameraKey) {
+      const camera = cameraList.value.find(c => c.CameraKey === cameraKey)
+      if (camera) {
+        connected = camera.CameraStatus === true
+        cameraName = camera.CameraName
+      }
+    }
+
+    return { key, connected, cameraName }
+  })
+  // 验证当前选中的点位是否有效
+  if (!pointList.value.some(p => p.key === selectedPoint.value)) {
+    selectedPoint.value = pointList.value[0]?.key || 'A'
+  }
+}
+
+
+// 暴露点位给父组件
+defineExpose({
+  getSelectedPoint: () => selectedPoint.value
+})
+
+// ========== 配置切换相关 ==========
 const leftConfigId = ref(0)
 const rightConfigId = ref(0)
 const leftTabs = ref([]) // 左脚配置选项
 const rightTabs = ref([]) // 右脚配置选项
 const showLeftMenu = ref(false) // 显示左脚菜单
 const showRightMenu = ref(false) // 显示右脚菜单
+const showPhotoMenu = ref(false) // 显示拍照配置菜单
 const menuPosition = ref({ x: 0, y: 0 }) // 菜单位置
 
 const runLeft = async () => {
@@ -212,6 +314,35 @@ const selectConfig = async (type, configId) => {
 const closeMenus = () => {
   showLeftMenu.value = false
   showRightMenu.value = false
+  showPhotoMenu.value = false
+}
+
+// ========== 新增:拍照配置菜单相关 ==========
+
+// 点击拍照配置按钮
+const handlePhotoConfigClick = (event: MouseEvent) => {
+  if(props.canStop){
+    return
+  }
+  event.preventDefault()
+  menuPosition.value = { x: event.clientX, y: event.clientY }
+
+  // 关闭其他菜单
+  showLeftMenu.value = false
+  showRightMenu.value = false
+
+  // 获取最新的相机列表
+  fetchCameraList()
+
+  showPhotoMenu.value = true
+}
+
+// 选择拍照点位
+const selectPhotoPoint = (pointKey: string) => {
+  selectedPoint.value = pointKey
+  localStorage.setItem(STORAGE_KEY, pointKey)
+  showPhotoMenu.value = false
+  ElMessage.success(`已切换到点位 ${pointKey}`)
 }
 
 </script>
@@ -320,4 +451,25 @@ const closeMenus = () => {
   bottom: 0;
   z-index: 1999;
 }
+
+/* 新增:拍照配置样式 */
+.photo-config-btn.active {
+  background: url(@/assets/images/Photography/lan.png) 0px 0px no-repeat !important;
+  background-size: 60px 60px !important;
+}
+
+.photo-menu {
+  min-width: 180px;
+}
+
+.photo-point-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+}
+
+.photo-point-item .point-name {
+  flex-shrink: 0;
+}
 </style>