Pārlūkot izejas kodu

Merge remote-tracking branch 'origin/dev-frontend'

panqiuyao 7 mēneši atpakaļ
vecāks
revīzija
2361e41d67

+ 0 - 1
build/extraResources/read.txt

@@ -1 +0,0 @@
-建议第三方软件放置在此目录中,打包时会将资源加入安装包内。

+ 10 - 0
electron/api/setting.js

@@ -21,6 +21,16 @@ module.exports = {
       }
     })
   },
+  //根据条件查询可执行程序-单条
+  getDeviceConfigDetailQuery(data){
+    return post({
+      url: '/device_config_detail_query',
+      data: data,
+      headers:{
+        'Content-Type':"application/json",
+      }
+    })
+  },
 
 
   //删除

+ 17 - 0
electron/api/takephoto.js

@@ -17,6 +17,23 @@ module.exports = {
     })
   },
 
+  //生成主图  create_main_image
+
+  createMainImage(data){
+    return post({
+      url: '/create_main_image',
+      data: data
+    })
+  },
+
+  getLastPhotoRecord(data){
+    return get({
+      url: '/get_last_photo_record',
+      data: data
+    })
+  },
+
+
 
 
 }

+ 1 - 1
electron/config/builder.json

@@ -1,5 +1,5 @@
 {
-  "productName": "ZhiHuiYin",
+  "productName": "智慧映",
   "appId": "valimart",
   "copyright": "valimart",
   "directories": {

+ 6 - 2
electron/controller/setting.js

@@ -5,7 +5,7 @@ const Log = require('ee-core/log');
 const Services = require('ee-core/services');
 const path = require('path');
 const fs = require('fs');
-const { getDeviceConfigs, getDeviceConfigDetail, removeConfig, saveDeviceConfig, resetDeviceConfig,updateSysConfigs,getSysConfig } = require('../api/setting');
+const { getDeviceConfigs, getDeviceConfigDetail,getDeviceConfigDetailQuery, removeConfig, saveDeviceConfig, resetDeviceConfig,updateSysConfigs,getSysConfig } = require('../api/setting');
 
 const errData = {
   msg :'请求失败,请联系管理员',
@@ -43,7 +43,11 @@ class SettingController extends Controller {
    */
   async getDeviceConfigDetail(args) {
     try {
-      const result = await getDeviceConfigDetail(args);
+      let fun = getDeviceConfigDetail
+      if(!args.id) {
+        fun =  getDeviceConfigDetailQuery
+      }
+      const result = await fun(args);
       if(result.data)  return result.data
       return errData;
     } catch (error) {

+ 13 - 2
electron/controller/socket.js

@@ -8,7 +8,7 @@ let socket = null;
 const { pyapp } = require('../config/app.config.json')
 
 const typeToMessage = {
-  run_mcu_single_finish:"seeting",
+  run_mcu_single:['seeting','default'],
   get_deviation_data:"developer",
   set_deviation:"developer",
   get_mcu_other_info:"developer",
@@ -53,7 +53,18 @@ class SocketController extends Controller {
           if(this_data.msg_type){
             let channel = 'controller.socket.message_'+this_data.msg_type;
             if(typeToMessage[this_data.msg_type]){
-              this.app.electron[typeToMessage[this_data.msg_type]].webContents.send(channel, this_data);
+              if(typeof typeToMessage[this_data.msg_type] === 'object'){
+
+                typeToMessage[this_data.msg_type].map(item=>{
+                  if(item === 'default'){
+                    win.webContents.send(channel, this_data);
+                  }else{
+                    if(this.app.electron[item]) this.app.electron[item].webContents.send(channel, this_data);
+                  }
+                })
+              }else{
+                if(this.app.electron[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);
             }

+ 30 - 1
electron/controller/takephoto.js

@@ -1,6 +1,6 @@
 'use strict';
 const { Controller } = require('ee-core');
-const  { getPhotoRecords,delectGoodsArts } =  require('../api/takephoto');
+const  { getPhotoRecords,delectGoodsArts,createMainImage,getLastPhotoRecord } =  require('../api/takephoto');
 const errData = {
   msg :'请求失败,请联系管理员',
   code:999
@@ -54,6 +54,35 @@ class takePhotoController extends Controller {
       return errData;
     }
   }
+  async createMainImage (params) {
+    console.log('createMainImage')
+    try {
+      const result = await createMainImage(params);
+      console.log('result')
+      console.log(result)
+      if(result.data)  return result.data
+      return errData;
+    } catch (error) {
+      console.log('error')
+      console.log(error)
+      return errData;
+    }
+  }
+
+  async getLastPhotoRecord (params) {
+    console.log('getLastPhotoRecord')
+    try {
+      const result = await getLastPhotoRecord(params);
+      console.log('result')
+      console.log(result)
+      if(result.data)  return result.data
+      return errData;
+    } catch (error) {
+      console.log('error')
+      console.log(error)
+      return errData;
+    }
+  }
 
 
 }

+ 1 - 1
frontend/package.json

@@ -9,7 +9,7 @@
     "preview": "vite preview"
   },
   "dependencies": {
-    "@element-plus/icons-vue": "2.3.1",
+    "@element-plus/icons-vue": "^2.3.1",
     "@planckdev/element-plus": "0.0.0-rc.6",
     "@types/axios": "0.14.4",
     "axios": "1.8.3",

+ 3 - 0
frontend/src/App.vue

@@ -15,6 +15,9 @@ import Login from '@/components/login/index.vue';
 import useUserInfo from "@/stores/modules/user";
 const useUserInfoStore = useUserInfo();
 
+import { listenerOnline } from '@/composables/online';
+listenerOnline();
+
 const router = useRouter()
 const route = useRoute()
 /* keep alive 的路由名称 */

BIN
frontend/src/assets/images/Photography/hui.png


BIN
frontend/src/assets/images/Photography/lan.png


BIN
frontend/src/assets/images/Photography/left.png


BIN
frontend/src/assets/images/Photography/wifi.png


BIN
frontend/src/assets/images/Photography/yk.png


+ 2 - 2
frontend/src/components/header-bar/index.vue

@@ -114,8 +114,8 @@ function openSetting() {
   clientStore.ipc.removeAllListeners(icpList.utils.openMain);
   let params = {
     title: '设置',
-    width: 900,
-    height: 630,
+    width: 1920,
+    height: 1080,
     frame: true,
     id: "seeting",
     url: getRouterUrl(href)

+ 1 - 1
frontend/src/components/login/index.vue

@@ -23,7 +23,7 @@
             label-position="left"
         >
           <div class="title__main">欢迎!</div>
-          <div class="title_sub">登录拍照机系统</div>
+          <div class="title_sub">登录智慧映系统</div>
 
           <el-form-item class="login-input" prop="username">
             <div class="login-icon">

+ 45 - 0
frontend/src/composables/online.ts

@@ -0,0 +1,45 @@
+
+import { ElMessageBox  } from 'element-plus';
+export function listenerOnline() {
+
+    if (!navigator.onLine) {
+        ShowError()
+    }
+// 监听网络状态变化
+    window.addEventListener('online', () => {
+        console.log('online')
+
+        ElMessageBox({
+            title:"网络变化",
+            message:'网络已连接,5S后将自动刷新网页!',
+            showCancelButton:false,
+            showConfirmButton:false,
+            closeOnClickModal:false,
+            closeOnPressEscape:false,
+            closeOnHashChange:false,
+            showClose:false
+        })
+        setTimeout(()=>{
+            window.location.reload();
+        })
+    });
+
+    window.addEventListener('offline', () => {
+        console.log('offline')
+        ShowError();
+    });
+
+    function ShowError(){
+
+        ElMessageBox({
+            title:"网络掉线!",
+            message:'网络已断开,请检查网络连接!',
+            showCancelButton:false,
+            showConfirmButton:false,
+            closeOnClickModal:false,
+            closeOnPressEscape:false,
+            closeOnHashChange:false,
+            showClose:false
+        })
+    }
+}

+ 1 - 2
frontend/src/composables/userCheck.ts

@@ -19,8 +19,7 @@ export function useCheckInfo() {
                 showConfirmButton:false,
                 closeOnClickModal:false,
                 closeOnPressEscape:false,
-                closeOnHashChange:false,
-                showClose:false
+                closeOnHashChange:false
             })
         }
 

+ 4 - 0
frontend/src/main.ts

@@ -5,9 +5,13 @@ import './style.css'
 import App from './App.vue'
 import ElementPlus from 'element-plus'
 import 'element-plus/dist/index.css'
+import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 
 const app = createApp(App)
 app.use(ElementPlus)
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+    app.component(key, component)
+}
 app.use(pinia)
 app.use(router)
 app.mount('#app')

+ 3 - 3
frontend/src/router/index.ts

@@ -1,4 +1,4 @@
-import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
+import { createRouter, createWebHistory, createWebHashHistory, RouteRecordRaw } from "vue-router";
 
 import { authGuard } from './plugins/authGuard'
 
@@ -68,13 +68,13 @@ const routes: RouteRecordRaw[] = [
         name: "developer",
         component: () => import("@/views/Developer/index.vue"),
         meta: {
-            title: '遥控器'
+            title: '开发者设置'
         }
     },
 ];
 
 const router = createRouter({
-    history: createWebHashHistory(), // 修改: 将 createWebHistory 改为 createWebHashHistory
+    history: createWebHistory(), // 修改: 将 createWebHistory 改为 createWebHashHistory
     routes
 });
 

+ 11 - 1
frontend/src/stores/modules/check.ts

@@ -76,7 +76,7 @@ export const checkInfo = defineStore('checkInfo', () => {
             return '拍照机初始化失败,请重新监测!';
         }
         for (const device of Object.values(devices)) {
-            if (device.status === -1) {
+            if (device.status === -1  && device.msg_type !== 'connect_bluetooth') {
                 clearInterval(CKTimerInterval)
                 checkTime.value = 0
                 mcu.isInitSend = false
@@ -122,6 +122,8 @@ export const checkInfo = defineStore('checkInfo', () => {
 
     }
 
+    //第二次才算mcu正在失败
+    const mcuErrorCount = ref(0);
     // 通用设备检查函数
     const checkDevice = async (deviceName: string, messageType: string) => {
         try {
@@ -155,6 +157,14 @@ export const checkInfo = defineStore('checkInfo', () => {
                             }
                         }
                         if([-1,0].includes(result.status)){
+                            if(result.status === -1 ){
+                                if(mcuErrorCount.value  === 0){
+                                    mcuErrorCount.value++;
+                                    return;
+                                }else{
+                                    mcuErrorCount.value = 0
+                                }
+                            }
                             devices[deviceName].status = result.status;
                             devices[deviceName].msg = result.msg;
                         }

+ 1 - 1
frontend/src/utils/appfun.ts

@@ -8,6 +8,6 @@ export  function getFilePath (file_path){
 
 //获取路由地址
 export  function getRouterUrl (href){
-    return window.location.origin+window.location.pathname+href
+    return window.location.origin+href
 }
 

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

@@ -31,7 +31,9 @@ const icpList = {
     },
     takePhoto:{
         getPhotoRecords: 'controller.takephoto.getPhotoRecords',
-        delectGoodsArts: 'controller.takephoto.delectGoodsArts'
+        delectGoodsArts: 'controller.takephoto.delectGoodsArts',
+        createMainImage: 'controller.takephoto.createMainImage',
+        getLastPhotoRecord: 'controller.takephoto.getLastPhotoRecord'
     },
     generate:{
         generatePhotoDetail: 'controller.generate.generatePhotoDetail',

+ 190 - 85
frontend/src/views/Photography/check.vue

@@ -1,83 +1,124 @@
 <template>
 
   <headerBar
+      v-if="!isSetting"
       title="拍摄物体镜头矫正"
       :menu="menu"
       showUser
   />
-  <div class="check-wrap flex">
+  <div class="check-wrap">
 
-    <div class="check-page flex-col">
+    <div class="check-page flex-col" :class="isSetting ? 'check-page_seeting' : '' ">
       <div class="main-container flex-col">
         <div class="content-wrapper flex-row justify-between">
           <div class="left-panel flex-col justify-between">
             <div class="tips-container flex-row" v-if="show">
-              <img class="tips-icon" referrerpolicy="no-referrer" src="@/assets/images/Photography/tips-icon.png" />
-              <span class="tips-tex">请在圆盘上摆上鞋子(注意左右脚),要求鞋外侧朝向拍照机,鞋子中轴线和红外线对齐</span>
-              <img class="close-icon cu-p" referrerpolicy="no-referrer" src="@/assets/images/Photography/close-icon.png"  @click="show = false"/>
+                <spanc  v-if="isSetting" class="tips-tex">
+                  请在圆盘上摆上鞋子(注意左右脚),要求鞋外侧朝向拍照机,鞋子中轴线和红外线对齐,如果光亮不够,可以打开光源。
+                </spanc>
+                <span v-else class="tips-tex">
+                  请在圆盘上摆上鞋子(注意左右脚),要求鞋外侧朝向拍照机,鞋子中轴线和红外线对齐,可以打开光源。
+                </span>
             </div>
             <div class="camera-preview  flex col center ">
-              <span v-if="step === 1" class="fs-14">
-                 <img v-if="previewKey" class="camera-img" :src="preview+'?key='+previewKey" />
-              </span>
+              <div class="camera-preview-img" v-if="step === 1">
+                <img v-if="previewKey" class="camera-img" :src="preview+'?key='+previewKey" />
+                <div class="example-image flex-col" v-if="!isSetting && previewKey > 1"><img src="https://huilimaimg.cnhqt.com/frontend/zhihuiyin/demo.jpg?x-oss-process=image/resize,w_400"></div>
+              </div>
               <template v-if="step === 2" >
-                <img class="camera-img" :src="imageUrl+'?key='+imageUrlkey" />
+                <img class="camera-img"  :src="getFilePath(imageTplPath)" />
                 <span  class="camera-description">这是一张用于检查镜头是否合适的测试图</span>
               </template>
+
             </div>
           </div>
-          <div class="example-image flex-col"><img src="https://huilimaimg.cnhqt.com/frontend/zhihuiyin/demo.jpg?x-oss-process=image/resize,w_400"></div>
-        </div>
-        <div v-if="step === 1" class="action-button flex cente">
-          <div @click="takePictures" class="check-button  button--primary1 flex-col"><span class="button-text" v-loading="loading">拍照检查</span>
-          </div>
         </div>
 
-        <div v-else class="action-button flex center">
-          <div @click="checkConfirm(false)" class="check-button  button--white flex-col">
-            <span class="button-text cu-p">重新拍照检查</span>
+        <template v-if="!isSetting">
+          <div v-if="step === 1" class="action-button flex cente">
+            <div @click="takePictures" class="check-button  button--primary1 flex-col"><span class="button-text" v-loading="loading">拍照检查</span>
+            </div>
           </div>
-          <router-link class="mar-left-20 " :to="{
-            name: 'PhotographyShot'
-          }">
-            <div class="check-button   button--primary1 flex-col">
-              <span class="button-text cu-p">确认无误,下一步</span>
+
+          <div v-else class="action-button flex center">
+            <div @click="checkConfirm(false)" class="check-button  button--white flex-col">
+              <span class="button-text cu-p">重新拍照检查</span>
             </div>
-          </router-link>
+            <router-link class="mar-left-20 " :to="{
+              name: 'PhotographyShot'
+            }">
+              <div class="check-button   button--primary1 flex-col">
+                <span class="button-text cu-p">确认无误,下一步</span>
+              </div>
+            </router-link>
 
-        </div>
+          </div>
+
+        </template>
       </div>
     </div>
 
-  </div>
+    <editRow
+      v-if="showrEditRow ||  isSetting"
+      :id="id"
+      @confirm="confirm"
+      ref="editData"
+      @onClose="onClose"
+      :addRowData="addRowData"
+    />
 
-  <hardware-check
+  </div>
+  <hardware-check v-if="!isSetting"
    isInitCheck
    @confirm="checkConfirm(true)"
   />
 </template>
 <script setup lang="ts">
-import  { watchEffect,ref, reactive } from  'vue'
+import {watchEffect, ref, reactive, defineEmits, defineProps, computed, onBeforeUnmount, onMounted} from 'vue'
 import client from "@/stores/modules/client";
+import socket from "@/stores/modules/socket";
 import icpList from '@/utils/ipc'
 import useUserInfo from "@/stores/modules/user";
 import headerBar from '@/components/header-bar/index.vue'
+import  editRow  from './components/editRow'
 const clientStore = client();
+const socketStore = socket(); // WebSocket状态管理实例
 
+const emit = defineEmits([ 'confirm','onClose']);
+
+const confirm = ()=>{
+  hideVideo()
+  emit('confirm')
+}
+const onClose = ()=>{
+  hideVideo()
+  emit('onClose')
+}
+// 定义 props
+const props = defineProps({
+  id:{
+    type: Number||String,
+    default: 0
+  },
+  addRowData:{
+    type: Object,
+    default: () => {
+      return { }
+    }
+  }
+})
 const menu = reactive([])
 const show = ref(true)
 
+const isSetting = computed(()=>{
+  return props.id || props.addRowData.mode_type
+})
+
 const useUserInfoStore = useUserInfo()
 import HardwareCheck from '@/components/check/index.vue'
-function onCheckComplete(){
-
-}
-
-import { ref,onBeforeUnmount } from 'vue'
-import socket from "../../stores/modules/socket";
 import { digiCamControlWEB } from  '@/utils/appconfig'
-const imageUrl = ref(digiCamControlWEB+'preview.jpg')
-const imageUrlkey = ref(0)
+import { getFilePath } from '@/utils/appfun'
+import {ElMessage} from "element-plus";
 const previewKey = ref(0)
 const preview = ref(digiCamControlWEB+'liveview.jpg')
 
@@ -85,9 +126,6 @@ const step = ref(1)
 function checkConfirm(init){
   step.value =1
   if(menu.length === 0){
-    menu.push({
-      type:'setting'
-    })
     if(useUserInfoStore.userInfo.brand_company_code === '1300'){
 
       menu.push({
@@ -98,10 +136,10 @@ function checkConfirm(init){
   if(!init) previewKey.value++;
 
   showVideo()
+  showrEditRow.value = true
 }
 
-
-
+const showrEditRow = ref(false)
 
 
 
@@ -135,70 +173,119 @@ function hideVideo(){
 }
 
 const loading = ref(false)
-
+const editData = ref(null);
+const imageTplPath = ref(null)
 function takePictures() {
+  console.log(editData);
+  console.log(editData.value.editRowData);
+
   if (clientStore.isClient) {
-    clientStore.ipc.removeAllListeners(icpList.camera.takePictures);
-    clientStore.ipc.send(icpList.camera.takePictures);
-    clientStore.ipc.on(icpList.camera.takePictures, async (event, result) => {
 
-      if(interval) clearInterval(interval)
-      loading.value = true;
-      imageUrlkey.value++;
-      setTimeout(()=>{
-        hideVideo()
-        step.value = 2
-        imageUrlkey.value++;
-        loading.value = false;
-      },8000)
+    loading.value = true;
+
+    socketStore.sendMessage({
+      type: 'run_mcu_single',
+      data: {
+        camera_height: Number(editData.value.editRowData.camera_height),
+        camera_angle:  Number(editData.value.editRowData.camera_angle),
+        led_switch:editData.value.editRowData.led_switch,
+        id:0,
+        mode_type:editData.value.editRowData.mode_type,
+        turntable_position:Number(editData.value.editRowData.turntable_position),
+        action_name:editData.value.editRowData.action_name || '测试',
+        turntable_angle: Number(editData.value.editRowData.turntable_angle),
+        shoe_upturn: Number(editData.value.editRowData.shoe_upturn),
+        action_index:1,
+        number_focus:0,
+        take_picture:true,
+        pre_delay:0,
+        after_delay:0,
+      }
+    });
+
+    clientStore.ipc.on(icpList.socket.message+'_run_mcu_single', async (event, result) => {
+      console.log('_run_mcu_single_check')
+      console.log(result)
+      if(result.code === 0 && result.data?.file_path){
+
+
+
+        clientStore.ipc.removeAllListeners(icpList.takePhoto.createMainImage);
+        clientStore.ipc.send(icpList.takePhoto.createMainImage,{
+          file_path:result.data.file_path
+        });
+        clientStore.ipc.on(icpList.takePhoto.createMainImage, async (event, result) => {
+          console.log('icpList.utils.createMainImage');
+
+          if(result.code === 0 && result.data?.main_out_path){
+            imageTplPath.value  = result.data?.main_out_path
+            hideVideo()
+            step.value = 2
+            loading.value = false;
+          }else if(result.msg){
+            if(result.code !== 0) ElMessage.error(result.msg)
+
+          }
+
+
+        });
+
+
+      }else if(result.msg){
+
+        if(result.code !== 0) ElMessage.error(result.msg)
+      }
+
     })
+
+
   }
 }
 
+onMounted(()=>{
+  showVideo()
+})
 
 /**
  * 页面卸载时移除所有事件监听器。
  */
 onBeforeUnmount(() => {
+  hideVideo()
   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 {
-
   background-color: rgba(234, 236, 237, 1);
-  width: 100%;
-  min-height: calc(100vh - 30px);
   display: flex;
+  justify-content: left;
 }
 .check-page {
+  width: calc(100% - 330px) ;
   position: relative;
-  width: 1200px;
-  height: 768px;
+  min-height: calc(100vh - 35px);
   overflow: hidden;
+  background: #fff;
 
   .main-container {
-    width: 1200px;
-    height: 739px;
+    width: 100%;
+    min-height: calc(100vh - 35px);
     margin-bottom: 1px;
 
     .content-wrapper {
-      width: 896px;
-      height: 644px;
-      margin: 10px 0 0 284px;
+      width:100%;
+      padding: 10px;
 
       .left-panel {
-        width: 633px;
-        height: 644px;
+        width: 100%;
+        height: calc(100% - 80px);
 
         .tips-container {
           background-color: rgba(255, 241, 222, 0.8);
           border-radius: 4px;
-          width: 633px;
+          width: 100%;
           height: 40px;
           border: 1px solid rgba(255, 228, 190, 1);
           justify-content: flex-center;
@@ -230,13 +317,11 @@ onBeforeUnmount(() => {
         }
 
         .camera-preview {
-          box-shadow: 0px 2px 10px 0px rgba(0, 32, 78, 0.1);
           background-color: rgba(255, 255, 255, 1);
-          height: 600px;
-          width: 600px;
-          margin: 4px 0 0 16px;
+          height:  calc(100vh - 200px);
+          position: relative;
+          margin-top: 20px;
           position: relative;
-
           .camera-description {
             position: absolute;
             bottom: 20px;
@@ -246,25 +331,39 @@ onBeforeUnmount(() => {
             border-radius: 20px;
             font-size: 14px;
           }
+          .camera-preview-img {
+            display: inline-block;
+            height: 100%;
+            min-width: 300px;
+            position: relative;
+          }
 
           .camera-img {
-            width: 100%;
+          //  width: 100%;
+            display: block;
+            position: relative;
+            box-shadow: 0px 2px 10px 0px rgba(0, 32, 78, 0.1);
+            height: 100%;
+            object-fit:contain;
 
           }
+          .example-image {
+            position: absolute;
+            right: 10px;
+            bottom: 10px;
+            background-color: rgba(216, 216, 216, 1);
+            height: 200px;
+            margin-top: 10px;
+            width: 200px;
+            img {
+              width: 200px;
+              height: 200px;
+              display: block;
+            }
+          }
         }
       }
 
-      .example-image {
-        background-color: rgba(216, 216, 216, 1);
-        height: 200px;
-        margin-top: 10px;
-        width: 200px;
-        img {
-          width: 200px;
-          height: 200px;
-          display: block;
-        }
-      }
     }
 
     .action-button {
@@ -286,4 +385,10 @@ onBeforeUnmount(() => {
     }
   }
 }
+.check-page_seeting {
+  min-height: calc(100vh - 92px);
+  .main-container {
+    min-height: calc(100vh - 92px);
+  }
+}
 </style>

+ 364 - 0
frontend/src/views/Photography/components/editRow.vue

@@ -0,0 +1,364 @@
+<template>
+  <div class="editrow_wrap" v-if="initStatus" v-loading="captureLoading">
+    <div class="config-type">参数值编辑:
+      <!-- <el-checkbox v-model="isDefault">开启运动调试</el-checkbox>-->
+    </div>
+    <el-form class="editForm" :model="editRowData" label-width="100px" >
+      <el-form-item label="动作名称">
+        <el-input v-model="editRowData.action_name" :disabled="editRowData.is_system" style="width: 170px;"/>
+      </el-form-item>
+      <el-form-item label="是否拍照">
+        <el-radio-group v-model="editRowData.take_picture">
+          <el-radio :label="true">拍照</el-radio>
+          <el-radio :label="false">不拍照</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="相机高度(mm)">
+        <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',-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',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',-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">
+          <el-radio-group v-model="editRowData.shoe_upturn">
+            <el-radio :label="true">翻转</el-radio>
+            <el-radio :label="false">不翻转</el-radio>
+          </el-radio-group>
+          <a class="cursor-pointer" @click="changeNum('overturn_steering')">测试翻转</a>
+        </div>
+      </el-form-item>
+      <el-form-item label="LED灯光开光" @change="changeNum('laser_position')">
+        <el-radio-group v-model="editRowData.led_switch">
+          <el-radio :label="false">关闭</el-radio>
+          <el-radio :label="true">开启</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="对焦次数">
+        <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" @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" @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>
+    <div class="btn-row mar-top-20">
+      <div class="normal-btn" @click="close" v-if="id">取消</div>
+      <div class="primary-btn" @click="saveRow">{{ id ? '保存并关闭' : '保存' }}</div>
+      </div>
+  </div>
+
+</template>
+
+<script setup lang="ts">
+import { ref, defineProps, defineEmits , watch, onMounted } from 'vue'
+import icpList from '@/utils/ipc';
+import { digiCamControlWEB } from  '@/utils/appconfig'
+import {ElMessage} from "element-plus";
+import client from "@/stores/modules/client";
+const clientStore = client();
+import socket from "@/stores/modules/socket";
+const socketStore = socket(); // WebSocket状态管理实例
+
+// 定义 props
+const props = defineProps({
+  id:{
+    type: Number||String,
+    default: 0
+  },
+  addRowData:{
+    type: Object,
+    default: () => {
+      return { }
+    }
+  }
+})
+
+const initStatus = ref(false)
+const isDefault = ref(true); // 是否为默认配置
+const editRowData = ref({}); // 当前编辑行的数据
+onMounted(()=>{
+  console.log('editrow')
+  if(props.addRowData.mode_type){
+    console.log(props.addRowData);
+    initStatus.value = true
+    editRowData.value = props.addRowData;
+    testShoesFlip()
+    return
+  }
+  let params = {
+    id: props.id
+  }
+  if(!props.id) params = {
+    mode_type:"执行左脚程序",
+    action_name:"侧视",
+  }
+  clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigDetail);
+  clientStore.ipc.send(icpList.setting.getDeviceConfigDetail, params);
+  clientStore.ipc.on(icpList.setting.getDeviceConfigDetail, (event, result) => {
+
+    console.log('getDeviceConfigDetail')
+    console.log(result)
+    if(result.code == 0 && result.data){
+
+      editRowData.value = result.data;
+      clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigDetail);
+      initStatus.value = true;
+      testShoesFlip()
+    }else if(result.msg){
+      ElMessage.error(result.msg)
+    }
+  });
+
+})
+
+
+/**
+ * 修改设备配置数值。
+ * @param {string} type - 配置类型
+ */
+async function changeNum(type, min, max) {
+  let socketValue = {
+    'camera_high_motor': 'camera_height',
+    'turntable_steering': 'turntable_angle',
+    'turntable_position_motor': 'turntable_position',
+    'camera_steering': 'camera_angle',
+    'overturn_steering': 'shoe_upturn',
+    '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.value.led_switch ? "1" : "0") : editRowData.value[socketValue[type]]
+      }
+    });
+  }
+}
+
+
+/*测试拍照*/
+const captureLoading = ref(false)
+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:editRowData.mode_type,
+        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', async (event, result) => {
+      console.log('_run_mcu_single_row')
+
+      captureLoading.value = false;
+
+    })
+  }
+}
+
+const emit = defineEmits([ 'confirm','onClose']);
+const close = ()=>{
+  console.log('onClose')
+  emit('onClose')
+}
+
+/**
+ * 保存当前编辑的配置。
+ */
+const saveRow = () => {
+  if(!editRowData.value.action_name){
+    ElMessage.error('请输入动作名称')
+    return;
+  }
+  clientStore.ipc.send(icpList.setting.saveDeviceConfig, {
+    ...editRowData.value
+  });
+  clientStore.ipc.on(icpList.setting.saveDeviceConfig, (event, result) => {
+    if (result.code == 0) {
+      emit('confirm')
+      ElMessage.success('保存成功');
+      clientStore.ipc.removeAllListeners(icpList.setting.saveDeviceConfig);
+    } else {
+      ElMessage.error('保存失败');
+    }
+  });
+};
+
+// 暴露给父组件
+defineExpose({
+  editRowData, // 父组件可通过 editData.value.editRowData 访问
+});
+</script>
+<style>
+</style>
+
+<style lang="scss" scoped>
+.editrow_wrap {
+}
+.config-type{
+  font-size: 16px;
+  color: #333333;
+  display: flex;
+  align-items: center;
+  justify-content: flex-start;
+  padding: 10px;
+  border-bottom: 1px solid #CCCCCC;
+  .el-checkbox{
+    margin-left: 10px;
+  }
+}
+.editForm{
+  padding: 10px;
+  display: grid;
+  grid-template-columns: repeat(1, 1fr);
+  gap: 0;
+  .flex-row{
+    display: flex;
+    align-items: center;
+    :deep(.el-radio){
+      margin-right: 6px !important;
+    }
+    :deep(.el-radio__label){
+      padding-left: 4px;
+    }
+  }
+  :deep(.el-form-item) {
+    margin-bottom: 0;
+    .el-form-item__label {
+      width: 120px !important;
+      padding-right: 0 !important;
+      background: #FBFCFF;
+      border: 1px solid #EEEEEE;
+      height: 41px;
+      line-height: 41px;
+      padding-left: 5px;
+      text-align: left;
+    }
+    .el-form-item__content {
+      width: 190px;
+      position: relative;
+      height: 41px;
+      background: #FFFFFF;
+      padding-left: 7px;
+      border: 1px solid #EEEEEE;
+      .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,
+      input[type="number"]::-webkit-outer-spin-button {
+        opacity: 1;
+        height: 28px;
+        position: absolute;
+        top: 2px;
+        right: 2px;
+        cursor: pointer;
+      }
+
+      input[type="number"] {
+        -moz-appearance: number-input; /* Firefox */
+      }
+    }
+  }
+}
+
+.btn-row{
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 10px;
+}
+.primary-btn{
+  width: 100px;
+  height: 30px;
+  background: linear-gradient( 135deg, #2FB0FF 0%, #B863FB 100%);
+  border-radius: 4px;
+  color: #fff;
+  font-size: 14px;
+  text-align: center;
+  line-height: 30px;
+  cursor: pointer;
+}
+.normal-btn{
+  width: 100px;
+  height: 30px;
+  background: #fff;
+  border: 1px solid #CCCCCC;
+  border-radius: 4px;
+  font-size: 14px;
+  text-align: center;
+  line-height: 30px;
+  cursor: pointer;
+}
+</style>

+ 23 - 16
frontend/src/views/Photography/detail.vue

@@ -9,16 +9,18 @@
   <div class="detail-container">
     <div>
       <!-- 主图LOGO部分 -->
-      <div class="logo-section flex left top">
-        <div class="section-title">
+      <div class="logo-section flex left top" >
+        <div class="section-title" style="margin-bottom: 0px;">
           <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
           主图LOGO:
         </div>
+      </div>
+      <div class="logo-section flex left top">
         <upload :value="form.logo_path" @input="onInput"></upload>
       </div>
 
       <el-divider />
-      <!-- 图片抠图与货号图生成 -->
+<!--      &lt;!&ndash; 图片抠图与货号图生成 &ndash;&gt;
       <div class="section">
         <div class="section-title">
           <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
@@ -38,8 +40,8 @@
             </el-icon>
           </div>
 
-          <!-- 货号文件夹 -->
-          <!--    <div class="form-item">
+          &lt;!&ndash; 货号文件夹 &ndash;&gt;
+          &lt;!&ndash;    <div class="form-item">
             <div class="label">货号文件夹:</div>
             <div class="folder-warp">
               <div class="folder-input">
@@ -56,11 +58,11 @@
               </div>
             </div>
           </div>
-         -->
+         &ndash;&gt;
 
         </div>
       </div>
-      <el-divider />
+      <el-divider />-->
 
       <!-- 选择详情模板部分 -->
       <div class="template-section ">
@@ -92,17 +94,22 @@
             </div>
           </div>
         </div>
+
+        <div class="template-tips c-333 fs-14 line-20 te-l mar-top-20 flex ">
+          <el-icon><WarningFilled /></el-icon>
+          <span class="mar-left-10">该模版图片顺序说明:{{form.selectTemplate.template_image_order}}</span>
+        </div>
       </div>
       <el-divider />
 
       <!-- 详情高级配置 -->
-      <div class="section">
+<!--      <div class="section">
         <div class="section-title">
           <img src="@/assets/images/Photography/zhuangshi.png" style="width: 32px; height: 32px;" />
           详情高级配置
         </div>
         <div class="section-content">
-          <!-- 图片顺序 -->
+          &lt;!&ndash; 图片顺序 &ndash;&gt;
           <div class="form-item">
             <div class="label">图片顺序:</div>
             <el-input v-model="imageOrder" placeholder="请输入图片顺序" class="specific-page-input">
@@ -112,14 +119,14 @@
             </el-input>
           </div>
 
-          <!-- 同款检验 -->
-          <!--   <div class="form-item">
+          &lt;!&ndash; 同款检验 &ndash;&gt;
+          &lt;!&ndash;   <div class="form-item">
             <div class="label">同款检验:</div>
             <el-checkbox v-model="checkSimilar">同款下货号必须齐全</el-checkbox>
           </div>
--->
-          <!-- 可指定页面独修改 -->
-          <!--      <div class="form-item">
+&ndash;&gt;
+          &lt;!&ndash; 可指定页面独修改 &ndash;&gt;
+          &lt;!&ndash;      <div class="form-item">
             <div class="label">可指定页面独修改:</div>
             <el-input v-model="specificPage" placeholder="请输入入需要单独修改的页面,示例:4:1 (需修改模版的编号:第一张)"
               class="specific-page-input">
@@ -128,10 +135,10 @@
               </template>
             </el-input>
           </div>
-          -->
+          &ndash;&gt;
         </div>
       </div>
-      <el-divider />
+      <el-divider />-->
       <!-- 详情资料准备部分 -->
       <div class="data-prep-section">
         <div class="flex-item left">

+ 321 - 65
frontend/src/views/Photography/shot.vue

@@ -11,7 +11,7 @@
   <div class="photography-page flex-col">
     <div class="main-container">
       <div class="content-wrapper flex-row">
-        <img class="camera-image" referrerpolicy="no-referrer" src="@/assets/images/Photography/camera-icon.png" />
+<!--        <img class="camera-image" referrerpolicy="no-referrer" src="@/assets/images/Photography/camera-icon.png" />-->
         <div class="step-number flex-col"><span class="text_22">1</span></div>
         <div class="step-one flex-col justify-between">
           <div class="step-header flex-row">
@@ -22,30 +22,18 @@
           </div>
           <div class="step-content flex-row justify-between">
             <div class="method-container flex-col">
+              <div class="input-container flex-row">
+                <el-input class="input-item" v-model="goods_art_no" placeholder="请输入货号"> </el-input>
+
+              </div>
               <div class="auto-method flex-row justify-between">
                 <div class="text-method-tag flex-col"><span class="text_4">自动获取</span></div>
                 <span class="method-description">用遥控器扫描商品资料二维码</span>
               </div>
               <div class="scan-method flex-row justify-between">
                 <div class="remote-control flex-col">
-                  <div class="scan-button flex-col"></div>
-                  <img class="scan-line" referrerpolicy="no-referrer"
-                    src="@/assets/images/Photography/remote-scan-line.png" />
                 </div>
-                <span class="scan-label">遥控器扫描键</span>
-              </div>
-              <div class="manual-method flex-row justify-between">
-                <div class="method-tag flex-col"><span class="text_7">手动获取</span></div>
-                <span class="method-description">手工输入货号</span>
-              </div>
-              <div class="input-container flex-row">
-                <el-input class="input-item" v-model="goods_art_no_tpl" placeholder="请输入货号">
-                  <template #append>
-
-                    <el-button class="input-button" type="primary" @click="saveGoodsArtNo">确认</el-button>
-                  </template>
-                </el-input>
-
+<!--                <span class="scan-label">遥控器扫描键</span>-->
               </div>
             </div>
             <img class="remote-image" referrerpolicy="no-referrer"
@@ -60,22 +48,25 @@
               <img class="info-icon" referrerpolicy="no-referrer" src="@/assets/images/Photography/info-icon.png" />
               <span class="tips-text">遥控左右按键可启动拍摄,中间按钮可在拍摄5张主图后解锁,用于拍摄自定义图</span>
             </div>
-            <span class="left-foot-text">遥控器左键:控制左脚鞋启动拍摄</span>
-            <div class="remote-control-container flex-row justify-end">
-              <div class="remote-buttons flex-row">
-                <div class="left-button flex-col"></div>
-                <div class="right-button flex-col"></div>
-              </div>
-              <img class="left-button-image" referrerpolicy="no-referrer"
-                src="@/assets/images/Photography/remote-left-button.png" />
-              <img class="right-button-image" referrerpolicy="no-referrer"
-                src="@/assets/images/Photography/remote-right-button.png" />
+            <div class="wifi mar-top-20">
+              <img  referrerpolicy="no-referrer"
+                   src="@/assets/images/Photography/wifi.png" style="width: 60px" />
+            </div>
+            <div class="remote-control-wrap">
+              <RemoteControl
+                  @onRemoteControl="onRemoteControl"
+              />
             </div>
-            <span class="right-foot-text">遥控器右键:控制右脚鞋启动拍摄</span>
+
           </div>
         </div>
 
       </div>
+
+      <div class="last-photo" v-show="showlastPhoto" v-key="lastPhoto.image_path">
+        <div>{{lastPhtotoName[lastPhoto.image_index] || ''}}</div>
+        <el-image  :src="getFilePath(lastPhoto.image_path)"  fit="contain" ></el-image>
+      </div>
       <div class="history-section flex-col">
           <span class="history-title flex between">
             <div>拍摄记录</div>
@@ -94,9 +85,15 @@
               <div v-else class="history-item clearfix"  v-for="item,index in goodsList" style="padding:10px;">
                 <div class="flex  between flex-item  c-333">
                   <div class="chaochu flex-item flex left">货号:{{ item.goods_art_no }}</div>
-                  <div class="c-666 fs-12">{{ getTime(item.action_time) }}</div>
+                  <div>
+                    <el-button size="small"  type="primary"  @click="reTakePictureNos(item.goods_art_no,item)" plain>重拍</el-button>
+                    <el-button size="small" @click="delGoods({goods_art_nos:[item.goods_art_no]})">删除</el-button>
+                  </div>
+                </div>
+                <div class="flex  between flex-item  c-333">
+                  <div class="c-999 fs-12">{{ getTime(item.action_time) }}</div>
                 </div>
-                <div class="mar-top-10" style="width: 100%" >
+                <div class="mar-top-10 clearfix history-item_image_wrap" style="width: 100%" >
                     <component class="history-item_image"
                          v-loading="!image.image_path && runAction.goods_art_no == item.goods_art_no"
                          v-for="image in item.items"
@@ -110,11 +107,14 @@
                           v-if="image.image_path"
                       >
                         <template #reference>
-                          <el-image  :src="getFilePath(image.image_path)"  fit="contain" >
-                            <template #error>
-                              <div class="image-slot"></div>
-                            </template>
-                          </el-image>
+                          <div class="flex el-image_view">
+                            <el-image  :src="getFilePath(image.image_path)"  fit="contain" >
+                              <template #error>
+                                <div class="image-slot"></div>
+                              </template>
+                            </el-image>
+                            <div class="reset-button" @click="reTakePicture(image)">重拍</div>
+                          </div>
                         </template>
 
 
@@ -164,6 +164,7 @@ import { getFilePath,getRouterUrl } from '@/utils/appfun'
 import {useRouter} from "vue-router";
 import HardwareCheck from '@/components/check/index.vue'
 import checkInfo from "@/stores/modules/check";
+import RemoteControl from '@/views/RemoteControl/index'
 
 
 const loading = ref(false)
@@ -181,9 +182,6 @@ const menu = computed(()=>{
         type:'setting'
       },
       {
-        type:'remoteControl'
-      },
-      {
         type:'developer'
       }
     ]
@@ -194,9 +192,6 @@ const menu = computed(()=>{
     {
       type:'setting'
     },
-    {
-      type:'remoteControl'
-    }
   ]
 
 
@@ -205,6 +200,38 @@ const menu = computed(()=>{
 const clientStore = client();
 
 const Router = useRouter()
+/*const goodsList = ref([
+  {
+    "goods_art_no": "123456789",
+    "items": [
+      {
+        "record_id":1,
+        "image_deal_mode":0,
+        "image_path": "C:\\Users\\Administrator\\Pictures\\digiCamControl\\Session1\\DSC_0001.jpg",
+      },
+      {
+        "record_id":2,
+        "image_deal_mode":0,
+        "image_path": "C:\\Users\\Administrator\\Pictures\\digiCamControl\\Session1\\DSC_0002.jpg",
+      },
+      {
+        "record_id":3,
+        "image_deal_mode":0,
+        "image_path": "C:\\Users\\Administrator\\Pictures\\digiCamControl\\Session1\\DSC_0003.jpg",
+      },
+      {
+        "record_id":4,
+        "image_deal_mode":0,
+        "image_path": "C:\\Users\\Administrator\\Pictures\\digiCamControl\\Session1\\DSC_0004.jpg",
+      },
+      {
+        "record_id":5,
+        "image_deal_mode":0,
+        "image_path": "C:\\Users\\Administrator\\Pictures\\digiCamControl\\Session1\\DSC_0005.jpg",
+      }
+    ]
+  }
+])*/
 const goodsList = ref([])
 const  goods_art_no_tpl = ref('')
 const  goods_art_no = ref('')
@@ -243,7 +270,16 @@ async function getPhotoRecords(params?:{}) {
     loading.true = false;
     clientStore.ipc.removeAllListeners(icpList.takePhoto.getPhotoRecords);
     if(result.code === 0){
-      goodsList.value = result.data.list
+
+      console.log('======================');
+      console.log(runAction.value);
+     goodsList.value = result.data.list
+      if(isDelGoodsGetList.value){
+        isDelGoodsGetList.value = false;
+        return;
+      }
+      getLastPhotoRecord()
+
     }else if(result.msg) {
       ElMessage.error(result.msg)
     }
@@ -255,6 +291,11 @@ async function getPhotoRecords(params?:{}) {
  * @param data - 包含拍摄所需的数据对象。
  */
 async function runGoods(data) {
+  if(runLoading.value || takePictureLoading.value){
+    ElMessage.error('拍摄程序正在运行,请稍候')
+    return
+  }
+
   await socketStore.connectSocket();
 
   socketStore.sendMessage({
@@ -263,10 +304,12 @@ async function runGoods(data) {
   })
   ElMessage.success('开始拍摄,请稍后')
   runLoading.value = true;
-  runAction.value = data
+  runAction.value.action = data.action
+  runAction.value.goods_art_no = data.goods_art_no
   goods_art_no.value = ''
   goods_art_no_tpl.value = ''
-
+  reNosObj.value.goods_art_no = null;
+  reNosObj.value.action = null;
 
   clientStore.ipc.on(icpList.socket.message + '_run_mcu', (event, result) => {
 
@@ -275,7 +318,7 @@ async function runGoods(data) {
     console.log(result);
     if(result.code !== 0 && result.msg){
       ElMessage.error(result.msg)
-      runLoading.value = false
+   //   runLoading.value = false
       return;
     }
   })
@@ -309,6 +352,21 @@ async function delAll(){
  * 删除指定的商品货号。
  * @param params - 包含需要删除的货号列表的对象。
  */
+const delGoods = async function(params){
+
+
+  await ElMessageBox.confirm('确定要删除货号:'+params.goods_art_nos[0]+'的拍摄数据吗?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+  })
+  del(params)
+}
+
+/**
+ * 删除指定的商品货号。
+ * @param params - 包含需要删除的货号列表的对象。
+ */
+const isDelGoodsGetList = ref(false)
 const del = async function(params){
 
   clientStore.ipc.removeAllListeners(icpList.takePhoto.delectGoodsArts);
@@ -316,8 +374,12 @@ const del = async function(params){
   clientStore.ipc.on(icpList.takePhoto.delectGoodsArts, (event, result) => {
     clientStore.ipc.removeAllListeners(icpList.takePhoto.delectGoodsArts);
     if(result.code === 0){
+      isDelGoodsGetList.value =true
       ElMessage.info('货号删除成功')
       getPhotoRecords()
+      if(reNosObj.value.goods_art_no){
+        runGoods(reNosObj.value)
+      }
     }else if(result.msg) {
       ElMessage.error(result.msg)
     }
@@ -325,6 +387,57 @@ const del = async function(params){
 
 }
 
+//单个重拍
+
+const reTakePicture = async (img)=>{
+  if(!img.id) return;
+  await ElMessageBox.confirm('此操作会先删除此数据,需要继续吗?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+  })
+
+  socketStore.sendMessage({
+    type: 're_take_picture',
+    "data":{"record_id":img.id},
+  })
+
+  clientStore.ipc.removeAllListeners(icpList.socket.message + '_re_take_picture');
+
+  clientStore.ipc.on(icpList.socket.message + '_re_take_picture', (event, result) => {
+    console.log('_re_take_picture')
+    console.log(result)
+    if(result.code === 0){
+      getPhotoRecords()
+      setTimeout(()=>{
+        showlastPhoto.value = false
+      },6000)
+    }else if(result.msg) {
+      ElMessage.error(result.msg)
+    }
+    clientStore.ipc.removeAllListeners(icpList.socket.message + '_re_take_picture');
+
+  })
+}
+
+const reNosObj = ref({
+  goods_art_no:null,
+  action:null,
+})
+//货号重拍
+const reTakePictureNos = async (goods_art_no,item)=>{
+
+  await ElMessageBox.confirm('此操作会先删除删除货号:'+goods_art_no+'的拍摄数据吗,需要继续吗?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+  })
+  reNosObj.value.goods_art_no = goods_art_no
+  reNosObj.value.action = '执行左脚程序'
+  if(item.items && typeof item.items === 'object' && item.items[0].image_deal_mode){
+      reNosObj.value.action = '执行右脚程序'
+  }
+  del({goods_art_nos:[goods_art_no]})
+}
+
 /**
  * 检查是否可以进入下一步操作。
  */
@@ -361,7 +474,7 @@ onMounted(async () => {
     }
   });
 
-  window.addEventListener('storage', handleStorageEvent);
+/*  window.addEventListener('storage', handleStorageEvent);*/
 
 
 
@@ -396,13 +509,20 @@ onMounted(async () => {
   clientStore.ipc.on(icpList.socket.message + '_photo_take_finish', (event, result) => {
     console.log('_photo_take_finish')
     console.log(result)
-    if(result.code === 0) runLoading.value = false;
+    if(result.code === 0) {
+      runLoading.value = false;
+      runAction.value.goods_art_no = '';
+      runAction.value.action = '';
+      setTimeout(()=>{
+        showlastPhoto.value = false
+      },5000)
+    }
 
   })
 
   // 监听手动触发拍照事件
   clientStore.ipc.on(icpList.socket.message + '_handler_take_picture', async (event, result) => {
-    console.log('_photo_take_finish')
+    console.log('_handler_take_picture')
     console.log(result)
     if(result.code === 0){
       if(runLoading.value || takePictureLoading.value){
@@ -429,8 +549,35 @@ onMounted(async () => {
 })
 
 
+const onRemoteControl = (type)=>{
+  if(type == 'take_picture'){
 
-const handleStorageEvent = (e) => {
+    if(runLoading.value || takePictureLoading.value){
+      ElMessage.error('拍摄程序正在运行,请稍候')
+      return
+    }
+
+    ElMessage.success('正在拍摄中,请稍候')
+    socketStore.sendMessage({
+      type: 'handler_take_picture',
+    })
+    return;
+  }
+
+
+  if(!goods_art_no.value){
+    ElMessage.error('请先扫描货号或者手动输入货号!')
+    return;
+  }
+  let action = '执行左脚程序'
+  if(type  === 'right')  action = '执行右脚程序'
+  runGoods({
+    "action": action,
+    "goods_art_no": goods_art_no.value,
+  })
+
+}
+/*const handleStorageEvent = (e) => {
   if(e.key === 'run_mcu' && e.newValue){
     if(!goods_art_no.value){
       ElMessage.error('请先扫描货号或者手动输入货号!')
@@ -446,7 +593,7 @@ const handleStorageEvent = (e) => {
     localStorage.setItem('run_mcu','')
 
   }
-};
+};*/
 const checkInfoStore = checkInfo()
 checkInfoStore.set_blue_tooth_scan_NO('')
 watchEffect(async ()=>{
@@ -467,11 +614,49 @@ onBeforeUnmount(() => {
   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');
-  window.removeEventListener('storage', handleStorageEvent);
+/*  window.removeEventListener('storage', handleStorageEvent);*/
 
 
 })
 
+/*
+* 打开最近一张拍摄图
+* */
+const lastPhoto = ref({})
+const showlastPhoto = ref(false)
+const lastPhtotoName = ref({
+  0:"俯视图",
+  1:"侧视图",
+  2:"后视图",
+  3:"鞋底",
+  4:"内里",
+})
+const  getLastPhotoRecord = async ()=>{
+  if(goodsList.value && goodsList.value.length === 0) return;
+  clientStore.ipc.removeAllListeners(icpList.takePhoto.getLastPhotoRecord);
+  clientStore.ipc.send(icpList.takePhoto.getLastPhotoRecord,);
+
+  clientStore.ipc.on(icpList.takePhoto.getLastPhotoRecord, (event, result) => {
+    console.log('getLastPhotoRecord')
+    console.log(result)
+    console.log(runAction.value)
+    clientStore.ipc.removeAllListeners(icpList.takePhoto.getLastPhotoRecord);
+    if(result.code === 0){
+      if(lastPhoto.value.image_path){
+        if(  lastPhoto.value.image_path == result.data.image_path) return;
+
+        if(runAction.value.goods_art_no === result.data.goods_art_no){
+          showlastPhoto.value = true
+        }
+      }
+      lastPhoto.value = result.data
+    }else if(result.msg) {
+      ElMessage.error(result.msg)
+    }
+  });
+}
+
+
 /**
  * 打开主图详情页面。
  */
@@ -608,7 +793,7 @@ function openPhotographyDetail() {
 
           .method-container {
             background-color: rgba(247, 247, 247, 1);
-            height: 469px;
+            height: 484px;
             width: 353px;
 
             .auto-method {
@@ -652,15 +837,15 @@ function openPhotographyDetail() {
             }
 
             .scan-method {
-              width: 263px;
-              height: 246px;
-              margin: 9px 0 0 85px;
+              width: 350px;
+              height: 350px;
+              margin: 10px;
 
               .remote-control {
-                height: 246px;
-                background: url(@/assets/images/Photography/left-button.png) 0px 0px no-repeat;
-                background-size: 98px 246px;
-                width: 97px;
+                width: 350px;
+                height: 350px;
+                background: url(@/assets/images/Photography/left.png) 0px 0px no-repeat;
+                background-size: 300px 300px;
                 position: relative;
 
                 .scan-button {
@@ -736,9 +921,9 @@ function openPhotographyDetail() {
             }
 
             .input-container {
-              width: 260px;
+              width: calc(100% - 20px );
               height: 36px;
-              margin: 7px 0 42px 83px;
+              margin: 20px 10px 0;
 
               .input-item {
                 :deep(.el-input__inner){
@@ -779,8 +964,12 @@ function openPhotographyDetail() {
         .shooting-container {
           background-color: rgba(247, 247, 247, 1);
           width: 353px;
-          height: 469px;
+          height: 484px;
           margin: 28px 0 0 2px;
+          .remote-control-wrap {
+            width: 353px;
+            height: 300px;
+          }
 
           .shooting-tips {
             width: 325px;
@@ -942,13 +1131,43 @@ function openPhotographyDetail() {
         }
 
         .history-warp {
-          width: 100%;
           flex-grow: 1;
           overflow: auto;
           height: calc(100% - 125px);
 
           .history-item {
+            .history-item_image_wrap {
+              padding-bottom: 20px;
+              border-bottom: 1px solid #CCCCCC;
+            }
             .history-item_image {
+              &::before{
+                color: #bbb;
+                position: absolute;
+                left: 0;
+                right: 0;
+                top:50%;
+                margin-top: -15px;
+                line-height: 30px;
+              }
+
+              &:nth-child(1)::before{
+                content:"俯视图";
+              }
+              &:nth-child(2)::before{
+                content:"侧视图";
+              }
+              &:nth-child(3)::before{
+                content:"后视图";
+              }
+              &:nth-child(4)::before{
+                content:"鞋底";
+              }
+              &:nth-child(5)::before{
+                content:"内里";
+              }
+
+              position: relative;
               width: 70px;
               height: 70px;
               float: left;
@@ -971,11 +1190,41 @@ function openPhotographyDetail() {
 
             }
 
+
+            .el-image_view {
+              display: flex;
+              width: 100%;
+              height: 100%;
+
+              .reset-button {
+                width: 40px;
+                text-align: center;
+                height: 20px;
+                position: absolute;
+                left:50%;
+                top:50%;
+                margin-left:-20px;
+                margin-top:-10px;
+                color: #ffffff;
+                font-size: 14px;
+                background: rgba(0,0,0,0.6);
+                border-radius: 12px;
+                display: none;
+                cursor: pointer;
+              }
+              &:hover {
+                .reset-button {
+                  display: block;
+                }
+              }
+            }
             p:first-of-type {
               ::v-deep {
                 .el-loading-mask { display: block !important;}
               }
             }
+
+
           }
         }
 
@@ -1020,6 +1269,13 @@ function openPhotographyDetail() {
           }
         }
       }
+.last-photo{
+  position: fixed;
+  left: 20px;
+  top: 50px;
+  bottom: 20px;
+  right: 350px;
+  z-index: 10;
+}
 </style>
 
-<style lang="scss" scoped></style>

+ 31 - 43
frontend/src/views/RemoteControl/index.vue

@@ -1,47 +1,27 @@
 <template>
 
-
+<!--
   <headerBar
     title="遥控模拟器"
-  />
+  />-->
 
-  <div class="main-container">
-    <el-row align="middle">
-      <el-col :span="8"></el-col>
-      <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" @click="runLeft">左脚</div></el-col>
-      <el-col :span="8"><div class="button up" @click="run_take_picture">拍照</div></el-col>
-      <el-col :span="8"><div class="button up" @click="runRight">右脚</div></el-col>
-    </el-row>
-    <el-row align="middle">
-      <el-col :span="2"></el-col>
-      <el-col :span="20" class="fs-12">获取商品货号后,点击左右键启动拍摄程序,在拍摄程序结束后,可点击拍照启动单独拍照程序</el-col>
-      <el-col :span="2"></el-col>
-    </el-row>
-    <el-row align="middle">
-      <el-col :span="8"></el-col>
-      <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>
+  <div class="remote-control_main-container">
+    <div class="te-c"  style="color: #8C92A7">遥控器模拟器</div>
     <el-row align="middle" class="mar-top-50">
       <el-col :span="3"></el-col>
-      <el-col :span="8"></el-col>
-<!--      <el-col :span="8"><div class="button up">继续</div></el-col>-->
-      <el-col :span="2"></el-col>
-      <el-col :span="8"></el-col>
-<!--      <el-col :span="8"><div class="button up">停止</div></el-col>-->
+      <el-col :span="6"><div class="button up" @click="runLeft">左脚</div></el-col>
+      <el-col :span="6"><div class="button up" @click="run_take_picture">拍照</div></el-col>
+      <el-col :span="6"><div class="button up" @click="runRight">右脚</div></el-col>
       <el-col :span="3"></el-col>
     </el-row>
+    <div class="te-c mar-top-50 fs-14"  style="color: #8C92A7">左脚控制左脚鞋启动拍摄</div>
+    <div class="te-c mar-top-10 fs-14"  style="color: #8C92A7">右脚控制右脚鞋启动拍摄</div>
   </div>
 
 </template>
 
 <script setup lang="ts">
+import { defineEmits } from 'vue'
 import headerBar from '@/components/header-bar/index.vue'
 import icpList from '@/utils/ipc'
 import client from "@/stores/modules/client";
@@ -51,47 +31,55 @@ const clientStore = client();
 // 初始化 WebSocket 状态管理
 const socketStore = socket()
 
+const emit = defineEmits(['onRemoteControl'])
 const runLeft = async () => {
-  localStorage.setItem('run_mcu','left')
+  emit('onRemoteControl','left')
 }
 
 
 const runRight = async () => {
-  localStorage.setItem('run_mcu','right')
+  emit('onRemoteControl','right')
 }
 
 
 
 const run_take_picture = () => {
-  socketStore.sendMessage({
-    type: 'handler_take_picture',
-  })
+  emit('onRemoteControl','take_picture')
 }
 
 </script>
 
 <style scoped lang="scss">
-.main-container {
+.remote-control_main-container {
   background: #EAECED;
-  height: calc(100vh - 30px);
+  height: 358px;
+  width: 300px;
+  margin:  0 auto;
+  margin-top: 10px;
   padding-top: 30px;
+  background: url(@/assets/images/Photography/yk.png) 0px 0px no-repeat;
+  background-size: 300px 358px;
+
 }
 .button {
-  width: 68px;
-  height: 68px;
+  width: 60px;
+  height: 60px;
   background: #fff;
-  border-radius: 68px;
-  line-height: 68px;
+  border-radius: 60px;
+  line-height: 60px;
   color: #474747;
   margin: 0 auto;
   box-shadow: 0 2px 8px 0 rgba(0,0,0,0.2);
+
+  background: url(@/assets/images/Photography/hui.png) 0px 0px no-repeat;
+  background-size: 60px 60px;
 }
 .el-row {
   min-height: 100px;
 }
 .button:hover {
-  background: #2957FF;
-  color: #fff;
+  background: url(@/assets/images/Photography/lan.png) 0px 0px no-repeat;
+  background-size: 60px 60px;
   cursor: pointer;
 }
 </style>

+ 81 - 0
frontend/src/views/Setting/components/EditDialog.vue

@@ -0,0 +1,81 @@
+<template>
+  <el-dialog
+    v-model="visible"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    custom-class="editRowDialog"
+    fullscreen
+    align-center
+    append-to-body
+    :show-close="false"
+  >
+      <check
+          v-if="visible"
+          @onClose="onClose"
+          :addRowData="addRowData"
+          @confirm="onConfirm"
+         :id="id"
+      />
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref, defineProps, defineEmits , watch } from 'vue'
+import check from '@/views/Photography/check'
+
+
+const props = defineProps({
+  id:{
+    type: Number||String,
+    default: 0
+  },
+  modelValue: {
+    type: Boolean,
+    default: false
+  },
+  addRowData:{
+    type: Object,
+    default: () => {
+      return { }
+    }
+  }
+})
+
+const emit = defineEmits<{
+  (e: 'update:modelValue', value: boolean): void
+  (e: 'confirm'): void
+}>()
+
+const visible = ref(props.modelValue)
+
+// 监听visible变化
+watch(() => visible.value, (newVal) => {
+  console.log(visible.value)
+  emit('update:modelValue', newVal)
+})
+
+const onClose = ()=>{
+  console.log('onClose')
+  visible.value = false;
+}
+const onConfirm = ()=>{
+  console.log('onConfirm')
+  emit('confirm')
+  onClose()
+}
+
+</script>
+<style lang="scss">
+.editRowDialog {
+  .el-dialog__body { background: rgba(0,0,0,.5)}
+  .check-wrap {
+    border-radius: 10px;
+    overflow: hidden;
+  }
+}
+
+</style>
+
+<style lang="scss" scoped>
+
+</style>

+ 21 - 318
frontend/src/views/Setting/index.vue

@@ -9,21 +9,11 @@
         <img src="@/assets/images/setting/icon1a.png" class="nav-icon" v-else/>
         <span>基础配置</span>
       </div>
-<!--      <div class="nav-item" :class="{'active': activeIndex === 1}" @click="activeIndex = 1">
-        <img src="@/assets/images/setting/icon2.png" class="nav-icon" v-if="activeIndex !== 1"/>
-        <img src="@/assets/images/setting/icon2a.png" class="nav-icon" v-else/>
-        <span>拍照设置</span>
-      </div>-->
       <div class="nav-item" :class="{'active': activeIndex === 2}" @click="activeIndex = 2">
         <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/>
         <span>其他设置</span>
       </div>
-<!--      <div class="nav-item" :class="{'active': activeIndex === 3}" @click="activeIndex = 3">
-        <img src="@/assets/images/setting/icon4.png" class="nav-icon" v-if="activeIndex !== 3"/>
-        <img src="@/assets/images/setting/icon4a.png" class="nav-icon" v-else/>
-        <span>遥控器设置</span>
-      </div>-->
       <div class="nav-item" :class="{'active': activeIndex === 4}" @click="activeIndex = 4">
         <img src="@/assets/images/setting/icon4.png" class="nav-icon" v-if="activeIndex !== 4"/>
         <img src="@/assets/images/setting/icon4a.png" class="nav-icon" v-else/>
@@ -32,18 +22,6 @@
     </nav>
 
     <div class="form-container">
-<!--        <div class="captureBox" v-if="activeIndex === 0">
-            <div class="form-item">
-                <label>canpture_one图片输出文件夹:</label>
-                <div class="input-group">
-                  <el-input style="width: 430px;" type="textarea" :rows="2" v-model="formData.captureOneFolder" readonly></el-input>
-                  <div class="select-btn" @click="selectFolder">
-                   <img src="@/assets/images/setting/folder.png" /> 选择文件夹
-                  </div>
-                </div>
-            </div>
-&lt;!&ndash;            <p class="error-text">capture one的导出拍照图像自动和智能拍的待处理图像自不一致,请重新选择</p>&ndash;&gt;
-          </div>-->
       <!--基础配置-->
           <div class="selectBox" v-if="activeIndex === 0">
                 <div class="form-item">
@@ -72,50 +50,6 @@
                 </div>
         </div>
       <!--基础配置-->
-      <!--拍照设置-->
-          <div class="selectBox" style="padding-top: 0px" v-if="activeIndex === 1">
-                <div class="form-item">
-                    <label>重复拍摄警告:</label>
-                    <div class="select-wrapper">
-                    <el-select v-model="formData.take_photo_configs.repart_take_photo_warning" placeholder="请选择">
-                      <el-option v-for="item in repeatWarningList" :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.take_photo_configs.single_photo_warning" placeholder="请选择">
-                      <el-option v-for="item in singleWarningList" :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.take_photo_configs.total_photo_warning" placeholder="请选择">
-                      <el-option v-for="item in totalWarningList" :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-input v-model="formData.focusTime" placeholder="请输入">
-                        <template #append>秒</template>
-                      </el-input>
-                    </div>
-                </div>-->
-                <div class="form-item">
-                    <label>拍照停留:</label>
-                    <div class="select-wrapper">
-                      <el-input v-model="formData.take_photo_configs.camera_delay" placeholder="请输入">
-                        <template #append>秒</template>
-                      </el-input>
-                    </div>
-                </div>
-        </div>
-      <!--拍照配置-->
       <!--其他设置-->
           <div class="selectBox" style="padding-top: 0px" v-if="activeIndex === 2">
                 <div class="form-item">
@@ -152,48 +86,6 @@
                 </div>-->
           </div>
       <!--其他设置-->
-          <div class="selectBox" style="padding-top: 0px" v-if="activeIndex === 3">
-                <div class="form-item">
-                    <label>接收器:</label>
-                    <div class="select-wrapper">
-                    <el-select v-model="formData.receiver" placeholder="请选择">
-                      <el-option v-for="item in receiverList" :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.left" placeholder="请选择">
-                      <el-option v-for="item in leftList" :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.right" placeholder="请选择">
-                      <el-option v-for="item in rightList" :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.up" placeholder="请选择">
-                      <el-option v-for="item in upList" :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.down" placeholder="请选择">
-                      <el-option v-for="item in downList" :key="item.value" :label="item.label" :value="item.value"></el-option>
-                    </el-select>
-                    </div>
-                </div>
-          </div>
           <div class="selectBox" style="padding-top: 0px;padding-left: 0;" v-if="activeIndex === 4">
             <el-tabs v-model="activeTab" type="card">
               <el-tab-pane label="执行左脚程序" name="left">
@@ -205,7 +97,7 @@
                 <div class="primary-btn" @click="addRow">新增一行</div>
                 <div class="normal-btn" @click="resetConfig">重新初始化</div>
               </div>
-              <el-table height="200px" :data="activeTab === 'left' ? lefttableData : righttableData" style="width: 100%" border>
+              <el-table max-height="700" :data="activeTab === 'left' ? lefttableData : righttableData" style="width: 100%" border>
                 <el-table-column prop="id" label="id" />
                 <el-table-column prop="action_name" label="步骤" />
                 <el-table-column prop="take_picture" label="是否拍照" width="200px">
@@ -216,16 +108,16 @@
                     </el-radio-group>
                   </template>
                 </el-table-column>
-                <el-table-column prop="action_index" label="排序" >
+<!--                <el-table-column prop="action_index" label="排序" >
                   <template #default="{row, $index}">
                     <a v-if="$index !== 0" class="cursor-pointer" @click="upRow(row)">上移</a>
                     <a class="mar-left-10 cursor-pointer" v-if="$index !== activeTab === 'left' ? lefttableData.length - 1 : righttableData.length - 1" @click="downRow(row)">下移</a>
                   </template>
-                </el-table-column>
+                </el-table-column>-->
                 <el-table-column prop="value" label="操作" >
                   <template #default="{row, $index}">
                     <a class="mar-right-10 cursor-pointer" @click="editRow(row, $index)">编辑</a>
-                    <a class="cursor-pointer" @click="deleteRow(row, $index)">删除</a>
+                    <a class="cursor-pointer" v-if="!row.is_system" @click="deleteRow(row, $index)">删除</a>
                   </template>
                 </el-table-column>
               </el-table>
@@ -237,89 +129,14 @@
           保存
         </button>
       </div>
-      <el-dialog custom-class="editDialog" v-model="dialogVisible" :title="editTitle" width="660px">
-        <div class="config-type">配置类型:执行{{ activeTab === 'left' ? '左脚' : '右脚' }}程序 <el-checkbox v-model="isDefault">开启运动调试</el-checkbox></div>
-        <el-form class="editForm" :model="editRowData" label-width="100px">
-          <el-form-item label="动作名称">
-            <el-input v-model="editRowData.action_name" style="width: 170px;"/>
-          </el-form-item>
-          <el-form-item label="是否拍照">
-            <el-radio-group v-model="editRowData.take_picture">
-              <el-radio :label="true">拍照</el-radio>
-              <el-radio :label="false">不拍照</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="相机高度(mm)">
-            <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',-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',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',-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">
-            <el-radio-group v-model="editRowData.shoe_upturn">
-              <el-radio :label="true">翻转</el-radio>
-              <el-radio :label="false">不翻转</el-radio>
-              </el-radio-group>
-              <a class="cursor-pointer" @click="changeNum('overturn_steering')">测试翻转</a>
-            </div>
-          </el-form-item>
-          <el-form-item label="LED灯光开光" @change="changeNum('laser_position')">
-            <el-radio-group v-model="editRowData.led_switch">
-              <el-radio :label="false">关闭</el-radio>
-              <el-radio :label="true">开启</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="对焦次数">
-            <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" @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" @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">
-              <el-radio :label="false">否</el-radio>
-              <el-radio :label="true">是</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="待用户确认照片">
-            <el-radio-group v-model="editRowData.confirm_photo">
-              <el-radio :label="false">否</el-radio>
-              <el-radio :label="true">是</el-radio>
-            </el-radio-group>
-          </el-form-item> -->
-        </el-form>
-        <template #footer>
-          <div class="btn-row">
-            <div class="normal-btn" @click="dialogVisible = false">关闭</div>
-             <div class="primary-btn" v-loading="captureLoading" @click="testShoesFlip">运行并拍照</div>
-            <div class="primary-btn" @click="saveRow">保存并关闭</div>
-          </div>
-        </template>
-      </el-dialog>
+
+    <EditDialog
+        v-if="dialogVisible"
+        v-model="dialogVisible"
+        :id="editId"
+        :addRowData="addRowData"
+        @confirm="getList"
+    />
   </div>
 </template>
 
@@ -335,6 +152,7 @@ import { useRoute, useRouter } from 'vue-router';
 import { onMounted, watch } from 'vue';
 import socket from "@/stores/modules/socket";
 import headerBar from '@/components/header-bar/index.vue';
+import EditDialog from './components/EditDialog'
 import client from "@/stores/modules/client";
 import icpList from '@/utils/ipc';
 const clientStore = client();
@@ -477,9 +295,9 @@ const lefttableData = ref([]); // 左脚配置表格数据
 const righttableData = ref([]); // 右脚配置表格数据
 const dialogVisible = ref(false); // 编辑对话框可见状态
 const editTitle = ref(''); // 编辑对话框标题
-const editRowData = ref({}); // 当前编辑行的数据
+const addRowData = ref({}); // 新增行
 const activeTab = ref('left'); // 当前激活的标签页
-const editIndex = ref(0); // 当前编辑行的索引
+const editId = ref(0); // 当前编辑行的索引
 const isDefault = ref(false); // 是否为默认配置
 
 const indexKey  ={
@@ -626,7 +444,8 @@ const saveSetting = async (index) => {
  */
 const addRow = () => {
   if (activeTab.value === 'left') {
-    editRowData.value = {
+    editId.value = -1
+    addRowData.value = {
       mode_type: '执行左脚程序',
       action_name: '',
       take_picture: false,
@@ -643,7 +462,8 @@ const addRow = () => {
     dialogVisible.value = true;
     editTitle.value = '新增步骤';
   } else {
-    editRowData.value = {
+    editId.value = -1
+    addRowData.value = {
       mode_type: '执行右脚程序',
       action_name: '',
       take_picture: false,
@@ -691,16 +511,7 @@ const getList = () => {
  */
 const editRow = (row, index) => {
   dialogVisible.value = true;
-  editTitle.value = row.action_name + '编辑';
-  clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigDetail);
-  clientStore.ipc.send(icpList.setting.getDeviceConfigDetail, {
-    id: row.id
-  });
-  clientStore.ipc.on(icpList.setting.getDeviceConfigDetail, (event, result) => {
-    editRowData.value = result.data;
-    clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigDetail);
-  });
-  editIndex.value = index;
+  editId.value = row.id
 };
 
 /**
@@ -730,25 +541,6 @@ const deleteRow = (row, index) => {
 };
 
 /**
- * 保存当前编辑的配置。
- */
-const saveRow = () => {
-  clientStore.ipc.send(icpList.setting.saveDeviceConfig, {
-    ...editRowData.value
-  });
-  clientStore.ipc.on(icpList.setting.saveDeviceConfig, (event, result) => {
-    if (result.code == 0) {
-      getList();
-      ElMessage.success('保存成功');
-      dialogVisible.value = false;
-      clientStore.ipc.removeAllListeners(icpList.setting.saveDeviceConfig);
-    } else {
-      ElMessage.error('保存失败');
-    }
-  });
-};
-
-/**
  * 重置设备配置。
  */
 const resetConfig = () => {
@@ -772,95 +564,6 @@ const resetConfig = () => {
   });
 };
 
-/**
- * 修改设备配置数值。
- * @param {string} type - 配置类型
- */
-async function changeNum(type, min, max) {
-    let socketValue = {
-      'camera_high_motor': 'camera_height',
-      'turntable_steering': 'turntable_angle',
-      'turntable_position_motor': 'turntable_position',
-      'camera_steering': 'camera_angle',
-      'overturn_steering': 'shoe_upturn',
-      '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.value.led_switch ? "1" : "0") : editRowData.value[socketValue[type]]
-      }
-    });
-  }
-}
-
-
-/*测试拍照*/
-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)
-
-
-    })
-  }
-}
 
 
 
@@ -877,7 +580,7 @@ body {
 }
 .container {
   margin: 0 auto;
-  height: calc(100vh - 30px);
+  min-height: calc(100vh - 30px);
   background: #EAECED;
   display: flex;
   flex-direction: column;

+ 1104 - 0
frontend/src/views/Setting/index_old.vue

@@ -0,0 +1,1104 @@
+<template>
+  <headerBar
+    title="设置"
+  />
+  <div class="container">
+    <nav class="settings-nav">
+      <div class="nav-item" :class="{'active': activeIndex === 0}" @click="activeIndex = 0">
+        <img src="@/assets/images/setting/icon1.png" class="nav-icon" v-if="activeIndex !== 0"/>
+        <img src="@/assets/images/setting/icon1a.png" class="nav-icon" v-else/>
+        <span>基础配置</span>
+      </div>
+<!--      <div class="nav-item" :class="{'active': activeIndex === 1}" @click="activeIndex = 1">
+        <img src="@/assets/images/setting/icon2.png" class="nav-icon" v-if="activeIndex !== 1"/>
+        <img src="@/assets/images/setting/icon2a.png" class="nav-icon" v-else/>
+        <span>拍照设置</span>
+      </div>-->
+      <div class="nav-item" :class="{'active': activeIndex === 2}" @click="activeIndex = 2">
+        <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/>
+        <span>其他设置</span>
+      </div>
+<!--      <div class="nav-item" :class="{'active': activeIndex === 3}" @click="activeIndex = 3">
+        <img src="@/assets/images/setting/icon4.png" class="nav-icon" v-if="activeIndex !== 3"/>
+        <img src="@/assets/images/setting/icon4a.png" class="nav-icon" v-else/>
+        <span>遥控器设置</span>
+      </div>-->
+      <div class="nav-item" :class="{'active': activeIndex === 4}" @click="activeIndex = 4">
+        <img src="@/assets/images/setting/icon4.png" class="nav-icon" v-if="activeIndex !== 4"/>
+        <img src="@/assets/images/setting/icon4a.png" class="nav-icon" v-else/>
+        <span>左右脚程序设置</span>
+      </div>
+    </nav>
+
+    <div class="form-container">
+<!--        <div class="captureBox" v-if="activeIndex === 0">
+            <div class="form-item">
+                <label>canpture_one图片输出文件夹:</label>
+                <div class="input-group">
+                  <el-input style="width: 430px;" type="textarea" :rows="2" v-model="formData.captureOneFolder" readonly></el-input>
+                  <div class="select-btn" @click="selectFolder">
+                   <img src="@/assets/images/setting/folder.png" /> 选择文件夹
+                  </div>
+                </div>
+            </div>
+&lt;!&ndash;            <p class="error-text">capture one的导出拍照图像自动和智能拍的待处理图像自不一致,请重新选择</p>&ndash;&gt;
+          </div>-->
+      <!--基础配置-->
+          <div class="selectBox" v-if="activeIndex === 0">
+                <div class="form-item">
+                    <label>主图尺寸:</label>
+                    <div class="select-wrapper">
+                    <el-select v-model="formData.basic_configs.main_image_size" placeholder="请选择">
+                      <el-option v-for="item in mainImageSizeList" :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.basic_configs.image_out_format" placeholder="请选择">
+                      <el-option v-for="item in imageFormatList" :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.basic_configs.image_sharpening" placeholder="请选择">
+                      <el-option v-for="item in imageSharpeningList" :key="item.value" :label="item.label" :value="item.value"></el-option>
+                    </el-select>
+                    </div>
+                </div>
+        </div>
+      <!--基础配置-->
+      <!--拍照设置-->
+          <div class="selectBox" style="padding-top: 0px" v-if="activeIndex === 1">
+                <div class="form-item">
+                    <label>重复拍摄警告:</label>
+                    <div class="select-wrapper">
+                    <el-select v-model="formData.take_photo_configs.repart_take_photo_warning" placeholder="请选择">
+                      <el-option v-for="item in repeatWarningList" :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.take_photo_configs.single_photo_warning" placeholder="请选择">
+                      <el-option v-for="item in singleWarningList" :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.take_photo_configs.total_photo_warning" placeholder="请选择">
+                      <el-option v-for="item in totalWarningList" :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-input v-model="formData.focusTime" placeholder="请输入">
+                        <template #append>秒</template>
+                      </el-input>
+                    </div>
+                </div>-->
+                <div class="form-item">
+                    <label>拍照停留:</label>
+                    <div class="select-wrapper">
+                      <el-input v-model="formData.take_photo_configs.camera_delay" placeholder="请输入">
+                        <template #append>秒</template>
+                      </el-input>
+                    </div>
+                </div>
+        </div>
+      <!--拍照配置-->
+      <!--其他设置-->
+          <div class="selectBox" style="padding-top: 0px" v-if="activeIndex === 2">
+                <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>
+      <!--其他设置-->
+          <div class="selectBox" style="padding-top: 0px" v-if="activeIndex === 3">
+                <div class="form-item">
+                    <label>接收器:</label>
+                    <div class="select-wrapper">
+                    <el-select v-model="formData.receiver" placeholder="请选择">
+                      <el-option v-for="item in receiverList" :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.left" placeholder="请选择">
+                      <el-option v-for="item in leftList" :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.right" placeholder="请选择">
+                      <el-option v-for="item in rightList" :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.up" placeholder="请选择">
+                      <el-option v-for="item in upList" :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.down" placeholder="请选择">
+                      <el-option v-for="item in downList" :key="item.value" :label="item.label" :value="item.value"></el-option>
+                    </el-select>
+                    </div>
+                </div>
+          </div>
+          <div class="selectBox" style="padding-top: 0px;padding-left: 0;" v-if="activeIndex === 4">
+            <el-tabs v-model="activeTab" type="card">
+              <el-tab-pane label="执行左脚程序" name="left">
+              </el-tab-pane>
+              <el-tab-pane label="执行右脚程序" name="right"></el-tab-pane>
+            </el-tabs>
+            <div class="form-table">
+              <div class="btnBox">
+                <div class="primary-btn" @click="addRow">新增一行</div>
+                <div class="normal-btn" @click="resetConfig">重新初始化</div>
+              </div>
+              <el-table height="200px" :data="activeTab === 'left' ? lefttableData : righttableData" style="width: 100%" border>
+                <el-table-column prop="id" label="id" />
+                <el-table-column prop="action_name" label="步骤" />
+                <el-table-column prop="take_picture" label="是否拍照" width="200px">
+                  <template #default="scope">
+                    <el-radio-group v-model="scope.row.take_picture">
+                      <el-radio :label="true">拍照</el-radio>
+                      <el-radio :label="false">不拍照</el-radio>
+                    </el-radio-group>
+                  </template>
+                </el-table-column>
+                <el-table-column prop="action_index" label="排序" >
+                  <template #default="{row, $index}">
+                    <a v-if="$index !== 0" class="cursor-pointer" @click="upRow(row)">上移</a>
+                    <a class="mar-left-10 cursor-pointer" v-if="$index !== activeTab === 'left' ? lefttableData.length - 1 : righttableData.length - 1" @click="downRow(row)">下移</a>
+                  </template>
+                </el-table-column>
+                <el-table-column prop="value" label="操作" >
+                  <template #default="{row, $index}">
+                    <a class="mar-right-10 cursor-pointer" @click="editRow(row, $index)">编辑</a>
+                    <a class="cursor-pointer" @click="deleteRow(row, $index)">删除</a>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+          </div>
+    </div>
+      <div class="text-center mt-8">
+        <button class="bg-gradient-to-r from-primary" @click="saveSetting(activeIndex)" v-if="activeIndex !== 4">
+          保存
+        </button>
+      </div>
+      <el-dialog custom-class="editDialog" v-model="dialogVisible" :title="editTitle" width="660px">
+        <div class="config-type">配置类型:执行{{ activeTab === 'left' ? '左脚' : '右脚' }}程序 <el-checkbox v-model="isDefault">开启运动调试</el-checkbox></div>
+        <el-form class="editForm" :model="editRowData" label-width="100px">
+          <el-form-item label="动作名称">
+            <el-input v-model="editRowData.action_name" style="width: 170px;"/>
+          </el-form-item>
+          <el-form-item label="是否拍照">
+            <el-radio-group v-model="editRowData.take_picture">
+              <el-radio :label="true">拍照</el-radio>
+              <el-radio :label="false">不拍照</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="相机高度(mm)">
+            <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',-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',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',-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">
+            <el-radio-group v-model="editRowData.shoe_upturn">
+              <el-radio :label="true">翻转</el-radio>
+              <el-radio :label="false">不翻转</el-radio>
+              </el-radio-group>
+              <a class="cursor-pointer" @click="changeNum('overturn_steering')">测试翻转</a>
+            </div>
+          </el-form-item>
+          <el-form-item label="LED灯光开光" @change="changeNum('laser_position')">
+            <el-radio-group v-model="editRowData.led_switch">
+              <el-radio :label="false">关闭</el-radio>
+              <el-radio :label="true">开启</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="对焦次数">
+            <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" @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" @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">
+              <el-radio :label="false">否</el-radio>
+              <el-radio :label="true">是</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="待用户确认照片">
+            <el-radio-group v-model="editRowData.confirm_photo">
+              <el-radio :label="false">否</el-radio>
+              <el-radio :label="true">是</el-radio>
+            </el-radio-group>
+          </el-form-item> -->
+        </el-form>
+        <template #footer>
+          <div class="btn-row">
+            <div class="normal-btn" @click="dialogVisible = false">关闭</div>
+             <div class="primary-btn" v-loading="captureLoading" @click="testShoesFlip">运行并拍照</div>
+            <div class="primary-btn" @click="saveRow">保存并关闭</div>
+          </div>
+        </template>
+      </el-dialog>
+  </div>
+</template>
+
+<script setup>
+/**
+ * Vue组件逻辑部分,包含与设备配置相关的功能。
+ * 主要功能包括:表单数据管理、设备配置列表获取、新增/编辑/删除步骤、保存配置等。
+ */
+
+// 引入Vue相关功能和第三方库
+import { ref, reactive } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { onMounted, watch } from 'vue';
+import socket from "@/stores/modules/socket";
+import headerBar from '@/components/header-bar/index.vue';
+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();
+const router = useRouter();
+
+// 定义响应式变量
+const folderPath = ref(''); // 文件夹路径
+const activeIndex = ref(0); // 当前激活的索引
+const socketStore = socket(); // WebSocket状态管理实例
+
+/**
+ * 表单数据对象,用于存储设备配置信息。
+ */
+const formData = reactive({
+  //基础配置
+  basic_configs:{
+    "main_image_size": "",//主图尺寸
+    "image_out_format": "",//图片输出格式
+    "image_sharpening": "" //图片锐化
+  },
+  //拍照配置
+  take_photo_configs:{
+    "repart_take_photo_warning": false,//重复拍摄警告
+    "single_photo_warning": "",//单次张数警告
+    "total_photo_warning": "",//累计拍照警告
+    "camera_delay": ""//拍照停留
+  },
+  other_configs:{
+    "product_type": "",//产品类型
+    "cutout_mode": "",//默认抠图模式
+    "device_speed": "",//设备运动速度
+    "running_mode": "" //运行模式
+  },
+  captureOneFolder: '', // Capture One文件夹路径
+  mainImageSize: '', // 主图尺寸
+  imageFormat: '', // 图片格式
+  imageSharpening: '', // 图片锐化
+  repeatWarning: '', // 重复警告
+  singleWarning: '', // 单次警告
+  totalWarning: '', // 总警告
+  focusTime: '', // 对焦时间
+  photoTime: '', // 拍照时间
+  productType: '', // 产品类型
+  defaultCutoutMode: '', // 默认抠图模式
+  deviceSpeed: '', // 设备速度
+  runMode: '', // 运行模式
+  receiver: '', // 接收器类型
+  left: '', // 左脚配置
+  right: '', // 右脚配置
+  up: '', // 上移配置
+  down: '', // 下移配置
+});
+
+// 配置选项列表
+const mainImageSizeList = ref([
+  { label: '800', value: '800' },
+  { label: '1024', value: '1024' },
+  { label: '1200', value: '1200' },
+  { label: '1600', value: '1600' },
+]);
+const imageFormatList = ref([
+  { label: 'jpg', value: 'jpg' },
+  { label: 'png', value: 'png' },
+  { label: 'jpeg', value: 'jpeg' },
+]);
+const imageSharpeningList = ref([
+  { label: '0', value: '0' },
+  { label: '1', value: '1' },
+  { label: '2', value: '2' },
+  { label: '3', value: '3' },
+]);
+const repeatWarningList = ref([
+  { label: '关闭', value: false },
+  { label: '开启', value: true },
+]);
+const singleWarningList = ref([
+  { label: '11', value: '11' },
+  { label: '12', value: '12' },
+  { label: '13', value: '13' },
+]);
+const totalWarningList = ref([
+  { label: '1.0', value: '1.0' },
+  { label: '1.5', value: '1.5' },
+  { label: '2.0', value: '2.0' },
+]);
+const productTypeList = ref([
+  { label: '鞋类', value: '鞋类' },
+  { label: '服装', value: '服装' },
+  { label: '箱包', value: '箱包' },
+]);
+const defaultCutoutModeList = ref([
+  { label: '普通抠图', value: '普通抠图' },
+  { label: '精细化抠图', value: '精细化抠图' },
+]);
+const deviceSpeedList = ref([
+  { label: '一档', value: '1' },
+  { label: '二档', value: '2' },
+  { label: '三档', value: '3' },
+]);
+const runModeList = ref([
+  { label: '普通模式', value: '普通模式' },
+  { label: '待用户确认模式', value: '待用户确认模式' }
+]);
+const receiverList = ref([
+  { label: '蓝牙', value: '1' },
+  { label: '2.4G', value: '2' },
+  { label: '5.8G', value: '3' },
+]);
+const leftList = ref([
+  { label: '左脚', value: '1' },
+  { label: '右脚', value: '2' },
+  { label: '左右脚', value: '3' },
+]);
+const rightList = ref([
+  { label: '左脚', value: '1' },
+  { label: '右脚', value: '2' },
+  { label: '左右脚', value: '3' },
+]);
+const upList = ref([
+  { label: '上移', value: '1' },
+  { label: '下移', value: '2' },
+  { label: '左右移', value: '3' },
+]);
+const downList = ref([
+  { label: '上移', value: '1' },
+  { label: '下移', value: '2' },
+  { label: '左右移', value: '3' },
+]);
+
+// 表格数据和对话框状态
+const lefttableData = ref([]); // 左脚配置表格数据
+const righttableData = ref([]); // 右脚配置表格数据
+const dialogVisible = ref(false); // 编辑对话框可见状态
+const editTitle = ref(''); // 编辑对话框标题
+const editRowData = ref({}); // 当前编辑行的数据
+const activeTab = ref('left'); // 当前激活的标签页
+const editIndex = ref(0); // 当前编辑行的索引
+const isDefault = ref(false); // 是否为默认配置
+
+const indexKey  ={
+  0:"basic_configs",
+  1:"take_photo_configs",
+  2:"other_configs",
+}
+
+/**
+ * 监听路由参数变化,更新activeIndex和activeTab。
+ */
+watch(() => route.query.type, async (newType,oldType) => {
+
+  if(['0','1','2'].includes(oldType)){
+    await  saveSetting(oldType)
+  }
+  const typeValue = parseInt(newType) || 0;
+  switch (typeValue) {
+      case 4:
+        activeTab.value = 'left';
+        clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigList);
+        clientStore.ipc.send(icpList.setting.getDeviceConfigList, {
+          mode_type: '执行左脚程序'
+        });
+        clientStore.ipc.on(icpList.setting.getDeviceConfigList, (event, result) => {
+          if (result?.data?.list) {
+            lefttableData.value = result.data.list;
+          }
+          clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigList);
+        });
+        break;
+      default:
+        clientStore.ipc.removeAllListeners(icpList.setting.getSysConfig);
+        clientStore.ipc.send(icpList.setting.getSysConfig,{
+          key: indexKey[typeValue]
+        });
+        clientStore.ipc.on(icpList.setting.getSysConfig, (event, result) => {
+          if(result.code == 0 && result.data){
+            formData[indexKey[typeValue]] = result.data
+          }
+          console.log('icpList.setting.getSysConfig')
+          console.log(result)
+          clientStore.ipc.removeAllListeners(icpList.setting.getSysConfig);
+        });
+        break;
+    }
+}, { immediate: true });
+
+/**
+ * 监听activeTab变化,获取对应标签页的设备配置列表。
+ */
+watch(() => activeTab.value, (newTab) => {
+  if (newTab === 'left') {
+    clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigList);
+    clientStore.ipc.send(icpList.setting.getDeviceConfigList, {
+      mode_type: '执行左脚程序'
+    });
+    clientStore.ipc.on(icpList.setting.getDeviceConfigList, (event, result) => {
+      if (result?.data?.list) {
+        lefttableData.value = result.data.list;
+      }
+      clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigList);
+    });
+  } else {
+    clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigList);
+    clientStore.ipc.send(icpList.setting.getDeviceConfigList, {
+      mode_type: '执行右脚程序'
+    });
+    clientStore.ipc.on(icpList.setting.getDeviceConfigList, (event, result) => {
+      if (result?.data?.list) {
+        righttableData.value = result.data.list;
+      }
+      clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigList);
+    });
+  }
+});
+
+/**
+ * 监听activeIndex变化,更新URL中的查询参数。
+ */
+watch(() => activeIndex.value, (newIndex) => {
+  router.push({
+    query: {
+      ...route.query,
+      type: newIndex.toString()
+    }
+  });
+});
+
+/**
+ * 组件挂载时初始化activeIndex。
+ */
+onMounted(() => {
+  if (route.query.type) {
+    const typeValue = parseInt(route.query.type);
+    if (!isNaN(typeValue) && typeValue >= 0 && typeValue <= 3) {
+      activeIndex.value = typeValue;
+    }
+  }
+
+
+});
+
+/**
+ * 打开文件夹选择对话框并更新表单数据。
+ */
+const selectFolder = () => {
+  clientStore.ipc.removeAllListeners(icpList.utils.openDirectory);
+  clientStore.ipc.send(icpList.utils.openDirectory);
+  clientStore.ipc.on(icpList.utils.openDirectory, async (event, result) => {
+    formData.captureOneFolder = result;
+    clientStore.ipc.removeAllListeners(icpList.utils.openDirectory);
+  });
+};
+
+/**
+ * 保存当前表单配置。
+ */
+const saveSetting = async (index) => {
+
+  await new Promise((resolve, reject) => {
+
+    clientStore.ipc.removeAllListeners(icpList.setting.updateSysConfigs);
+    clientStore.ipc.send(icpList.setting.updateSysConfigs,{
+      key: indexKey[index],
+      value:JSON.stringify({
+        ...formData[indexKey[index]]
+      })
+    });
+    clientStore.ipc.on(icpList.setting.updateSysConfigs, async (event, result) => {
+      clientStore.ipc.removeAllListeners(icpList.setting.updateSysConfigs);
+      if(result.code === 0 && result.msg){
+        resolve(result)
+      }
+
+    });
+  });
+
+
+};
+
+/**
+ * 新增一行配置。
+ */
+const addRow = () => {
+  if (activeTab.value === 'left') {
+    editRowData.value = {
+      mode_type: '执行左脚程序',
+      action_name: '',
+      take_picture: false,
+      camera_height: 0,
+      camera_angle: 0,
+      turntable_position: 0,
+      turntable_angle: 0,
+      shoe_upturn: false,
+      led_switch: false,
+      number_focus: 0,
+      pre_delay: 0,
+      after_delay: 0,
+    };
+    dialogVisible.value = true;
+    editTitle.value = '新增步骤';
+  } else {
+    editRowData.value = {
+      mode_type: '执行右脚程序',
+      action_name: '',
+      take_picture: false,
+      camera_height: 0,
+      camera_angle: 0,
+      turntable_position: 0,
+      turntable_angle: 0,
+      shoe_upturn: false,
+      led_switch: false,
+      number_focus: 0,
+      pre_delay: 0,
+      after_delay: 0,
+    };
+    dialogVisible.value = true;
+    editTitle.value = '新增步骤';
+  }
+};
+
+/**
+ * 获取设备配置列表。
+ */
+const getList = () => {
+  clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigList);
+  clientStore.ipc.send(icpList.setting.getDeviceConfigList, {
+    mode_type: activeTab.value === 'left' ? '执行左脚程序' : '执行右脚程序'
+  });
+  clientStore.ipc.on(icpList.setting.getDeviceConfigList, (event, result) => {
+    if (result.code == 0) {
+      if (activeTab.value === 'left') {
+        lefttableData.value = result.data.list;
+      } else {
+        righttableData.value = result.data.list;
+      }
+    } else {
+      ElMessage.error('获取列表失败');
+    }
+    clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigList);
+  });
+};
+
+/**
+ * 编辑指定行的配置。
+ * @param {Object} row - 当前行的数据
+ * @param {number} index - 当前行的索引
+ */
+const editRow = (row, index) => {
+  dialogVisible.value = true;
+  editTitle.value = row.action_name + '编辑';
+  clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigDetail);
+  clientStore.ipc.send(icpList.setting.getDeviceConfigDetail, {
+    id: row.id
+  });
+  clientStore.ipc.on(icpList.setting.getDeviceConfigDetail, (event, result) => {
+    editRowData.value = result.data;
+    clientStore.ipc.removeAllListeners(icpList.setting.getDeviceConfigDetail);
+  });
+  editIndex.value = index;
+};
+
+/**
+ * 删除指定行的配置。
+ * @param {Object} row - 当前行的数据
+ * @param {number} index - 当前行的索引
+ */
+const deleteRow = (row, index) => {
+  ElMessageBox.confirm('确定删除该步骤吗?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    clientStore.ipc.send(icpList.setting.removeDeviceConfig, {
+      id: row.id
+    });
+    clientStore.ipc.on(icpList.setting.removeDeviceConfig, (event, result) => {
+      if (result.code == 0) {
+        getList();
+        ElMessage.success('删除成功');
+      } else {
+        ElMessage.error('删除失败');
+      }
+      clientStore.ipc.removeAllListeners(icpList.setting.removeDeviceConfig);
+    });
+  });
+};
+
+/**
+ * 保存当前编辑的配置。
+ */
+const saveRow = () => {
+  clientStore.ipc.send(icpList.setting.saveDeviceConfig, {
+    ...editRowData.value
+  });
+  clientStore.ipc.on(icpList.setting.saveDeviceConfig, (event, result) => {
+    if (result.code == 0) {
+      getList();
+      ElMessage.success('保存成功');
+      dialogVisible.value = false;
+      clientStore.ipc.removeAllListeners(icpList.setting.saveDeviceConfig);
+    } else {
+      ElMessage.error('保存失败');
+    }
+  });
+};
+
+/**
+ * 重置设备配置。
+ */
+const resetConfig = () => {
+  ElMessageBox.confirm(`确定初始化执行${activeTab.value === 'left' ? '左脚' : '右脚'}程序吗?`, '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    clientStore.ipc.send(icpList.setting.resetDeviceConfig, {
+      mode_type: activeTab.value === 'left' ? '执行左脚程序' : '执行右脚程序'
+    });
+    clientStore.ipc.on(icpList.setting.resetDeviceConfig, (event, result) => {
+      if (result.code == 0) {
+        getList();
+        ElMessage.success('重置成功');
+      } else {
+        ElMessage.error('重置失败');
+      }
+      clientStore.ipc.removeAllListeners(icpList.setting.resetDeviceConfig);
+    });
+  });
+};
+
+
+
+
+</script>
+
+<style lang="scss">
+.el-image-viewer__wrapper{
+  z-index: 9999 !important;
+}
+</style>
+<style lang="scss" scoped>
+body {
+  background: #EAECED;
+}
+.container {
+  margin: 0 auto;
+  height: calc(100vh - 30px);
+  background: #EAECED;
+  display: flex;
+  flex-direction: column;
+  justify-content: flex-start;
+}
+.settings-nav {
+  display: flex;
+  justify-content: center;
+  gap: 40px;
+  padding: 30px 0;
+  background: #EDEFF0;
+  border-bottom: 1px solid rgba(0,0,0,0.1);
+}
+.nav-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  cursor: pointer;
+  color: #666;
+  transition: all 0.3s;
+  width: 100px;
+  justify-content: center;
+  height: 70px;
+  font-size: 14px;
+}
+.nav-item.active {
+    background: #DFE2E3;
+    border-radius: 10px;
+    color: #2957FF;
+}
+.nav-item i {
+  font-size: 24px;
+  margin-bottom: 8px;
+  width: 40px;
+  height: 40px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background: #fff;
+  border-radius: 8px;
+}
+.form-container {
+  border-radius: 12px;
+  padding: 30px;
+  padding-top: 10px;
+  width: 800px;
+  margin: 0 auto;
+  height: 306px;
+}
+.form-item {
+  margin-bottom: 24px;
+  display: flex;
+  align-items: center;
+}
+.form-item label {
+  display: block;
+  min-width: 98px;
+  text-align: right;
+  font-size: 14px;
+  color: #1A1A1A;
+}
+.input-group {
+  display: flex;
+  gap: 12px;
+}
+.input-group input {
+  flex: 1;
+  border: 1px solid #e5e7eb;
+  border-radius: 4px;
+  padding: 8px 12px;
+  font-size: 14px;
+}
+.select-wrapper {
+  position: relative;
+  width: 200px;
+  :deep(.el-input__inner){
+    border-radius: 6px;
+  }
+}
+.error-text {
+  color: #dc2626;
+  font-size: 12px;
+  margin-top: 4px;
+}
+.from-primary{
+    width: 150px;
+    height: 40px;
+    color: #FFFFFF;
+    background: linear-gradient( 135deg, #2FB0FF 0%, #B863FB 100%);
+}
+.nav-icon{
+    width: 32px;
+    height: 32px;
+}
+.captureBox{
+  border-bottom: 1px solid rgba(0,0,0,0.1);
+}
+.selectBox{
+    padding-top: 30px;
+    padding-left: 100px;
+    border-bottom: 1px solid rgba(0,0,0,0.1);
+    :deep(.el-tabs__header){
+      padding-left: 0;
+    }
+    :deep(.el-tabs--card>.el-tabs__header){
+      border-bottom: 1px solid #CCCCCC;
+    }
+    :deep(.el-tabs__item){
+      height: 30px;
+      line-height: 30px;
+    }
+    :deep(.el-tabs__nav-wrap){
+      margin-bottom: 0;
+    }
+    :deep(.el-tabs__item.is-active){
+      color: #333;
+      font-weight: bold;
+      background: #fff;
+    }
+}
+.select-btn{
+  display: flex;
+  align-items: center;
+  flex-shrink: 0;
+  width: 120px;
+  height: 30px;
+  background: #DFE2E3;
+  border-radius: 6px;
+  justify-content: center;
+  font-size: 14px;
+  color: #2957FF;
+  gap: 5px;
+  img{
+    width: 16px;
+    height: 16px;
+  }
+}
+.mt-8{
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 30px 0;
+  height: 100px;
+}
+.form-table{
+  margin-top: 10px;
+  .btnBox{
+    display: flex;
+    align-items: center;
+    margin-bottom: 12px;
+  }
+  :deep(.el-table .el-table__header){
+    padding: 0;
+    height: 30px;
+    .el-table__cell{
+      background: #F1F4FF;
+    }
+  }
+  :deep(.el-table .el-table__cell){
+    padding: 0;
+    text-align: center;
+  }
+  :deep(.el-table__row) {
+    height: 30px;
+    padding: 0;
+    &:nth-child(even) {
+      background: #F1F4FF;
+    }
+    &:nth-child(odd) {
+      background: #FFFFFF;
+    }
+  }
+  .primary-btn{
+    width: 80px;
+    height: 30px;
+    background: linear-gradient( 135deg, #2FB0FF 0%, #B863FB 100%);
+    border-radius: 4px;
+    color: #fff;
+    font-size: 14px;
+    text-align: center;
+    cursor: pointer;
+    line-height: 30px;
+    margin-right: 10px;
+  }
+  .normal-btn{
+    width: 80px;
+    height: 30px;
+    background: #fff;
+    border: 1px solid #CCCCCC;
+    border-radius: 4px;
+    font-size: 14px;
+    text-align: center;
+    line-height: 30px;
+    cursor: pointer;
+  }
+  .cursor-pointer{
+    cursor: pointer;
+  }
+}
+.editDialog{
+  .el-dialog__body{
+    padding: 0 !important;
+  }
+  .btn-row{
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+    gap: 10px;
+  }
+  .primary-btn{
+    width: 100px;
+    height: 30px;
+    background: linear-gradient( 135deg, #2FB0FF 0%, #B863FB 100%);
+    border-radius: 4px;
+    color: #fff;
+    font-size: 14px;
+    text-align: center;
+    line-height: 30px;
+    cursor: pointer;
+  }
+  .normal-btn{
+    width: 100px;
+    height: 30px;
+    background: #fff;
+    border: 1px solid #CCCCCC;
+    border-radius: 4px;
+    font-size: 14px;
+    text-align: center;
+    line-height: 30px;
+    cursor: pointer;
+  }
+}
+  .config-type{
+    font-size: 14px;
+    color: #333333;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    padding: 10px 0;
+    .el-checkbox{
+      margin-left: 10px;
+    }
+  }
+.editForm{
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);
+  gap: 0;
+  .flex-row{
+    display: flex;
+    align-items: center;
+    :deep(.el-radio){
+      margin-right: 6px !important;
+    }
+    :deep(.el-radio__label){
+      padding-left: 4px;
+    }
+  }
+  :deep(.el-form-item) {
+    margin-bottom: 0;
+    .el-form-item__label {
+      width: 120px !important;
+      padding-right: 0 !important;
+        background: #FBFCFF;
+        border: 1px solid #EEEEEE;
+        height: 41px;
+        line-height: 41px;
+        padding-left: 5px;
+        text-align: left;
+      }
+      .el-form-item__content {
+        width: 190px;
+        position: relative;
+        height: 41px;
+        background: #FFFFFF;
+        padding-left: 7px;
+        border: 1px solid #EEEEEE;
+        .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,
+        input[type="number"]::-webkit-outer-spin-button {
+          opacity: 1;
+          height: 28px;
+          position: absolute;
+          top: 2px;
+          right: 2px;
+          cursor: pointer;
+        }
+
+        input[type="number"] {
+          -moz-appearance: number-input; /* Firefox */
+        }
+      }
+    }
+}
+</style>