Sfoglia il codice sorgente

feat(camera): 实现多相机模式支持及相机列表缓存功能

- 在 electron/controller/camera.js 中添加相机列表缓存机制
- 为设置页面添加多相机配置界面和点位切换功能
- 实现相机绑定、ISO参数配置的多点位管理模式
- 添加多相机模式下的拍摄点位选择功能
- 集成相机状态管理和自动分配逻辑
- 优化相机配置的数据结构和验证机制
panqiuyao 12 ore fa
parent
commit
867165d570

+ 18 - 0
electron/controller/camera.js

@@ -11,6 +11,9 @@ const { readConfigFile } = require('../utils/config');
 let  isOPen = true
 let isSoftwareStarted = false; // 标记相机控制软件是否已经启动
 
+// 缓存相机列表数据,供多窗口共享
+let cachedCameraList = [];
+
 // 注册进程关闭事件监听器,当软件被关闭时重置标志
 onProcessClosed(() => {
   Log.info('相机控制软件已关闭,重置 isSoftwareStarted 标志');
@@ -93,6 +96,10 @@ class CameraController extends Controller {
 
         if(res?.device_status  === 2){
           isOPen = true;
+          // 缓存相机列表数据
+          if (res.CameraLists) {
+            cachedCameraList = res.CameraLists;
+          }
           return  {
             ...res,
             status:2
@@ -173,6 +180,17 @@ class CameraController extends Controller {
     }
   }
 
+  /**
+   * 获取缓存的相机列表(供设置页面等子窗口调用)
+   */
+  async getCameraList() {
+    return {
+      CameraLists: cachedCameraList,
+      status: cachedCameraList.length > 0 ? 2 : 0,
+      msg: cachedCameraList.length > 0 ? '获取成功' : '暂无相机列表'
+    };
+  }
+
   // 示例:使用 fetchData 函数进行 HTTP 请求
   async fetchExampleData() {
     const options = {

+ 23 - 0
frontend/src/stores/modules/check.ts

@@ -39,6 +39,17 @@ export const checkInfo = defineStore('checkInfo', () => {
         },*/
     });
 
+    // 多相机模式 - 已连接相机列表
+    const cameraList = ref<Array<{
+        CameraSelection: string
+        CameraKey: string
+        CameraName: string
+        CameraStatus: boolean
+    }>>([])
+
+    // 当前选中的相机Key
+    const currentCameraKey = ref('')
+
     // 定义蓝牙扫描编号
     const blue_tooth_scan_NO = ref('')
     // 定义检查时间
@@ -121,6 +132,15 @@ export const checkInfo = defineStore('checkInfo', () => {
                           devices.cam_control.status = result.status;
                           devices.cam_control.msg = result.msg;
                       }
+                      // 解析 CameraLists(多相机模式)
+                      if (result.CameraLists && Array.isArray(result.CameraLists)) {
+                          cameraList.value = result.CameraLists
+                          // 默认选中第一个已连接的相机
+                          const activeCamera = result.CameraLists.find((c: any) => c.CameraStatus)
+                          if (activeCamera) {
+                              currentCameraKey.value = activeCamera.CameraKey
+                          }
+                      }
                   }
 
                   if(!result  && checkTime.value > 0){
@@ -296,6 +316,9 @@ export const checkInfo = defineStore('checkInfo', () => {
         set_blue_tooth_scan_NO,
         checkAction,
         reCheckAction,
+        // 多相机模式相关
+        cameraList,
+        currentCameraKey,
     };
 });
 

+ 12 - 0
frontend/src/stores/modules/user.ts

@@ -16,6 +16,16 @@ export const useUserInfo = defineStore('userInfo', () => {
   const isExpired = ref(false); // 企业账户是否过期
   const expiredMessage = ref(''); // 过期提示消息
 
+  // 多相机模式判断
+  // zhihuiyinType: 1 = 单相机, 2 = 多相机
+  const isMultiCameraMode = computed(() => {
+    return userInfo.value.zhihuiyinType === 2
+  })
+
+  const cameraMode = computed(() => {
+    return userInfo.value.zhihuiyinType === 2 ? 'multi' : 'single'
+  })
+
   // 定时检查相关
   let checkInterval: NodeJS.Timeout | null = null; // 定时器
   const CHECK_INTERVAL = 10 * 60 * 1000; // 10分钟检查一次
@@ -224,6 +234,8 @@ export const useUserInfo = defineStore('userInfo', () => {
     userInfo,
     isExpired,
     expiredMessage,
+    isMultiCameraMode,
+    cameraMode,
     updateUserInfo,
     updateLoginShow,
     loginAction,

+ 1 - 0
frontend/src/utils/ipc.ts

@@ -1,6 +1,7 @@
 const icpList = {
     camera:{
         connect: 'controller.camera.connect',
+        getCameraList: 'controller.camera.getCameraList',
         PreviewShow: 'controller.camera.liveShow',
         PreviewHide: 'controller.camera.liveHide',
         setParams:"controller.camera.setParams",

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

@@ -9,6 +9,26 @@
         <el-input v-model="editRowData.action_name" :disabled="editRowData.is_system" style="width: 170px;"/>
 
       </el-form-item>
+
+      <!-- 多相机模式 - 拍摄点位 -->
+      <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>
+      </el-form-item>
+
+      <!-- 多相机模式 - 仅拍照模式 -->
+      <el-form-item label="是否移动设备" v-if="isMultiCameraMode">
+        <div class="flex-row">
+          <el-radio-group v-model="editRowData.is_move_device">
+            <el-radio :label="0">不移动</el-radio>
+            <el-radio :label="1">移动</el-radio>
+          </el-radio-group>
+        </div>
+      </el-form-item>
+
       <el-form-item label="是否拍照" v-if="!editRowData.is_system">
         <el-radio-group v-model="editRowData.take_picture">
           <el-radio :label="1">拍照</el-radio>
@@ -76,15 +96,24 @@
 </template>
 
 <script setup lang="ts">
-import { ref, defineProps, defineEmits , watch, onMounted } from 'vue'
+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 client from "@/stores/modules/client";
+import useUserInfo from '@/stores/modules/user';
+
 const clientStore = client();
 import socket from "@/stores/modules/socket";
 const socketStore = socket(); // WebSocket状态管理实例
+import checkInfo from '@/stores/modules/check';
+
+const checkInfoStore = checkInfo();
+const userInfoStore = useUserInfo();
+
+// 是否为多相机模式
+const isMultiCameraMode = computed(() => userInfoStore.isMultiCameraMode)
 
 // 定义 props
 const props = defineProps({
@@ -109,6 +138,11 @@ onMounted(async ()=>{
     console.log(props.addRowData);
     initStatus.value = true
     editRowData.value = props.addRowData;
+    // 多相机模式默认值
+    if (isMultiCameraMode.value) {
+      editRowData.value.point_name = editRowData.value.point_name || 'A'
+      editRowData.value.is_move_device = editRowData.value.is_move_device ?? 1
+    }
     testShoesFlip()
     return
   }
@@ -130,6 +164,11 @@ onMounted(async ()=>{
 
   if(result.code == 0 && result.data){
     editRowData.value = result.data;
+    // 多相机模式默认值处理
+    if (isMultiCameraMode.value) {
+      editRowData.value.point_name = editRowData.value.point_name || 'A'
+      editRowData.value.is_move_device = editRowData.value.is_move_device ?? 1
+    }
     initStatus.value = true;
     testShoesFlip()
   }
@@ -236,19 +275,36 @@ const saveRow = async () => {
     return;
   }
 
+  // 多相机模式校验
+  if (isMultiCameraMode.value) {
+    if (!editRowData.value.point_name) {
+      ElMessage.error('请选择拍摄点位')
+      return;
+    }
+  }
+
   captureLoading.value = true
-  const result = await  saveDeviceConfig({
-    ...editRowData.value
-  })
+
+  // 构建保存数据,包含新增字段
+  const saveData = {
+    ...editRowData.value,
+  }
+
+  // 多相机模式添加点位信息
+  if (isMultiCameraMode.value) {
+    // 从相机列表中获取对应相机的名称
+    const camera = checkInfoStore.cameraList.find(c => c.CameraKey === editRowData.value.point_name)
+    saveData.point_name = editRowData.value.point_name
+    saveData.is_move_device = editRowData.value.is_move_device
+  }
+
+  const result = await  saveDeviceConfig(saveData)
   captureLoading.value = false
   if (result.code == 0) {
     emit('confirm')
     ElMessage.success('保存成功');
     clientStore.ipc.removeAllListeners(icpList.setting.saveDeviceConfig);
   }
-
-
-
 };
 
 // 暴露给父组件
@@ -373,4 +429,10 @@ defineExpose({
   line-height: 30px;
   cursor: pointer;
 }
+
+.mode-hint {
+  margin-left: 10px;
+  font-size: 12px;
+  color: #909399;
+}
 </style>

+ 383 - 101
frontend/src/views/Setting/components/CameraConfig.vue

@@ -1,45 +1,100 @@
 <template>
+  <div class="camera-config-container">
+    <!-- 统一使用多点位配置界面 -->
+    <div class="flex left fw-b fs-16 mar-top-20" style="padding-left: 100px">
+      {{ isMultiCameraMode ? '多点位相机配置' : '相机配置' }}<span style="color: #FD5E1A">(<el-icon style="position: relative; top:2px; margin: 0 3px"><WarningFilled /></el-icon>{{ isMultiCameraMode ? '每个点位绑定独立相机' : '点击点位切换相机配置' }})</span>
+    </div>
 
-  <div class="flex left fw-b fs-16 mar-top-20" style="padding-left: 100px">相机ISO参数<span style="color: #FD5E1A">(<el-icon style="position: relative; top:2px; margin: 0 3px"><WarningFilled /></el-icon>相机设置ISO auto时无效)</span></div>
-  <div class="selectBox">
-    <div class="form-item" style="padding-bottom: 30px;">
-      <div class="iso-inputs mar-top-20">
-        <div class="iso-group">
-          <span class="iso-label">拍照时:</span>
-          <div class="select-wrapper">
-            <el-select
-              v-model="iso_config.low"
-              filterable
-              default-first-option
-              placeholder="请选择或输入ISO值"
-              class="iso-input"
-            >
-              <el-option
-                v-for="item in iso_options"
-                :key="item"
-                :label="item"
-                :value="item"
-              />
-            </el-select>
-          </div>
+    <!-- 点位切换Tab -->
+    <div class="selectBox multi-camera-config">
+      <!-- 点位切换按钮 -->
+      <div class="point-tabs">
+        <div
+          v-for="point in displayPointList"
+          :key="point.key"
+          class="point-tab"
+          :class="{ active: activePoint === point.key }"
+          @click="switchPoint(point.key)"
+        >
+          <span class="tab-name">点位 {{ point.key }}</span>
+          <el-tag v-if="isMultiCameraMode" :type="point.connected ? 'success' : 'danger'" size="small">
+            {{ point.connected ? '已连接' : '未连接' }}
+          </el-tag>
+        </div>
+      </div>
+
+      <!-- 当前点位配置 -->
+      <div class="current-point-config">
+        <div class="config-header">
+          <span class="config-title">点位 {{ activePoint }} 配置</span>
         </div>
-        <div class="iso-group">
-          <span class="iso-label">预览时:</span>
-          <div class="select-wrapper">
-            <el-select
-              v-model="iso_config.high"
-              filterable
-              default-first-option
-              placeholder="请选择或输入ISO值"
-              class="iso-input"
-            >
-              <el-option
-                v-for="item in iso_options"
-                :key="item"
-                :label="item"
-                :value="item"
-              />
-            </el-select>
+
+        <div class="config-body">
+          <!-- 多相机模式下显示相机绑定 -->
+          <div v-if="isMultiCameraMode" class="form-item">
+            <label>绑定相机:</label>
+            <div class="camera-select-wrapper">
+              <el-select
+                v-model="multiConfig[activePoint].CameraKey"
+                placeholder="请选择相机"
+                filterable
+                clearable
+                @change="onCameraChange(activePoint)"
+              >
+                <el-option
+                  v-for="camera in availableCameras"
+                  :key="camera.CameraKey"
+                  :label="camera.CameraName"
+                  :value="camera.CameraKey"
+                  :disabled="!camera.CameraStatus"
+                >
+                  <span>{{ camera.CameraName }}</span>
+                  <el-tag v-if="!camera.CameraStatus" type="danger" size="small" style="margin-left: 8px">未连接</el-tag>
+                </el-option>
+              </el-select>
+            </div>
+          </div>
+
+          <!-- ISO配置 -->
+          <div class="iso-section">
+            <div class="iso-group">
+              <span class="iso-label">拍照ISO:</span>
+              <div class="select-wrapper">
+                <el-select
+                  v-model="multiConfig[activePoint].iso.low"
+                  filterable
+                  default-first-option
+                  placeholder="请选择"
+                  class="iso-select"
+                >
+                  <el-option
+                    v-for="item in iso_options"
+                    :key="item"
+                    :label="item"
+                    :value="item"
+                  />
+                </el-select>
+              </div>
+            </div>
+            <div class="iso-group">
+              <span class="iso-label">预览ISO:</span>
+              <div class="select-wrapper">
+                <el-select
+                  v-model="multiConfig[activePoint].iso.high"
+                  filterable
+                  default-first-option
+                  placeholder="请选择"
+                  class="iso-select"
+                >
+                  <el-option
+                    v-for="item in iso_options"
+                    :key="item"
+                    :label="item"
+                    :value="item"
+                  />
+                </el-select>
+              </div>
+            </div>
           </div>
         </div>
       </div>
@@ -48,13 +103,16 @@
 </template>
 
 <script setup>
-import { reactive, onMounted, ref, watch } from 'vue'
+import { reactive, onMounted, ref, watch, computed } from 'vue'
 import { ElMessage } from 'element-plus'
 import client from "@/stores/modules/client";
 import icpList from '@/utils/ipc';
 import socket from "@/stores/modules/socket.js";
+import useUserInfo from '@/stores/modules/user';
+
 const clientStore = client();
-const socketStore = socket(); // WebSocket状态管理实例
+const socketStore = socket();
+const userInfoStore = useUserInfo();
 
 // 定义props
 const props = defineProps({
@@ -67,30 +125,186 @@ const props = defineProps({
 // 定义emits
 const emit = defineEmits(['update:camera_configs'])
 
-const iso_config = reactive({
-  low: 100,
-  high: 6000,
-  mode: "auto22"
-})
+// 是否为多相机模式
+const isMultiCameraMode = computed(() => userInfoStore.isMultiCameraMode)
+
+// 当前选中的点位
+const activePoint = ref('A')
 
 const iso_options = ref(['auto', 100, 200, 400, 800, 1600, 3200, 6400, 12800])
 
-// 监听iso_config变化并更新父组件
-watch(iso_config, (newVal) => {
+// 相机列表(通过 IPC 获取,不再依赖 store)
+const cameraList = ref([])
+
+// 点位列表
+const pointList = [
+  { key: 'A', connected: false },
+  { key: 'B', connected: false },
+  { key: 'C', connected: false },
+]
+
+// 多相机模式的配置结构(统一数据结构)
+const multiConfig = reactive({
+  A: {
+    iso: { low: '100', high: '6400' },
+    CameraKey: '',
+    CameraName: ''
+  },
+  B: {
+    iso: { low: '100', high: '6400' },
+    CameraKey: '',
+    CameraName: ''
+  },
+  C: {
+    iso: { low: '100', high: '6400' },
+    CameraKey: '',
+    CameraName: ''
+  }
+})
+
+// 根据模式显示不同的点位列表
+const displayPointList = computed(() => {
+  if (isMultiCameraMode.value) {
+    return pointList
+  } else {
+    // 单相机模式只显示点位A
+    return pointList.filter(p => p.key === 'A')
+  }
+})
+
+// 其他点位(用于多相机模式预览)
+// const otherPoints = computed(() => {
+//   return pointList.filter(p => p.key !== activePoint.value)
+// })
+
+// 从本地 cameraList 获取可用相机列表(过滤掉已被其他点位绑定的相机)
+const availableCameras = computed(() => {
+  const cameras = cameraList.value || []
+  if (!isMultiCameraMode.value) {
+    return cameras
+  }
+  // 找出当前点位绑定的相机key
+  const currentBoundCameraKey = multiConfig[activePoint.value].CameraKey
+  // 返回未绑定或当前点位已绑定的相机
+  return cameras.filter(cam => {
+    // 如果是当前点位已绑定的相机,允许显示
+    if (cam.CameraKey === currentBoundCameraKey) {
+      return true
+    }
+    // 检查是否被其他点位绑定
+    const isBoundByOtherPoint = Object.entries(multiConfig).some(([key, config]) => {
+      return key !== activePoint.value && config.CameraKey === cam.CameraKey
+    })
+    return !isBoundByOtherPoint
+  })
+})
+
+// 切换点位
+const switchPoint = (pointKey) => {
+  activePoint.value = pointKey
+}
+
+// 更新点位连接状态
+const updatePointConnectionStatus = () => {
+  const cameras = cameraList.value || []
+  pointList.forEach(point => {
+    const boundCamera = multiConfig[point.key].CameraKey
+    if (boundCamera) {
+      const camera = cameras.find(c => c.CameraKey === boundCamera)
+      point.connected = camera ? camera.CameraStatus : false
+    } else {
+      point.connected = false
+    }
+  })
+}
+
+// 通过 IPC 获取相机列表
+const fetchCameraList = () => {
+  clientStore.ipc.invoke(icpList.camera.getCameraList).then(result => {
+    if (result && result.CameraLists && Array.isArray(result.CameraLists)) {
+      cameraList.value = result.CameraLists
+      // 自动分配相机到点位
+      if (isMultiCameraMode.value) {
+        autoAssignCameras()
+      }
+      updatePointConnectionStatus()
+    }
+  }).catch(err => {
+    console.error('获取相机列表失败:', err)
+  })
+}
+
+// 自动分配相机到点位
+const autoAssignCameras = () => {
+  const cameras = cameraList.value || []
+  const unassignedCameras = cameras.filter(cam => {
+    return !Object.values(multiConfig).some(p => p.CameraKey === cam.CameraKey)
+  })
+  const points = ['A', 'B', 'C']
+  unassignedCameras.slice(0, 3).forEach((cam, index) => {
+    if (points[index]) {
+      multiConfig[points[index]].CameraKey = cam.CameraKey
+      multiConfig[points[index]].CameraName = cam.CameraName
+    }
+  })
+}
+
+// 相机选择变更
+const onCameraChange = (pointKey) => {
+  const cameraKey = multiConfig[pointKey].CameraKey
+  if (cameraKey) {
+    const camera = cameraList.value.find(c => c.CameraKey === cameraKey)
+    if (camera) {
+      multiConfig[pointKey].CameraName = camera.CameraName
+    }
+  } else {
+    // 清除绑定时也清空 CameraName
+    multiConfig[pointKey].CameraName = ''
+  }
+  updatePointConnectionStatus()
+}
+
+// 监听multiConfig变化并更新父组件
+watch(multiConfig, (newVal) => {
   emit('update:camera_configs', {
-    iso_config,
+    iso_config: multiConfig,
   })
 }, { deep: true })
 
+// 初始化数据
 onMounted(() => {
-  // 从props初始化数据
-  if (props.camera_configs.iso_config.low !== undefined) {
-    iso_config.low = props.camera_configs.iso_config.low
-  }
-  if (props.camera_configs.iso_config.high !== undefined) {
-    iso_config.high = props.camera_configs.iso_config.high
+  // 解析配置数据
+  if (props.camera_configs.iso_config) {
+    const config = props.camera_configs.iso_config
+
+    // 判断是旧格式还是新格式
+    if (config.low !== undefined || config.high !== undefined) {
+      // 旧格式(单相机旧数据) -> 转换为新格式
+      multiConfig.A.iso.low = String(config.low || '100')
+      multiConfig.A.iso.high = String(config.high || '6400')
+    } else {
+      // 新格式 - A/B/C 结构
+      Object.keys(config).forEach(key => {
+        if (multiConfig[key] !== undefined) {
+          if (config[key].iso) {
+            multiConfig[key].iso.low = config[key].iso.low || '100'
+            multiConfig[key].iso.high = config[key].iso.high || '6400'
+          }
+          if (config[key].CameraKey) {
+            multiConfig[key].CameraKey = config[key].CameraKey
+            multiConfig[key].CameraName = config[key].CameraName || ''
+          }
+        }
+      })
+    }
   }
 
+  // 单相机模式默认选中点位A
+  activePoint.value = 'A'
+
+  // 通过 IPC 获取相机列表
+  fetchCameraList()
+
   // 读取设备当前可用的 ISO 档位
   clientStore.ipc.removeAllListeners(icpList.socket.message + '_smart_shooter_get_camera_property');
   socketStore.sendMessage({
@@ -109,77 +323,140 @@ onMounted(() => {
 })
 
 // 暴露保存方法,给父组件调用
-const save =  () => {
-  // 必填校验(允许填写数字或 'auto')
+const save = () => {
+  // 校验当前点位配置
+  const current = multiConfig[activePoint.value]
   const isEmpty = (v) => v === undefined || v === null || v === ''
-  if (isEmpty(iso_config.low)) {
-    ElMessage.error('请填写“用曝光灯时”的 ISO 值')
+
+  if (isEmpty(current.iso.low)) {
+    ElMessage.error('请选择拍照ISO')
     return false
   }
-  if (isEmpty(iso_config.high)) {
-    ElMessage.error('请填写“不用时”的 ISO 值')
+  if (isEmpty(current.iso.high)) {
+    ElMessage.error('请选择预览ISO')
     return false
   }
 
-  // 若均为数字,做简单范围校验(>0)
-  const lowNum = Number(iso_config.low)
-  const highNum = Number(iso_config.high)
+  const lowNum = Number(current.iso.low)
+  const highNum = Number(current.iso.high)
   if (!Number.isNaN(lowNum) && lowNum <= 0) {
-    ElMessage.error('“用曝光灯时”的 ISO 必须大于 0')
+    ElMessage.error('拍照ISO必须大于0')
     return false
   }
   if (!Number.isNaN(highNum) && highNum <= 0) {
-    ElMessage.error('“不用时”的 ISO 必须大于 0')
+    ElMessage.error('预览ISO必须大于0')
     return false
   }
 
-  return  true;
-/*  return await new Promise((resolve, reject) => {
-    clientStore.ipc.removeAllListeners(icpList.setting.updateSysConfigs);
-    clientStore.ipc.send(icpList.setting.updateSysConfigs,{
-      key: 'camera_configs',
-      value: JSON.stringify({
-        iso_low: iso_config.low,
-        iso_high: iso_config.high
-      })
-    });
-
-    clientStore.ipc.on(icpList.setting.updateSysConfigs, async (event, result) => {
-      clientStore.ipc.removeAllListeners(icpList.setting.updateSysConfigs);
-      if(result.code === 0 && result.msg){
-        resolve(true)
-      } else {
-        resolve(false)
-      }
-    });
-  });*/
+  return true
 }
 
 defineExpose({ save })
-
 </script>
 
 <style lang="scss" scoped>
-.iso-inputs {
-  display: flex;
-  flex-direction: column;
-  gap: 16px;
+.selectBox {
+  padding-top: 10px;
+}
+
+.multi-camera-config {
+  padding-top: 20px;
+  padding-left: 100px;
 }
 
-.iso-group {
+// 点位切换Tab
+.point-tabs {
   display: flex;
-  align-items: center;
   gap: 12px;
+  margin-bottom: 20px;
 }
 
-.iso-label {
-  min-width: 80px;
-  font-size: 14px;
-  color: #1A1A1A;
+.point-tab {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  padding: 10px 20px;
+  background: #F5F7FA;
+  border: 2px solid #E4E7ED;
+  border-radius: 8px;
+  cursor: pointer;
+  transition: all 0.3s;
+
+  &:hover {
+    border-color: #C0C4CC;
+  }
+
+  &.active {
+    background: #ECF5FF;
+    border-color: #409EFF;
+  }
+
+  .tab-name {
+    font-weight: bold;
+    color: #303133;
+  }
 }
 
-.iso-input {
-  width: 200px;
+// 当前点位配置卡片
+.current-point-config {
+  background: #fff;
+  border: 1px solid #E4E7ED;
+  border-radius: 12px;
+  overflow: hidden;
+  max-width: 500px;
+
+  .config-header {
+    padding: 16px 20px;
+    background: #F5F7FA;
+    border-bottom: 1px solid #E4E7ED;
+
+    .config-title {
+      font-weight: bold;
+      font-size: 16px;
+      color: #303133;
+    }
+  }
+
+  .config-body {
+    padding: 24px 20px;
+
+    .form-item {
+      margin-bottom: 20px;
+
+      label {
+        display: block;
+        margin-bottom: 8px;
+        font-size: 14px;
+        color: #606266;
+      }
+
+      .camera-select-wrapper {
+        max-width: 300px;
+      }
+    }
+
+    .iso-section {
+      display: flex;
+      gap: 20px;
+      flex-wrap: wrap;
+    }
+
+    .iso-group {
+      display: flex;
+      align-items: center;
+      gap: 12px;
+
+      .iso-label {
+        min-width: 70px;
+        font-size: 14px;
+        color: #1A1A1A;
+      }
+
+      .iso-select {
+        width: 150px;
+      }
+    }
+  }
 }
 
 .select-wrapper {
@@ -187,7 +464,12 @@ defineExpose({ save })
     border-radius: 6px;
   }
 }
-.selectBox {
-  padding-top: 10px;
+
+:deep(.el-select) {
+  width: 100%;
+}
+
+:deep(.camera-select-wrapper .el-select) {
+  width: 300px;
 }
 </style>