Quellcode durchsuchen

Merge branch 'dev-frontend' of liangyibo/CameraMachine into master

潘求垚 vor 8 Monaten
Ursprung
Commit
66dde38e7c

+ 19 - 14
electron/api/camera.js

@@ -4,14 +4,14 @@ const { net } = require('electron');
 
 //
 const baseURL = 'http://localhost:5513/';
-// 创建 Axios 实例
+// 鍒涘缓 Axios 瀹炰緥
 const service = axios.create({
   baseURL:baseURL,
   timeout: 60000,
 });
 
 
-// 封装 GET 方法
+// 灏佽� GET 鏂规硶
 function get(config = { url: '' }) {
   return service.get(config.url, {
     insecureHTTPParser: true,
@@ -64,11 +64,16 @@ module.exports = {
       url: '?CMD=LiveViewWnd_Hide'
     })
   },
-  capture(){
+  captureLive(){
     return get({
       url: '?CMD=LiveView_Capture'
     })
   },
+  capture(){
+    return get({
+      url: '?CMD=Capture'
+    })
+  },
 
   CMD(cmd){
     return get({
@@ -90,16 +95,16 @@ module.exports = {
 
 /*
 *
-* 设置   iso
-* 光圈  aperture
-* 拍摄模式  mode
-* 快门速度  shutterSpeed
-* 白平衡    whitebalance
-* 曝光补偿  ExposureCompensation
-* 对焦模式 focusmode
+* 璁剧疆   iso
+* 鍏夊湀  aperture
+* 鎷嶆憚妯″紡  mode
+* 蹇�棬閫熷害  shutterSpeed
+* 鐧藉钩琛�    whitebalance
+* 鏇濆厜琛ュ伩  ExposureCompensation
+* 瀵圭劍妯″紡 focusmode
 *
-*folder 文件夹
-曝光度:exposure
-压缩:compression
-测光点:metering
+*folder 鏂囦欢澶�
+鏇濆厜搴︼細exposure
+鍘嬬缉锛歝ompression
+娴嬪厜鐐癸細metering
 * */

+ 16 - 7
electron/controller/camera.js

@@ -4,7 +4,7 @@ const path = require('path');
 const fs = require('fs');
 const { Controller } = require('ee-core');
 const { spawn } = require('child_process');
-const { liveShow, liveHide, setParams, capture, getParams,CMD } = require('../api/camera');
+const { liveShow, liveHide, setParams, capture, getParams,CMD,captureLive } = require('../api/camera');
 const { dialog } = require('electron'); // 引入 electron 的 dialog 模块
 const { windowManager } = require('node-window-manager');
 // 检查并读取配置文件
@@ -129,7 +129,7 @@ function closeCameraControlTips() {
 
 
 }
-
+let  isOPen = false
 class CameraController extends Controller {
   constructor(ctx) {
     super(ctx);
@@ -137,9 +137,12 @@ class CameraController extends Controller {
 
   async connect() {
     try {
-      await checkCameraControlCmdExists()
-      await  CMD('All_Minimize')
-      closeCameraControlTips()
+      if(!isOPen){
+        await checkCameraControlCmdExists()
+        await  CMD('All_Minimize')
+        closeCameraControlTips()
+        isOPen = true
+      }
 
       const res = await getParams('iso')
       if(res  === '未将对象引用设置到对象的实例。'){
@@ -200,9 +203,15 @@ class CameraController extends Controller {
     }
   }
 
-  async takePictures() {
+  async takePictures(isLive=true) {
     try {
-      await capture();
+      if(isLive){
+
+        await captureLive();
+      }else{
+
+        await capture();
+      }
       return true;
     } catch (error) {
       throw error;

+ 11 - 1
electron/controller/socket.js

@@ -6,6 +6,12 @@ const CoreWindow = require('ee-core/electron/window');
 const WebSocket = require('ws'); // 引入原生 ws 库
 let socket = null;
 const { pyapp } = require('../config/app.config.json')
+
+const typeToMessage = {
+  run_mcu_single_finish:"seeting",
+  get_deviation_data:"developer",
+  set_deviation:"developer",
+}
 class SocketController extends Controller {
   constructor(ctx) {
     super(ctx);
@@ -43,7 +49,11 @@ class SocketController extends Controller {
           console.log(this_data);
           if(this_data.msg_type){
             let channel = 'controller.socket.message_'+this_data.msg_type;
-            win.webContents.send(channel, this_data);
+            if(typeToMessage[this_data.msg_type]){
+              this.app.electron[typeToMessage[this_data.msg_type]].webContents.send(channel, this_data);
+            }else{
+              win.webContents.send(channel, this_data);
+            }
           }
         }catch (e){
           console.log(e)

+ 1 - 0
frontend/package.json

@@ -10,6 +10,7 @@
   },
   "dependencies": {
     "@element-plus/icons-vue": "2.3.1",
+    "@planckdev/element-plus": "0.0.0-rc.6",
     "@types/axios": "0.14.4",
     "axios": "1.8.3",
     "crypto-js": "4.1.1",

+ 23 - 8
frontend/src/components/check/index.vue

@@ -54,7 +54,7 @@
             <div class="check-btn cu-p" @click="reCheck">重新监测</div>
         </div>
         <div class="flex" v-else>
-            <div class="check-btn cu-p" style="width: 180px" @click="confirm()">正常,开始下一步</div>
+            <div class="check-btn cu-p" style="width: 180px" @click="confirm()">检测成功,继续操作!</div>
         </div>
     </template>
   </el-dialog>
@@ -74,7 +74,7 @@ const clientStore = client();
  * @param {String} props.title - 对话框标题,默认为 '检测硬件'。
  */
 const props = defineProps({
-  modelValue: {
+  isInitCheck: {
     type: Boolean,
     default: false
   },
@@ -96,10 +96,10 @@ const checkSuccess = ref(false);
 const checkLoading = ref(true);
 
 // 定义事件发射器,用于更新父组件的 modelValue 和触发 confirm 事件
-const emit = defineEmits(['update:modelValue', 'confirm']);
+const emit = defineEmits([ 'confirm']);
 
 // 检测次数计数器
-const checkCount = ref(0);
+const checkCount = ref(props.isInitCheck ? 0 : 1);
 
 // 进度条的当前进度值
 
@@ -115,7 +115,9 @@ const visible = ref(false);
  * 该函数会重置进度条并启动定时器,逐步增加进度值直到达到 80。
  */
 function startProgress() {
+  checkInfoStore.set_isCheckStatus(true);
   checkLoading.value = true;
+  checkSuccess.value = false;
   checkInfoStore.reCheckAction()
 }
 
@@ -136,19 +138,30 @@ function reCheck() {
  * 并尝试连接 WebSocket 和发送消息。
  */
 watchEffect(async ()=>{
-  if( useUserInfoStore.userInfo.id && checkCount.value === 0){
+  if( useUserInfoStore.userInfo.id && checkCount.value === 0 && checkInfoStore.isCheckStatus){
     if(clientStore.isClient){
       visible.value = true
       //python 启动有延时,延迟2秒执行
       setTimeout(()=>{
-
         startProgress()
-      },2000)
+      },5000)
     }
     checkCount.value++;
   }
 })
 
+
+watchEffect(async ()=>{
+  if( checkCount.value >= 1 && checkInfoStore.getProgress !== 100 && !checkInfoStore.isCheckStatus){
+    localStorage.setItem('check',false)
+    visible.value = true
+    checkLoading.value = false;
+    checkSuccess.value = false;
+  }
+})
+
+
+
 watchEffect(async ()=>{
   if(checkInfoStore.getProgress === 100 ){
     checkLoading.value = false;
@@ -167,7 +180,7 @@ watchEffect(async ()=>{
 })
 
 watchEffect(async ()=>{
-  if( checkInfoStore.getProgress   === 1){
+  if( checkInfoStore.getProgress   === 100){
     checkLoading.value = false;
     checkSuccess.value = true;
   }
@@ -176,6 +189,8 @@ watchEffect(async ()=>{
 
 function confirm(){
   visible.value = false;
+  checkInfoStore.set_isCheckStatus(false);
+  localStorage.setItem('check',true)
   emit('confirm')
 }
 

+ 73 - 8
frontend/src/components/header-bar/index.vue

@@ -9,13 +9,21 @@
     <div class="header-bar__title">
       <span class="header-bar__text">{{ title }}</span>
     </div>
-    <div class="header-bar__buttons">
-<!--      <div class="header-bar__button" @click="minimizeWindow">
-        <img :src="iconMinimize" class="header-bar__button-icon" />
+    <div class="header-bar__buttons" >
+      <div class="header-bar__button header-bar__button__user" v-if="showUser">
+
+
+        <el-dropdown>
+          <span class="el-dropdown-link">
+           {{useUserInfoStore.userInfo.account_name || useUserInfoStore.userInfo.real_name || useUserInfoStore.userInfo.login_name}}
+          </span>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <el-dropdown-item @click="loginOut">退出登录</el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
       </div>
-      <div class="header-bar__button" @click="closeWindow">
-        <img :src="iconClose" class="header-bar__button-icon" />
-      </div>-->
     </div>
   </div>
   <div class="header-bar_blank"></div>
@@ -23,6 +31,7 @@
 
 <script setup lang="ts">
 import { defineProps, reactive } from 'vue';
+import useUserInfo from "@/stores/modules/user";
 import { useRouter} from "vue-router";
 import iconsz from './assets/shezhi@2x.png'
 import iconykq from './assets/yaokong@2x.png'
@@ -32,6 +41,7 @@ import icpList from '@/utils/ipc'
 import { getRouterUrl } from '@/utils/appfun'
 import client from "@/stores/modules/client";
 const clientStore = client();
+const useUserInfoStore = useUserInfo();
 
 // 定义 menu 项的类型
 interface MenuItem {
@@ -50,6 +60,10 @@ const props = defineProps({
   menu: {
     type: Array as () => MenuItem[],
     default: () => []
+  },
+  showUser:{
+    type: Boolean,
+    default: false
   }
 });
 const Router = useRouter()
@@ -62,7 +76,12 @@ const menuType = reactive({
   remoteControl: {
     name: '模拟遥控器',
     icon: iconykq,
-    click: openSetting
+    click: openRemoteControl
+  },
+  developer: {
+    name: '开发者设置',
+    icon: iconykq,
+    click: openDeveloper
   }
 });
 
@@ -104,6 +123,49 @@ function openSetting() {
   clientStore.ipc.send(icpList.utils.openMain, params);
 }
 
+
+function openRemoteControl(){
+
+  const { href } = Router.resolve({
+    name: 'RemoteControl',
+  })
+
+  clientStore.ipc.removeAllListeners(icpList.utils.openMain);
+  let params = {
+    title: '模拟遥控器',
+    width: 350,
+    height: 600,
+    frame: true,
+    id: "RemoteControl",
+    url: getRouterUrl(href)
+  }
+  clientStore.ipc.send(icpList.utils.openMain, params);
+}
+
+
+function openDeveloper(){
+
+  const { href } = Router.resolve({
+    name: 'developer',
+  })
+
+  clientStore.ipc.removeAllListeners(icpList.utils.openMain);
+  let params = {
+    title: '开发者设置',
+    width: 900,
+    height: 620,
+    frame: true,
+    id: "developer",
+    url: getRouterUrl(href)
+  }
+  clientStore.ipc.send(icpList.utils.openMain, params);
+}
+
+function loginOut(){
+  useUserInfoStore.loginOut();
+  useUserInfoStore.updateLoginShow(true)
+}
+
 // 新增
 function minimizeWindow() {
   clientStore.ipc.send(icpList.utils.minimizeWindow);
@@ -196,7 +258,7 @@ function closeWindow() {
 }
 
 .header-bar__button {
-  width: 30px;
+  min-width: 30px;
   height: 30px;
   border: none;
   cursor: pointer;
@@ -205,6 +267,9 @@ function closeWindow() {
   justify-content: center;
 }
 
+.header-bar__button__user {
+  padding: 0 10px;
+}
 .header-bar__button:hover {
   background-color: #e0e0e0;
 }

+ 28 - 0
frontend/src/composables/userCheck.ts

@@ -0,0 +1,28 @@
+
+import { ElMessageBox  } from 'element-plus';
+export function useCheckInfo() {
+
+        if(localStorage.getItem('check') === 'false'){
+            ShowError()
+        }
+        window.addEventListener('storage',(e)=>{
+            if(e.key === 'check' && e.newValue === false && e.oldValue === true){
+                ShowError()
+            }
+        })
+        function ShowError(){
+
+            ElMessageBox({
+                title:"链接出错!",
+                message:'设备连接出错,请在主窗口中重新连接设备后,在重新打开此窗口后进行操作',
+                showCancelButton:false,
+                showConfirmButton:false,
+                closeOnClickModal:false,
+                closeOnPressEscape:false,
+                closeOnHashChange:false,
+                showClose:false
+            })
+        }
+
+
+}

+ 16 - 0
frontend/src/router/index.ts

@@ -55,6 +55,22 @@ const routes: RouteRecordRaw[] = [
             title: '详情高级配置'
         }
     },
+    {
+        path: "/remote_control",
+        name: "RemoteControl",
+        component: () => import("@/views/RemoteControl/index.vue"),
+        meta: {
+            title: '遥控器'
+        }
+    },
+    {
+        path: "/developer",
+        name: "developer",
+        component: () => import("@/views/Developer/index.vue"),
+        meta: {
+            title: '遥控器'
+        }
+    },
 ];
 
 const router = createRouter({

+ 43 - 10
frontend/src/stores/modules/check.ts

@@ -3,7 +3,7 @@ import { ref, reactive, computed } from 'vue';
 import socket from "./socket";
 import icpList from "../../utils/ipc";
 import client from "./client";
-
+import {ElMessage} from "element-plus";
 const socketStore = socket();
 const clientStore = client();
 
@@ -33,8 +33,20 @@ export const checkInfo = defineStore('checkInfo', () => {
         },*/
     });
 
+    const blue_tooth_scan_NO = ref('')
     const checkTime = ref(0)
     let CKTimerInterval:any = null
+    let CKCamControlInterval:any = null
+    const isCheckStatus = ref(true)
+    const set_isCheckStatus = (value)=>{
+        isCheckStatus.value = value
+        //开始监听相机软件连接状态
+        if(!value){
+            CKCamControlInterval =   setInterval(()=>{
+                checkcamControl()
+            },3000)
+        }
+    }
 
     //mcu 初始化
     const mcu = reactive({
@@ -48,7 +60,8 @@ export const checkInfo = defineStore('checkInfo', () => {
         for (const device of Object.values(devices)) {
             if (device.status === 2) completed++;
         }
-        return parseFloat((completed / total * 100).toFixed(2));
+        let value = parseFloat((completed / total * 100).toFixed(2));
+        return value
     });
 
     // 获取错误信息
@@ -58,7 +71,7 @@ export const checkInfo = defineStore('checkInfo', () => {
             clearInterval(CKTimerInterval)
             checkTime.value = 0
             mcu.isInitSend = false
-            return '相机初始化失败,请重新监测或强制初始化!';
+            return '拍照机初始化失败,请重新监测!';
         }
         for (const device of Object.values(devices)) {
             if (device.status === -1) {
@@ -137,10 +150,18 @@ export const checkInfo = defineStore('checkInfo', () => {
                             devices[deviceName].status = result.status;
                             devices[deviceName].msg = result.msg;
                         }
-                    }else if([-1,0,2].includes(result.status)){
-                        devices[deviceName].status = result.status;
-                        devices[deviceName].msg = result.msg;
                     }
+                    if(deviceName === 'blue_tooth'){
+                        if (result.code === 0 && result.data?.data && result.data._type === 0 && typeof(result.data.data) === 'string'){
+                            blue_tooth_scan_NO.value = result.data.data
+                       //     ElMessage.success('商品货号'+result.data.data+'获取成功,请根据左右脚按遥控器上的左右脚按键启动拍摄')
+                        }
+                        if([-1,0,2].includes(result.status)){
+                            devices[deviceName].status = result.status;
+                            devices[deviceName].msg = result.msg;
+                        }
+                    }
+
                 }
             });
         } catch (error) {
@@ -155,6 +176,7 @@ export const checkInfo = defineStore('checkInfo', () => {
         for (const deviceName of Object.keys(devices)) {
             switch (deviceName){
                 case 'cam_control':
+                    if(CKCamControlInterval) clearInterval(CKCamControlInterval)
                     await checkcamControl();
                     break;
                 case 'camera':
@@ -183,20 +205,31 @@ export const checkInfo = defineStore('checkInfo', () => {
                     devices.blue_tooth.status = -1;
                     devices.blue_tooth.msg = '遥控器未连接。';
                 }
+                if( devices.mcu.status !== 2 || mcu.status !== 2 ){
+                    devices.mcu.status = -1;
+                    mcu.status = -1;
+                    devices.blue_tooth.msg = '拍照机链接失败';
+                }
             }
         },1000)
         await checkAction();
 
     };
 
+    function set_blue_tooth_scan_NO(value){
+        blue_tooth_scan_NO.value = value
+    }
+
 
     return {
         getProgress,
         getErrorMsg,
-        mcu: devices.mcu,
-        blueTooth: devices.blueTooth,
-        camControl: devices.camControl,
-        camera: devices.camera,
+        devices,
+        mcu,
+        isCheckStatus,
+        set_isCheckStatus,
+        blue_tooth_scan_NO,
+        set_blue_tooth_scan_NO,
         checkAction,
         reCheckAction,
     };

+ 21 - 1
frontend/src/stores/modules/user.ts

@@ -52,7 +52,7 @@ export const useUserInfo = defineStore('userInfo', () => {
     try {
       const res = await login(data); // 调用登录接口
       await updateToken(res.data.token); // 更新登录令牌
-   //   await getInfo(); // 获取用户信息
+      //   await getInfo(); // 获取用户信息
       return res;
     } catch (error) {
       console.error('登录失败:', error);
@@ -60,6 +60,25 @@ export const useUserInfo = defineStore('userInfo', () => {
     }
   };
 
+
+
+  /**
+   * 执行用户登录操作。
+   *
+   * @param {any} data - 登录所需的用户凭据。
+   * @returns {Promise<any>} 登录接口返回的结果。
+   * @throws {Error} 如果登录失败,抛出错误。
+   */
+  const loginOut = async (data: any) => {
+    try {
+      await updateToken(''); // 更新登录令牌
+      await updateUserInfo({})
+    } catch (error) {
+      console.error('登录失败:', error);
+      throw error;
+    }
+  };
+
   /**
    * 获取用户信息并更新状态。
    *
@@ -88,6 +107,7 @@ export const useUserInfo = defineStore('userInfo', () => {
     updateUserInfo,
     updateLoginShow,
     loginAction,
+    loginOut,
     getInfo,
   };
 });

+ 262 - 0
frontend/src/views/Developer/index.vue

@@ -0,0 +1,262 @@
+<template>
+
+
+  <headerBar
+    title="开发者配置"
+  />
+
+  <el-row>
+    <el-col :span="24"><h3>相机设置</h3></el-col>
+  </el-row>
+  <el-row>
+    <el-col :span="6">电机偏移mm:</el-col>
+    <el-col :span="12"><el-input
+        @change="changeNum('相机电机','move_deviation','camera_high_motor_deviation',0, 400)"
+        :min="0" :max="400"
+        :step="1"
+        v-model="editRowData.camera_high_motor_deviation" type="number"/>
+         <div class="error-msg">最小0,最大400</div>
+    </el-col>
+    <el-col :span="6"><el-button
+        @click="changeNum('相机电机','set_deviation','camera_high_motor_deviation',0, 400)"
+    >设定</el-button></el-col>
+  </el-row>
+  <el-row class="mar-top-10">
+    <el-col :span="6">舵机偏移(度):</el-col>
+
+    <el-col :span="12"><el-input
+        @change="changeNum('相机舵机','move_deviation','camera_steering_deviation',-40, 40)"
+        :min="-40" :max="40"
+        :step="0.1"
+        v-model="editRowData.camera_steering_deviation" type="number"/>
+      <div class="error-msg">最小-40,最大40</div>
+    </el-col>
+    <el-col :span="6"><el-button @click="changeNum('相机舵机','set_deviation','camera_steering_deviation',-40, 40)">设定</el-button></el-col>
+  </el-row>
+
+
+
+  <el-row>
+    <el-col :span="24"><h3>转盘设置</h3></el-col>
+  </el-row>
+  <el-row>
+    <el-col :span="6">角度偏移 (度):</el-col>
+    <el-col :span="12"><el-input
+        @change="changeNum('转盘舵机','move_deviation','turntable_steering_deviation',-720, 720)"
+        :min="-720" :max="720"
+        :step="1"
+        v-model="editRowData.turntable_steering_deviation" type="number"/>
+      <div class="error-msg">最小-720,最大720</div>
+    </el-col>
+    <el-col :span="6"><el-button  @click="changeNum('转盘舵机','set_deviation','turntable_steering_deviation',-720, 720)">设定</el-button></el-col>
+  </el-row>
+  <el-row class="mar-top-10">
+    <el-col :span="6">前后偏移:</el-col>
+
+    <el-col :span="12"><el-input
+        @change="changeNum('转盘前后电机','move_deviation','turntable_front_end_deviation',0, 950)"
+        :min="0" :max="950"
+        :step="1"
+        v-model="editRowData.turntable_front_end_deviation" type="number"/>
+      <div class="error-msg">最小0,最大950</div>
+    </el-col>
+    <el-col :span="6"><el-button @click="changeNum('转盘前后电机','set_deviation','turntable_front_end_deviation',0, 950)">设定</el-button></el-col>
+  </el-row>
+
+
+
+  <el-row>
+    <el-col :span="24"><h3>翻版舵机</h3></el-col>
+  </el-row>
+  <el-row>
+    <el-col :span="6">中位:</el-col>
+
+    <el-col :span="12"><el-input
+        @change="changeNum('翻板舵机中位','move_deviation','overturn_steering_middle',0, 180)"
+        :min="0" :max="180"
+        :step="0.5"
+        v-model="editRowData.overturn_steering_middle" type="number"/>
+      <div class="error-msg">最小0,最大180</div>
+    </el-col>
+    <el-col :span="6"><el-button @click="changeNum('翻板舵机中位','set_deviation','overturn_steering_middle',0, 180)">设定</el-button></el-col>
+  </el-row>
+  <el-row class="mar-top-10">
+    <el-col :span="6">高位:</el-col>
+    <el-col :span="12"><el-input
+        @change="changeNum('翻板舵机高位','move_deviation','overturn_steering_high',0, 180)"
+        :min="0" :max="180"
+        :step="0.5"
+        v-model="editRowData.overturn_steering_high" type="number"/>
+      <div class="error-msg">最小0,最大180</div>
+    </el-col>
+    <el-col :span="6"><el-button  @click="changeNum('翻板舵机高位','set_deviation','overturn_steering_high',0, 180)">设定</el-button></el-col>
+  </el-row>
+  <el-row class="mar-top-10">
+    <el-col :span="6">上升速度:</el-col>
+    <el-col :span="12"><el-input
+        @change="changeNum('翻板舵机上升速度','move_deviation','overturn_steering_up_speed',0, 10)"
+        :min="0" :max="10"
+        :step="1"
+        v-model="editRowData.overturn_steering_up_speed" type="number"/>
+      <div class="error-msg">最小0,最大10</div>
+    </el-col>
+    <el-col :span="6"><el-button @click="changeNum('翻板舵机上升速度','set_deviation','overturn_steering_up_speed',0, 10)">设定</el-button></el-col>
+  </el-row>
+  <el-row class="mar-top-10">
+    <el-col :span="6">下降速度:</el-col>
+    <el-col :span="12"><el-input
+        @change="changeNum('翻板舵机下降速度','move_deviation','overturn_steering_down_speed',0, 10)"
+        :min="0" :max="10"
+        :step="1"
+        v-model="editRowData.overturn_steering_down_speed" type="number"/>
+      <div class="error-msg">最小0,最大10</div>
+    </el-col>
+    <el-col :span="6"><el-button  @click="changeNum('翻板舵机下降速度','set_deviation','overturn_steering_down_speed',0, 10)">设定</el-button></el-col>
+  </el-row>
+
+
+  <el-row align="middle" justify="middle" class="mar-top-20">
+    <el-col :span="24">
+      <el-button type="primary" @click="get_deviation">读取偏移量</el-button>
+      <el-button type="primary" @click="connect_mcu__init">设备初始化</el-button>
+    </el-col>
+  </el-row>
+
+
+</template>
+
+<script setup lang="ts">
+
+import {ref,reactive,onMounted} from "vue";
+
+import client from "@/stores/modules/client";
+import  icpList from '@/utils/ipc'
+import socket from "@/stores/modules/socket";
+import {ElMessage} from "element-plus";
+const clientStore = client();
+const socketStore = socket()
+
+
+const editRowData = ref({
+  "camera_high_motor_deviation": '',
+  "camera_steering_deviation": '',
+  "turntable_steering_deviation": '',
+  "turntable_front_end_deviation":'',
+  "overturn_steering_middle": '',
+  "overturn_steering_high": '',
+  "overturn_steering_up_speed": '',
+  "overturn_steering_down_speed": ''
+}); // 当前编辑行的数据
+
+onMounted(()=>{
+  get_deviation()
+})
+
+//获取配置
+async function  get_deviation(){
+  if(clientStore.isClient){
+
+    socketStore.sendMessage({
+      type: 'get_deviation',
+      data:"get_deviation"
+    })
+
+
+    clientStore.ipc.on(icpList.socket.message+'_get_deviation_data', (event, result) => {
+      console.log('_get_deviation_data')
+      console.log(result)
+      if(result.code === 0){
+        editRowData.value.camera_high_motor_deviation = result.data.camera_high_motor_deviation
+        editRowData.value.camera_steering_deviation = result.data.camera_steering_deviation
+        editRowData.value.turntable_steering_deviation = result.data.turntable_steering_deviation
+        editRowData.value.turntable_front_end_deviation = result.data.turntable_front_end_deviation
+
+        editRowData.value.overturn_steering_middle = result.data.overturn_steering_middle
+        editRowData.value.overturn_steering_high = result.data.overturn_steering_high
+        editRowData.value.overturn_steering_up_speed = result.data.overturn_steering_up_speed
+        editRowData.value.overturn_steering_down_speed = result.data.overturn_steering_down_speed
+      }else if(result.msg){
+        ElMessage.error(result.msg)
+      }
+      clientStore.ipc.removeAllListeners(icpList.socket.message+'_get_deviation_data');
+    });
+  }
+}
+
+
+
+//MCU初始化
+async function  connect_mcu__init(){
+  if(clientStore.isClient){
+
+    socketStore.sendMessage({
+      type: 'init_mcu',
+      data:"init_mcu"
+    })
+  }
+}
+
+
+
+
+//设置 移动 调整
+async function changeNum(action_name, type, key, min, max) {
+  if(key && (min || max)){
+    if(editRowData.value[key] < min || editRowData.value[key] > max){
+      if(editRowData.value[key] < min){
+        editRowData.value[key] = min;
+      }else{
+        editRowData.value[key] = max;
+      }
+      ElMessage.error(`${action_name}值应在${min}到${max}之间`);
+      return;
+    }
+  }
+  socketStore.sendMessage({
+    type,
+    data: {
+      action_name,
+      value:Number(editRowData.value[key])
+    }
+  });
+
+  clientStore.ipc.removeAllListeners(icpList.socket.message+'_set_deviation');
+  clientStore.ipc.on(icpList.socket.message+'_set_deviation', (event, result) => {
+    console.log('set_deviation')
+    console.log(result)
+    if(result.code === 0){
+      ElMessage.success(result.msg)
+    }else if(result.msg){
+      ElMessage.error(result.msg)
+    }
+    clientStore.ipc.removeAllListeners(icpList.socket.message+'_get_deviation_data');
+  });
+
+}
+
+
+
+</script>
+
+<style scoped lang="scss">
+.el-col {
+    position: relative;
+  ::v-deep {
+    .error-msg{
+      display: none;
+      position: absolute;
+      top: 41px;
+      top: 28px;
+      left: 8px;
+      z-index: 22;
+      color: #dc2626;
+      font-size: 12px;
+    }
+    &:hover{
+      .error-msg{
+        display: block;
+      }
+    }
+  }
+}
+</style>

+ 22 - 4
frontend/src/views/Home/index.vue

@@ -9,14 +9,16 @@
         },
         {
           type:'remoteControl'
-        },
+        },{
+            type: 'developer'
+        }
     ]"
   />
   <div class="mb-4">
 
     <template v-if="!show">
       <div>{{useUserInfoStore.userInfo.account_name}}</div>
-      <div>{{useUserInfoStore.userInfo.phone}}</div>
+      <div>{{useUserInfoStore.userInfo.brand_company_code}}</div>
     </template>
     <template v-else>
       <img :src="src" width="500px" height="500px"/>
@@ -45,6 +47,7 @@
 
 
     <el-button type="info"  @click="connect_mcu">链接MCU</el-button>
+    <el-button type="info"  @click="connect_mcu__init">MCU初始化</el-button>
     <el-button type="info"  @click="connect_bluetooth">连接蓝牙</el-button>
     <el-button @click="socketDisconnect">socket 断开</el-button>
     <router-link
@@ -242,14 +245,29 @@ async function connect_mcu(){
   if(clientStore.isClient){
 
     await socketStore.connectSocket();
-     socketStore.sendMessage({
+    socketStore.sendMessage({
       type: 'connect_mcu',
-       data:"connect_mcu"
+      data:"connect_mcu"
+    })
+  }
+
+}
+
+async function connect_mcu__init(){
+
+  if(clientStore.isClient){
+
+    await socketStore.connectSocket();
+    socketStore.sendMessage({
+      type: 'init_mcu',
+      data:"init_mcu"
     })
   }
 
 }
 
+
+
 async function connect_bluetooth(){
 
   await socketStore.connectSocket();

+ 18 - 1
frontend/src/views/Photography/check.vue

@@ -3,6 +3,7 @@
   <headerBar
       title="拍摄物体镜头矫正"
       :menu="menu"
+      showUser
   />
   <div class="check-wrap flex">
 
@@ -51,6 +52,7 @@
   </div>
 
   <hardware-check
+   isInitCheck
    @confirm="checkConfirm(true)"
   />
 </template>
@@ -71,7 +73,7 @@ function onCheckComplete(){
 
 }
 
-import { ref } from 'vue'
+import { ref,onBeforeUnmount } from 'vue'
 import socket from "../../stores/modules/socket";
 import { digiCamControlWEB } from  '@/utils/appconfig'
 const imageUrl = ref(digiCamControlWEB+'preview.jpg')
@@ -86,6 +88,9 @@ function checkConfirm(init){
     menu.push({
       type:'setting'
     })
+    menu.push({
+      type:'developer'
+    })
   }
   if(!init) previewKey.value++;
 
@@ -145,6 +150,18 @@ function takePictures() {
     })
   }
 }
+
+
+/**
+ * 页面卸载时移除所有事件监听器。
+ */
+onBeforeUnmount(() => {
+  clientStore.ipc.removeAllListeners(icpList.camera.takePictures);
+  clientStore.ipc.removeAllListeners(icpList.camera.PreviewHide);
+  clientStore.ipc.removeAllListeners(icpList.camera.PreviewShow);
+
+
+})
 </script>
 <style scoped lang="scss">
 .check-wrap {

+ 9 - 8
frontend/src/views/Photography/detail.vue

@@ -1,14 +1,11 @@
 <template>
 
-  <headerBar title="拍摄商品" :menu="[
+  <headerBar title="主图与详情生成" :menu="[
     {
       type: 'setting'
-    },
-    {
-      name: '详情高级配置',
-      click: openPhotographySeniorDetail
     }
   ]" />
+
   <div class="detail-container">
     <div>
       <!-- 主图LOGO部分 -->
@@ -211,6 +208,10 @@ import { getRouterUrl } from '@/utils/appfun'
 import { Close, Warning } from '@element-plus/icons-vue'
 import LoadingDialog from '@/views/Photography/components/LoadingDialog.vue'
 
+
+import { useCheckInfo } from '@/composables/useCheckInfo';
+useCheckInfo();
+
 const showTips = ref(true)
 
 const folderPath = ref('') //货号文件夹
@@ -252,7 +253,7 @@ const queryParams = reactive({ // 分页查询参数
   current: 1,
 })
 const form = reactive({
-  selectTemplate: {}, //选中的模板 
+  selectTemplate: {}, //选中的模板
   dataType: '1', // 1: 选择excel文件 2: 系统对接
   logo_path: '', // 主图LOGO
   excelFilePath: 'D:\\MyDocuments\\PythonCode\\MyPython\\red_dragonfly\\deal_pics\\auto_capture_V2\\auto_photo', // 商品基础资料EXCEL文件选择
@@ -386,10 +387,10 @@ const generate = async function () {
       message.value = loadingMsg
     }
   });
-} 
+}
 const openLoadingDialog = (timer: number) => {
   loadingDialogVisible.value = true
- 
+
   showButton.value = true
   // 根据传入的秒数计算每次增加的进度值
   const step = 100 / timer

+ 94 - 43
frontend/src/views/Photography/shot.vue

@@ -2,12 +2,16 @@
 
   <headerBar
       title="拍摄商品"
+      showUser
       :menu="[
         {
           type:'setting'
         }
     ]"
   />
+
+  <hardware-check/>
+
   <div class="photography-page flex-col">
     <div class="main-container">
       <div class="content-wrapper flex-row">
@@ -155,13 +159,15 @@
 </template>
 <script setup lang="ts">
 import headerBar from '@/components/header-bar/index.vue'
-import { ref, reactive, onMounted, onBeforeUnmount } from 'vue'
+import { ref, reactive, onMounted, onBeforeUnmount,watchEffect } from 'vue'
 import icpList from '@/utils/ipc'
 import client from "@/stores/modules/client";
 import socket from "@/stores/modules/socket";
 import { ElMessage ,ElMessageBox } from 'element-plus'
 import { getFilePath,getRouterUrl } from '@/utils/appfun'
 import {useRouter} from "vue-router";
+import HardwareCheck from '@/components/check/index.vue'
+import checkInfo from "@/stores/modules/check";
 
 
 const loading = ref(false)
@@ -183,13 +189,20 @@ const runAction = ref({
 // 初始化 WebSocket 状态管理
 const socketStore = socket()
 
-
+/**
+ * 保存货号模板到货号变量中。
+ */
 function saveGoodsArtNo(){
-  if(goods_art_no_tpl.value) goods_art_no.value = goods_art_no_tpl.value
-
+  if(goods_art_no_tpl.value){
+    goods_art_no.value = goods_art_no_tpl.value
+    ElMessage.success('商品货号'+goods_art_no.value+'获取成功,请在遥控器上按下左或右脚按键,启动拍摄')
+  }
 }
 
-//获取拍照记录
+/**
+ * 获取拍照记录。
+ * @param params - 可选参数,用于分页或其他筛选条件。
+ */
 async function getPhotoRecords(params?:{}) {
   clientStore.ipc.removeAllListeners(icpList.takePhoto.getPhotoRecords);
   loading.true = true;
@@ -210,7 +223,10 @@ async function getPhotoRecords(params?:{}) {
   });
 }
 
-//执行拍照   扫了货号,点击遥控器
+/**
+ * 执行拍照操作。
+ * @param data - 包含拍摄所需的数据对象。
+ */
 async function runGoods(data) {
   console.log('aa2')
   await socketStore.connectSocket();
@@ -220,19 +236,42 @@ async function runGoods(data) {
     type: 'run_mcu',
     data,
   })
-  console.log('aa4')
+
+  ElMessage.success('开始拍摄,请稍后')
   runLoading.value = true;
   runAction.value = data
   goods_art_no.value = ''
   goods_art_no_tpl.value = ''
-}
 
 
+  clientStore.ipc.on(icpList.socket.message + '_run_mcu', (event, result) => {
+
+    clientStore.ipc.removeAllListeners(icpList.socket.message + '_run_mcu');
+    console.log('_run_mcu');
+    console.log(result);
+    if(result.code !== 0 && result.msg){
+      ElMessage.error(result.msg)
+      runLoading.value = false
+      return;
+    }
+  })
+
+
+}
 
+/**
+ * 格式化时间字符串。
+ * @param time - 原始时间字符串。
+ * @returns 格式化后的时间字符串,若输入为空则返回 null。
+ */
 const getTime = function(time){
   if(!time) return null
   return time.replace('T',' ').substr(5,11)
 }
+
+/**
+ * 删除所有商品货号的历史记录。
+ */
 async function delAll(){
     let params = goodsList.value.map(item=>item.goods_art_no)
     await ElMessageBox.confirm('确定要删除当下的历史记录吗?', '提示', {
@@ -241,6 +280,11 @@ async function delAll(){
     })
     del({goods_art_nos:params})
 }
+
+/**
+ * 删除指定的商品货号。
+ * @param params - 包含需要删除的货号列表的对象。
+ */
 const del = async function(params){
 
   clientStore.ipc.removeAllListeners(icpList.takePhoto.delectGoodsArts);
@@ -257,6 +301,9 @@ const del = async function(params){
 
 }
 
+/**
+ * 检查是否可以进入下一步操作。
+ */
 const  next = async function(){
   if(runLoading.value){
     ElMessage.error('正在拍摄中,请稍候')
@@ -268,54 +315,44 @@ const  next = async function(){
   }
 }
 
-
+/**
+ * 页面挂载时初始化事件监听器并获取初始数据。
+ */
 onMounted(async () => {
-  //扫货号
+  // 监听蓝牙扫描事件
   clientStore.ipc.on(icpList.socket.message + '_blue_tooth_scan', (event, result) => {
 
     console.log('_blue_tooth_scan')
     if (result.code === 0 && result.data?.data) {
-      if(result.data?.data.goods_art_no) runGoods(result.data?.data)
-      if(!result.data?.data.goods_art_no && goods_art_no.value){
-        console.log('aa')
-        console.log('手工')
-        console.log({
-          ...result.data?.data,
-          goods_art_no: goods_art_no.value
-        })
-        runGoods({
-          ...result.data?.data,
-          goods_art_no: goods_art_no.value
-        })
-        console.log('aa1')
+      console.log(goods_art_no.value);
+      if(!goods_art_no.value){
+          ElMessage.error('请先扫描货号或者手动输入货号!')
+          return;
       }
+      runGoods({
+        ...result.data?.data,
+        goods_art_no: goods_art_no.value
+      })
 
     }
   });
 
+
   await getPhotoRecords();
-  // 扫码后 货号入库
+  // 监听图片处理完成事件
   clientStore.ipc.on(icpList.socket.message + '_image_process', (event, result) => {
     console.log('_image_process')
     console.log(result)
     getPhotoRecords()
   })
 
-
-/*
-  clientStore.ipc.on(icpList.socket.message + '_mcu', (event, result) => {
-    console.log('_mcu')
-    console.log(result)
-
-  })
-*/
-
+  // 监听拍照完成事件
   clientStore.ipc.on(icpList.socket.message + '_photo_take', (event, result) => {
     console.log('_photo_take')
     console.log(result)
     if(result.status === 2 && result.msg.includes('执行完成')){
       getPhotoRecords()
-      //延迟两秒在获取一遍
+      // 延迟两秒再获取一遍数据
       setTimeout(()=>{
         getPhotoRecords()
       },2000)
@@ -324,7 +361,7 @@ onMounted(async () => {
 
   })
 
-
+  // 监听拍照完成后的最终状态事件
   clientStore.ipc.on(icpList.socket.message + '_photo_take_finish', (event, result) => {
     console.log('_photo_take_finish')
     console.log(result)
@@ -332,7 +369,7 @@ onMounted(async () => {
 
   })
 
-
+  // 监听手动触发拍照事件
   clientStore.ipc.on(icpList.socket.message + '_handler_take_picture', async (event, result) => {
     console.log('_photo_take_finish')
     console.log(result)
@@ -341,6 +378,9 @@ onMounted(async () => {
         ElMessage.error('拍摄程序正在运行,请稍候')
         return
       }
+
+      ElMessage.success('正在拍摄中,请稍候')
+
       await socketStore.connectSocket();
       socketStore.sendMessage(result.data)
       takePictureLoading.value = true;
@@ -349,25 +389,35 @@ onMounted(async () => {
 
   })
 
+})
 
-
-
-
+const checkInfoStore = checkInfo()
+checkInfoStore.set_blue_tooth_scan_NO('')
+watchEffect(async ()=>{
+  if(checkInfoStore.blue_tooth_scan_NO){
+    ElMessage.success('商品货号'+checkInfoStore.blue_tooth_scan_NO+'获取成功,请在遥控器上按下左或右脚按键,启动拍摄')
+    goods_art_no.value = checkInfoStore.blue_tooth_scan_NO
+    checkInfoStore.set_blue_tooth_scan_NO('')
+  }
 })
 
-//关闭页面  去掉监听
+
+/**
+ * 页面卸载时移除所有事件监听器。
+ */
 onBeforeUnmount(() => {
   clientStore.ipc.removeAllListeners(icpList.socket.message + '_blue_tooth_scan');
   clientStore.ipc.removeAllListeners(icpList.socket.message + '_image_process');
+  clientStore.ipc.removeAllListeners(icpList.socket.message + '_run_mcu');
   clientStore.ipc.removeAllListeners(icpList.socket.message + '_photo_take');
   clientStore.ipc.removeAllListeners(icpList.socket.message + '_photo_take_finish');
 
 
 })
 
-
-
-//打开主图详情
+/**
+ * 打开主图详情页面。
+ */
 function openPhotographyDetail() {
 
   const { href } = Router.resolve({
@@ -391,6 +441,7 @@ function openPhotographyDetail() {
 
 </script>
 
+
 <style  lang="scss">
 .shot-image-popper {
   width: calc(100vw - 370px) !important;

+ 63 - 0
frontend/src/views/RemoteControl/index.vue

@@ -0,0 +1,63 @@
+<template>
+
+
+  <headerBar
+    title="遥控模拟器"
+  />
+
+  <div class="main-container">
+    <el-row align="middle">
+      <el-col :span="8"></el-col>
+      <el-col :span="8"><div class="button up">上</div></el-col>
+      <el-col :span="8"></el-col>
+    </el-row>
+    <el-row align="middle">
+      <el-col :span="8"><div class="button up">左脚</div></el-col>
+      <el-col :span="8"><div class="button up">拍照</div></el-col>
+      <el-col :span="8"><div class="button up">右脚</div></el-col>
+    </el-row>
+    <el-row align="middle">
+      <el-col :span="8"></el-col>
+      <el-col :span="8"><div class="button up">下</div></el-col>
+      <el-col :span="8"></el-col>
+    </el-row>
+    <el-row align="middle" class="mar-top-50">
+      <el-col :span="3"></el-col>
+      <el-col :span="8"><div class="button up">继续</div></el-col>
+      <el-col :span="2"></el-col>
+      <el-col :span="8"><div class="button up">停止</div></el-col>
+      <el-col :span="3"></el-col>
+    </el-row>
+  </div>
+
+</template>
+
+<script setup lang="ts">
+import headerBar from '@/components/header-bar/index.vue'
+</script>
+
+<style scoped lang="scss">
+.main-container {
+  background: #EAECED;
+  height: calc(100vh - 30px);
+  padding-top: 30px;
+}
+.button {
+  width: 68px;
+  height: 68px;
+  background: #fff;
+  border-radius: 68px;
+  line-height: 68px;
+  color: #474747;
+  margin: 0 auto;
+  box-shadow: 0 2px 8px 0 rgba(0,0,0,0.2);
+}
+.el-row {
+  min-height: 100px;
+}
+.button:hover {
+  background: #2957FF;
+  color: #fff;
+  cursor: pointer;
+}
+</style>

+ 111 - 12
frontend/src/views/Setting/index.vue

@@ -250,20 +250,24 @@
             </el-radio-group>
           </el-form-item>
           <el-form-item label="相机高度(mm)">
-            <el-input v-model="editRowData.camera_height" @change="changeNum('camera_high_motor')" :min="0" :max="400" :step="1"  style="width: 170px;" type="number">
+            <el-input v-model="editRowData.camera_height" @change="changeNum('camera_high_motor',0, 400)" :min="0" :max="400" :step="1"  style="width: 170px;" type="number">
             </el-input>
+            <div class="error-msg">最小0,最大400</div>
           </el-form-item>
           <el-form-item label="相机倾角">
-            <el-input v-model="editRowData.camera_angle" :min="-40" :max="40" :step=".1" @change="changeNum('camera_steering')" style="width: 170px;" type="number">
+            <el-input v-model="editRowData.camera_angle" :min="-40" :max="40" :step=".1" @change="changeNum('camera_steering',-40, 40)" style="width: 170px;" type="number">
             </el-input>
+            <div class="error-msg">最小-40,最大40</div>
           </el-form-item>
           <el-form-item label="转盘前后位置">
-            <el-input v-model="editRowData.turntable_position" @change="changeNum('turntable_position_motor')" :min="0" :max="800" :step="1"  style="width: 170px;" type="number">
+            <el-input v-model="editRowData.turntable_position" @change="changeNum('turntable_position_motor',0, 800)" :min="0" :max="800" :step="1"  style="width: 170px;" type="number">
             </el-input>
+            <div class="error-msg">最小0,最大800</div>
           </el-form-item>
           <el-form-item label="转盘角度">
-            <el-input v-model="editRowData.turntable_angle" @change="changeNum('turntable_steering')" :min="-720" :max="720" :step="1"  style="width: 170px;" type="number">
+            <el-input v-model="editRowData.turntable_angle" @change="changeNum('turntable_steering',-720, 720)" :min="-720" :max="720" :step="1"  style="width: 170px;" type="number">
             </el-input>
+            <div class="error-msg">最小-720,最大720</div>
           </el-form-item>
           <el-form-item label="鞋子翻转">
             <div class="flex-row">
@@ -281,16 +285,19 @@
             </el-radio-group>
           </el-form-item>
           <el-form-item label="对焦次数">
-            <el-input v-model="editRowData.number_focus" @change="changeNum('take_picture')" :min="0" :max="1" :step="1"  style="width: 170px;" type="number">
+            <el-input v-model="editRowData.number_focus" @change="changeNum('take_picture',0, 1)" :min="0" :max="1" :step="1"  style="width: 170px;" type="number">
             </el-input>
+            <div class="error-msg">最小0,最大1</div>
           </el-form-item>
           <el-form-item label="拍照前延时(秒)">
-            <el-input v-model="editRowData.pre_delay" :min="0" :max="99" :step="1"  style="width: 170px;" type="number">
+            <el-input v-model="editRowData.pre_delay" :min="0" :max="99" :step="1" @change="changeNum('pre_delay',0, 99)" style="width: 170px;" type="number">
             </el-input>
+            <div class="error-msg">最小0,最大99</div>
           </el-form-item>
           <el-form-item label="拍照后延时(秒)">
-            <el-input v-model="editRowData.after_delay" :min="0" :max="99" :step="1"  style="width: 170px;" type="number">
+            <el-input v-model="editRowData.after_delay" :min="0" :max="99" :step="1" @change="changeNum('after_delay',0, 99)" style="width: 170px;" type="number">
             </el-input>
+            <div class="error-msg">最小0,最大99</div>
           </el-form-item>
           <!-- <el-form-item label="是否等待">
             <el-radio-group v-model="editRowData.wait_user">
@@ -308,7 +315,7 @@
         <template #footer>
           <div class="btn-row">
             <div class="normal-btn" @click="dialogVisible = false">关闭</div>
-            <!-- <div class="primary-btn" @click="testShoesFlip">拍照测试</div> -->
+             <div class="primary-btn" v-loading="captureLoading" @click="testShoesFlip">运行并拍照</div>
             <div class="primary-btn" @click="saveRow">保存并关闭</div>
           </div>
         </template>
@@ -332,6 +339,10 @@ import client from "@/stores/modules/client";
 import icpList from '@/utils/ipc';
 const clientStore = client();
 import { ElMessage, ElMessageBox } from 'element-plus';
+import { digiCamControlWEB } from  '@/utils/appconfig'
+import { useCheckInfo } from '@/composables/userCheck';
+import { preview } from '@planckdev/element-plus/utils'
+useCheckInfo();
 
 // 路由和状态管理初始化
 const route = useRoute();
@@ -766,8 +777,7 @@ const resetConfig = () => {
  * 修改设备配置数值。
  * @param {string} type - 配置类型
  */
-async function changeNum(type) {
-  if (isDefault.value) {
+async function changeNum(type, min, max) {
     let socketValue = {
       'camera_high_motor': 'camera_height',
       'turntable_steering': 'turntable_angle',
@@ -777,18 +787,91 @@ async function changeNum(type) {
       'laser_position': 'led_switch',
       'take_picture': 'number_focus',
     };
+    if(min || max){
+      if(editRowData.value[socketValue[type]] < min || editRowData.value[socketValue[type]] > max){
+        if(editRowData.value[socketValue[type]] < min){
+          editRowData.value[socketValue[type]] = min;
+        }else{
+          editRowData.value[socketValue[type]] = max;
+        }
+        ElMessage.error(`${type}值应在${min}到${max}之间`);
+        return;
+      }
+    }
+    if(type=='pre_delay' || type=='after_delay'){
+      return
+    }
+  if (isDefault.value) {
     socketStore.sendMessage({
       type: 'control_mcu',
       data: {
         device_name: type,
-        value: type == 'laser_position' ? (editRowData.led_switch ? 1 : 0) : editRowData.value[socketValue[type]]
+        value: type == 'laser_position' ? (editRowData.value.led_switch ? "1" : "0") : editRowData.value[socketValue[type]]
       }
     });
   }
 }
-</script>
 
 
+/*测试拍照*/
+const captureLoading = ref(false)
+const imageUrl = ref(digiCamControlWEB+'preview.jpg')
+const imageUrlkey = ref(0)
+function testShoesFlip(){
+  if (clientStore.isClient) {
+
+    socketStore.sendMessage({
+      type: 'run_mcu_single',
+      data: {
+        camera_height: Number(editRowData.value.camera_height),
+        camera_angle:  Number(editRowData.value.camera_angle),
+        led_switch:editRowData.value.led_switch,
+        id:0,
+        mode_type:'执行'+ activeTab.value === 'left' ? '左脚' : '右脚'+'程序',
+        turntable_position:Number(editRowData.value.turntable_position),
+        action_name:editRowData.value.action_name || '测试',
+        turntable_angle: Number(editRowData.value.turntable_angle),
+        shoe_upturn: Number(editRowData.value.shoe_upturn),
+        action_index:1,
+        number_focus:0,
+        take_picture:false,
+        pre_delay:0,
+        after_delay:0,
+      }
+    });
+      captureLoading.value = true;
+
+
+    clientStore.ipc.on(icpList.socket.message+'_run_mcu_single_finish', async (event, result) => {
+      console.log('_run_mcu_single_finish')
+      setTimeout(()=>{
+        clientStore.ipc.removeAllListeners(icpList.camera.takePictures);
+        clientStore.ipc.send(icpList.camera.takePictures,false);
+        clientStore.ipc.on(icpList.camera.takePictures, async (event, result) => {
+          setTimeout(()=>{
+            imageUrlkey.value++;
+            console.log('preview')
+            preview(imageUrl.value+'?key='+imageUrlkey.value)
+            captureLoading.value = false;
+          },8000)
+          clientStore.ipc.removeAllListeners(icpList.camera.takePictures);
+        })
+      },1000)
+
+
+    })
+  }
+}
+
+
+
+</script>
+
+<style lang="scss">
+.el-image-viewer__wrapper{
+  z-index: 9999 !important;
+}
+</style>
 <style lang="scss" scoped>
 body {
   background: #EAECED;
@@ -1067,6 +1150,7 @@ body {
       }
       .el-form-item__content {
         width: 190px;
+        position: relative;
         height: 41px;
         background: #FFFFFF;
         padding-left: 7px;
@@ -1074,6 +1158,21 @@ body {
         .el-input__wrapper {
           box-shadow: none;
         }
+        .error-msg{
+          display: none;
+          position: absolute;
+          top: 41px;
+          top: 28px;
+          left: 8px;
+          z-index: 22;
+          color: #dc2626;
+          font-size: 12px;
+        }
+        &:hover{
+          .error-msg{
+            display: block;
+          }
+        }
 
         // 确保number类型输入框的上下箭头始终显示
         input[type="number"]::-webkit-inner-spin-button,