Browse Source

feat(setting): 新增相机配置功能并优化设置模块

- 新增相机配置页面,支持 ISO 参数设置
- 在设置导航中添加相机配置选项
- 优化设置模块代码结构,提高可维护性- 添加 ISO 参数校验逻辑- 实现相机配置的保存和加载功能
panqiuyao 3 months ago
parent
commit
2a29d55803

+ 2 - 1
electron/utils/socket.js

@@ -14,7 +14,8 @@ const typeToMessage = {
   set_deviation:"developer",
   get_mcu_other_info:"developer",
   set_mcu_other_info:"developer",
-  send_command:"developer"
+  send_command:"developer",
+  smart_shooter_get_camera_property:"seeting",
 }
 
 

+ 174 - 0
frontend/src/views/Setting/components/CameraConfig.vue

@@ -0,0 +1,174 @@
+<template>
+  <div class="selectBox">
+    <div class="form-item" style="padding-bottom: 30px;">
+      <div class="te-l fw-b fs-16">ISO参数(相机设置ISO auto时无效)</div>
+      <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>
+        </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>
+        </div>
+      </div>
+    </div>
+
+  </div>
+</template>
+
+<script setup>
+import { reactive,onMounted,ref } 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";
+const clientStore = client();
+const socketStore = socket(); // WebSocket状态管理实例
+
+const iso_config = reactive({
+  low: 100,
+  high: 6000,
+  mode:"auto22"
+})
+
+const iso_options = ref(['auto',100,200,400,800,1600,3200,6400,12800])
+
+onMounted( () => {
+  // 读取已保存的相机配置
+  clientStore.ipc.removeAllListeners(icpList.setting.getSysConfig);
+  clientStore.ipc.send(icpList.setting.getSysConfig,{key: 'camera_configs'});
+  clientStore.ipc.on(icpList.setting.getSysConfig, (event, result) => {
+    if(result.code == 0 && result.data){
+      iso_config.low = result.data.iso_low ?? iso_config.low
+      iso_config.high = result.data.iso_high ?? iso_config.high
+    }
+    clientStore.ipc.removeAllListeners(icpList.setting.getSysConfig);
+  });
+
+  // 读取设备当前可用的 ISO 档位
+  clientStore.ipc.removeAllListeners(icpList.socket.message+'_smart_shooter_get_camera_property');
+  socketStore.sendMessage({
+    type: 'smart_shooter_get_camera_property'
+  });
+
+  clientStore.ipc.on(icpList.socket.message+'_smart_shooter_get_camera_property', async (event, result) => {
+    if(result.code == 0 && result.data){
+      const ISO  =  result.data.filter(item => item.CameraPropertyType == 'ISO')
+      if(ISO.length > 0){
+        iso_options.value = ISO[0].CameraPropertyRange
+      }
+    }
+    clientStore.ipc.removeAllListeners(icpList.socket.message+'_smart_shooter_get_camera_property');
+  })
+})
+
+// 暴露保存方法,给父组件调用
+const save = async () => {
+  // 必填校验(允许填写数字或 'auto')
+  const isEmpty = (v) => v === undefined || v === null || v === ''
+  if (isEmpty(iso_config.low)) {
+    ElMessage.error('请填写“用曝光灯时”的 ISO 值')
+    return false
+  }
+  if (isEmpty(iso_config.high)) {
+    ElMessage.error('请填写“不用时”的 ISO 值')
+    return false
+  }
+
+  // 若均为数字,做简单范围校验(>0)
+  const lowNum = Number(iso_config.low)
+  const highNum = Number(iso_config.high)
+  if (!Number.isNaN(lowNum) && lowNum <= 0) {
+    ElMessage.error('“用曝光灯时”的 ISO 必须大于 0')
+    return false
+  }
+  if (!Number.isNaN(highNum) && highNum <= 0) {
+    ElMessage.error('“不用时”的 ISO 必须大于 0')
+    return false
+  }
+
+  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)
+      }
+    });
+  });
+}
+
+defineExpose({ save })
+
+</script>
+
+<style lang="scss" scoped>
+.iso-inputs {
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+}
+
+.iso-group {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+}
+
+.iso-label {
+  min-width: 80px;
+  font-size: 14px;
+  color: #1A1A1A;
+}
+
+.iso-input {
+  width: 200px;
+}
+
+.select-wrapper {
+  :deep(.el-input__inner) {
+    border-radius: 6px;
+  }
+}
+</style>

+ 65 - 45
frontend/src/views/Setting/index.vue

@@ -10,6 +10,11 @@
         <img src="@/assets/images/setting/icon1a.png" class="nav-icon" v-else/>
         <span>基础配置</span>
       </div>
+      <div class="nav-item" v-if="configInfoStore.appModel === 1" :class="{'active': activeIndex === 3}" @click="toggleTab(3)" v-log="{ describe: { action: '点击切换设置Tab', tab: '相机配置' } }">
+        <img src="@/assets/images/setting/icon2.png" class="nav-icon" v-if="activeIndex !== 3"/>
+        <img src="@/assets/images/setting/icon2a.png" class="nav-icon" v-else/>
+        <span>相机配置</span>
+      </div>
       <div class="nav-item" :class="{'active': activeIndex === 2}" @click="toggleTab(2)" v-log="{ describe: { action: '点击切换设置Tab', tab: '其他设置' } }">
         <img src="@/assets/images/setting/icon3.png" class="nav-icon" v-if="activeIndex !== 2"/>
         <img src="@/assets/images/setting/icon3a.png" class="nav-icon" v-else/>
@@ -71,47 +76,53 @@
                <DebugPanel ref="debugPanel" />
         </div>
       <!--基础配置-->
+      <!--相机配置-->
+      <template v-if="activeIndex === 3">
+      <CameraConfig ref="cameraConfigRef"/>
+
+      </template>
+      <!--相机配置-->
       <!--其他设置-->
-          <template v-if="activeIndex === 2">
-
-            <div class="selectBox" style="padding-top: 0px" >
-              <div class="form-item">
-                <label>产品类型:</label>
-                <div class="select-wrapper">
-                  <el-select v-model="formData.other_configs.product_type" placeholder="请选择">
-                    <el-option v-for="item in productTypeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
-                  </el-select>
-                </div>
-              </div>
-              <div class="form-item">
-                <label>默认抠图模式:</label>
-                <div class="select-wrapper">
-                  <el-select v-model="formData.other_configs.cutout_mode" placeholder="请选择">
-                    <el-option v-for="item in defaultCutoutModeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
-                  </el-select>
-                </div>
-              </div>
-              <div class="form-item">
-                <label>设备运动速度:</label>
-                <div class="select-wrapper">
-                  <el-select v-model="formData.other_configs.device_speed" placeholder="请选择">
-                    <el-option v-for="item in deviceSpeedList" :key="item.value" :label="item.label" :value="item.value"></el-option>
-                  </el-select>
-                </div>
-              </div>
-
-              <!--                <div class="form-item">
-                                  <label>运行模式:</label>
-                                  <div class="select-wrapper">
-                                  <el-select v-model="formData.other_configs.running_mode" placeholder="请选择">
-                                    <el-option v-for="item in runModeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
-                                  </el-select>
-                                  </div>
-                              </div>-->
+      <template v-if="activeIndex === 2">
+
+        <div class="selectBox" style="padding-top: 0px" >
+          <div class="form-item">
+            <label>产品类型:</label>
+            <div class="select-wrapper">
+              <el-select v-model="formData.other_configs.product_type" placeholder="请选择">
+                <el-option v-for="item in productTypeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
+              </el-select>
+            </div>
+          </div>
+          <div class="form-item">
+            <label>默认抠图模式:</label>
+            <div class="select-wrapper">
+              <el-select v-model="formData.other_configs.cutout_mode" placeholder="请选择">
+                <el-option v-for="item in defaultCutoutModeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
+              </el-select>
             </div>
+          </div>
+          <div class="form-item">
+            <label>设备运动速度:</label>
+            <div class="select-wrapper">
+              <el-select v-model="formData.other_configs.device_speed" placeholder="请选择">
+                <el-option v-for="item in deviceSpeedList" :key="item.value" :label="item.label" :value="item.value"></el-option>
+              </el-select>
+            </div>
+          </div>
+
+          <!--                <div class="form-item">
+                              <label>运行模式:</label>
+                              <div class="select-wrapper">
+                              <el-select v-model="formData.other_configs.running_mode" placeholder="请选择">
+                                <el-option v-for="item in runModeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
+                              </el-select>
+                              </div>
+                          </div>-->
+        </div>
 
-            <other-config/>
-          </template>
+        <other-config/>
+      </template>
       <!--其他设置-->
           <div class="selectBox" style="padding-top: 0px;padding-left: 0;" v-if="activeIndex === 4">
             <actionConfig/>
@@ -148,6 +159,7 @@ import { useCheckInfo } from '@/composables/userCheck';
 import { preview } from '@planckdev/element-plus/utils'
 import actionConfig from './components/action_config.vue'
 import otherConfig from './components/otherConfig'
+import CameraConfig from './components/CameraConfig';
 useCheckInfo();
 
 //点击三次  打开资源目录
@@ -155,6 +167,7 @@ useCheckInfo();
 import DebugPanel from './components/DebugPanel.vue';
 // 在setup函数中创建调试面板实例
 const debugPanel = ref(null);
+const cameraConfigRef = ref(null);
 // 添加设置点击计数器
 const settingClickCount = ref(0);
 
@@ -336,7 +349,7 @@ watch(() => route.query.type, async (newType,oldType) => {
  //   await  saveSetting(oldType)
   }
   const typeValue = parseInt(newType) || 0;
-  if(typeValue === 4) return;
+  if([3,4].includes(typeValue)) return;
   switch (typeValue) {
       default:
         clientStore.ipc.removeAllListeners(icpList.setting.getSysConfig);
@@ -414,13 +427,14 @@ const handleInput = (value) => {
 };
 
 const toggleTab = async (item) => {
-  let oldType = activeIndex.value;
-
-  if([0,1,2].includes(oldType)){
-   const next =  await  saveSetting(oldType)
-    if(next === false) return ;
+  const oldType = activeIndex.value;
+  // 切换前保存当前 Tab 配置(包含相机配置 3)
+  if ([0,1,2,3].includes(oldType)) {
+    const next = await saveSetting(oldType);
+    if (next === false) return false;
   }
-   activeIndex.value = item;
+  activeIndex.value = item;
+  return true;
 };
 
 const onSava = async (index)=>{
@@ -437,6 +451,12 @@ const onSava = async (index)=>{
  */
 const saveSetting = async (index) => {
   // 构建临时提交数据
+  if(index === 3) {
+    if (cameraConfigRef.value && typeof cameraConfigRef.value.save === 'function') {
+      return await cameraConfigRef.value.save()
+    }
+    return false
+  }
   const submitData = {
     ...formData[indexKey[index]]
   };

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "ZhiHuiYin",
-  "version": "1.2.0",
+  "version": "1.2.3",
   "description": "1、支持SmartShooter5软件控制相机",
   "main": "main.js",
   "scripts": {

+ 1 - 1
public/dist/index.html

@@ -5,7 +5,7 @@
     <link rel="icon" type="image/svg+xml" href="./vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>智惠映AI自动拍照机</title>
-    <script type="module" crossorigin src="./assets/index-Dx7tl9Wa.js"></script>
+    <script type="module" crossorigin src="./assets/index-CNEyPYnH.js"></script>
     <link rel="stylesheet" crossorigin href="./assets/index-CWbjMgyJ.css">
   </head>
   <body>