Переглянути джерело

Merge remote-tracking branch 'origin/dev-frontend_0804' into dev-frontend_yi

# Conflicts:
#	package.json
panqiuyao 3 місяців тому
батько
коміт
62abbaacc6

+ 26 - 0
electron/api/camera.js

@@ -2,6 +2,7 @@ const axios = require('axios');
 const http = require('http');
 const { net } = require('electron');
 const { post } = require('./request')
+const { windowManager } = require('node-window-manager');
 //
 const baseURL = 'http://localhost:5513/';
 // 创建 Axios 实例
@@ -131,6 +132,31 @@ module.exports = {
       url: '/close_other_window',
     })
   },
+  async minimizeSmartShooter(){
+    try {
+      // 获取所有窗口
+      const windows = windowManager.getWindows();
+      
+      // 查找SmartShooter窗口
+      const smartShooterWindow = windows.find(window => {
+        const title = window.getTitle().toLowerCase();
+        return title.includes('smartshooter') || title.includes('smart shooter');
+      });
+      
+      if (smartShooterWindow) {
+        // 最小化窗口
+        smartShooterWindow.minimize();
+        console.log('SmartShooter窗口已最小化');
+        return { success: true, message: 'SmartShooter窗口已最小化' };
+      } else {
+        console.log('未找到SmartShooter窗口');
+        return { success: false, message: '未找到SmartShooter窗口' };
+      }
+    } catch (error) {
+      console.error('最小化SmartShooter失败:', error);
+      return { success: false, message: '最小化失败: ' + error.message };
+    }
+  },
   async checkCamera(){
     if(readConfigFile().controlType === 'digiCamControl'){
       return  fetchExampleData(`?slc=get&param1=iso`)

+ 14 - 3
electron/utils/camera.js

@@ -4,7 +4,7 @@ const path = require('path');
 const fs = require('fs');
 const Log = require('ee-core/log');
 const { spawn } = require('child_process');
-const { liveShow, liveHide, setParams, capture, getParams,CMD,captureLive,closeOtherWindow } = require('../api/camera');
+const { liveShow, liveHide, setParams, capture, getParams,CMD,captureLive,closeOtherWindow,minimizeSmartShooter } = require('../api/camera');
 
 const { dialog } = require('electron'); // 引入 electron 的 dialog 模块
 const { windowManager } = require('node-window-manager');
@@ -97,7 +97,17 @@ async function openCameraControlCmd(digiCamControlPath) {
         const child = spawn(exePath);
 
         child.stdout.on('data', (data) => {
-
+          // 如果是SmartShooter,启动后最小化窗口
+          if(readConfigFile().controlType === 'SmartShooter'){
+            // 等待软件启动完成
+            setTimeout(async () => {
+              try {
+                await minimizeSmartShooter();
+              } catch (error) {
+                console.log('最小化SmartShooter失败:', error);
+              }
+            }, 1000); // 等待3秒让软件完全启动
+          }
           resolve(true)
         });
 
@@ -134,5 +144,6 @@ async function closeCameraControlTips() {
 
 module.exports = {
   checkCameraControlCmdExists,
-  closeCameraControlTips
+  closeCameraControlTips,
+  minimizeSmartShooter
 };

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

@@ -70,8 +70,15 @@
         <slot name="title">{{ title }}</slot>
       </span>
     </div>
-    <div class="header-bar__buttons" v-if="showUser">
-      <div class="header-bar__button header-bar__button__user">
+    <div class="header-bar__buttons">
+      <!-- 版本信息 - 始终显示 -->
+      <div class="header-bar__button header-bar__button__version">
+        <span class="version-text" @click="openOTA" title="点击查看版本详情">
+          当前版本:{{ currentVersion }}
+        </span>
+      </div>
+      <!-- 用户信息 - 仅在需要时显示 -->
+      <div class="header-bar__button header-bar__button__user" v-if="showUser">
         <el-dropdown>
           <span class="el-dropdown-link">
             {{ useUserInfoStore.userInfo.account_name || useUserInfoStore.userInfo.real_name || useUserInfoStore.userInfo.login_name }}
@@ -383,6 +390,20 @@ function handleOutsideClick(event: MouseEvent) {
   padding: 0 10px;
 }
 
+.header-bar__button__version {
+  padding: 0 8px;
+  margin-right: 5px;
+}
+
+.version-text {
+  font-size: 12px;
+  color: #666;
+  cursor: pointer;
+  padding: 2px 6px;
+  border-radius: 3px;
+  transition: all 0.2s ease;
+}
+
 .header-bar__button:hover {
   background-color: #e0e0e0;
 }

+ 84 - 7
frontend/src/views/Home/index.vue

@@ -1,10 +1,5 @@
 <template>
-  <headerBar title="首页"
-             :menu="
-              [{
-                type:'ota'
-              }]"
-  >
+  <headerBar title="首页">
 
     <template  #title><div @click="handleSettingClick" v-log="{ describe: { action: '点击首页标题' } }">首页</div></template>
   </headerBar>
@@ -36,11 +31,18 @@ import { ref, onMounted } from 'vue';
 import axios from 'axios';
 import client from "@/stores/modules/client";
 import icpList from '@/utils/ipc';
+import packageJson from '@/../../package.json';
+import { getRouterUrl } from '@/utils/appfun';
 
 const configInfoStore = configInfo();
 const router = useRouter();
 const loading = ref(true);
 
+// 版本检查相关
+const currentVersion = ref(packageJson.version);
+const latestVersion = ref('');
+const isLatest = ref(true);
+
 
 import socket from "@/stores/modules/socket";
 // 初始化 WebSocket 状态管理
@@ -108,13 +110,88 @@ function openResourceDirectory() {
   clientStore.ipc.send(icpList.utils.shellFun, params);
 }
 
+// 版本号比较函数
+const compareVersions = (v1, v2) => {
+  const parts1 = v1.split('.').map(Number);
+  const parts2 = v2.split('.').map(Number);
+
+  for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
+    const num1 = parts1[i] || 0;
+    const num2 = parts2[i] || 0;
 
+    if (num1 > num2) return 1;
+    if (num1 < num2) return -1;
+  }
 
+  return 0;
+};
 
+// 打开OTA窗口
+const openOTA = () => {
+  const { href } = router.resolve({
+    name: 'ota'
+  });
+
+  clientStore.ipc.removeAllListeners(icpList.utils.openMain);
+  let params = {
+    title: '版本更新',
+    width: 900,
+    height: 700,
+    frame: true,
+    id: 'ota',
+    url: getRouterUrl(href)
+  };
+  clientStore.ipc.send(icpList.utils.openMain, params);
+};
+
+// 获取版本信息并检查更新
+const checkForUpdates = async () => {
+  try {
+    // 添加时间戳避免缓存问题
+    const timestamp = new Date().getTime();
+    const response = await axios.get('https://ossimg.valimart.net/frontend/html/zhihuiyin/version.json', {
+      params: {
+        _t: timestamp
+      }
+    });
+
+    // 确保 response.data 是 JSON 数据
+    let data;
+    if (typeof response.data === 'string') {
+      data = JSON.parse(response.data);
+    } else {
+      data = response.data;
+    }
 
-// 在组件挂载时执行健康检查
+    if (data.length > 0) {
+      const latest = data[data.length - 1];
+      latestVersion.value = latest.version;
+
+      // 比较版本号
+      isLatest.value = compareVersions(currentVersion.value, latest.version) >= 0;
+
+      // 如果发现新版本,自动打开OTA窗口
+      if (!isLatest.value) {
+        openOTA();
+      }
+    }
+  } catch (error) {
+    console.error('检查版本更新失败:', error);
+    // 静默处理错误,不影响用户体验
+  }
+};
+
+
+
+
+
+// 在组件挂载时执行健康检查和版本检查
 onMounted(() => {
   checkHealth();
+  // 延迟执行版本检查,避免影响健康检查
+  setTimeout(() => {
+    checkForUpdates();
+  }, 1000);
 });
 </script>
 

+ 114 - 11
frontend/src/views/Photography/detail.vue

@@ -138,7 +138,7 @@
                     :class="{
                         active: item === form.logo_path
                     }"
-                    @click.native="form.logo_path = item"
+                    @click.native="selectLogo(item)"
             ></upload>
             <upload @input="onInput"></upload>
           </div>
@@ -161,10 +161,10 @@
 
             <div class="template-list">
               <div v-for="(template, index) in visibleTemplates" :key="index" class="template-item"
-                @click="form.selectTemplate = template" v-log="{ describe: { action: '点击选择详情模板', template_name: template.template_name } }">
+                @click="selectTemplate(template)" v-log="{ describe: { action: '点击选择详情模板', template_name: template.template_name } }">
                 <el-image :src="template.template_cover_image" fit="contain" class="cur-p"
                   style="width: 100%; display: block;" />
-                <div class="select-warp" :class="form.selectTemplate.id == template.id ? 'active' : ''">
+                <div class="select-warp" :class="form.selectTemplate?.id == template.id ? 'active' : ''">
                   <el-icon color="#FFFFFF">
                     <Select />
                   </el-icon>
@@ -178,7 +178,7 @@
 
             <div class="template-tips c-333 fs-14 line-20 te-l mar-top-20 flex left">
               <el-icon><WarningFilled /></el-icon>
-              <span class="mar-left-10">该模版图片顺序说明:{{form.selectTemplate.template_image_order}}</span>
+              <span class="mar-left-10">该模版图片顺序说明:{{form.selectTemplate?.template_image_order}}</span>
             </div>
 
             <!-- 模板下:一键上架 和 电商平台(多选) -->
@@ -357,7 +357,7 @@ import { clickLog, setLogInfo } from '@/utils/log'
 import { ElMessage, ElMessageBox } from 'element-plus'
 
 import headerBar from '@/components/header-bar/index.vue'
-import { ref, computed, reactive, onMounted, onBeforeUnmount, nextTick } from 'vue';
+import { ref, computed, reactive, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
 import { Select, EditPen } from '@element-plus/icons-vue'
 import upload from '@/components/upload'
 import client from "@/stores/modules/client";
@@ -470,6 +470,13 @@ onMounted(() => {
   loadDetailCache()
 })
 
+// 监听数据类型变化,自动保存到缓存
+watch(() => form.dataType, (newValue) => {
+  if (newValue) {
+    saveDataTypeToCache(newValue)
+  }
+})
+
 // 页面卸载时清理监听器
 onBeforeUnmount(() => {
   clientStore.ipc.removeAllListeners(icpList.socket.message + '_detail_progress');
@@ -532,6 +539,9 @@ const toggleService = (key: string) => {
   const idx = form.services.indexOf(key)
   if (idx > -1) form.services.splice(idx, 1)
   else form.services.push(key)
+  
+  // 保存服务选择状态到缓存
+  saveServicesToCache(form.services)
 }
 
 // 电商平台多选与一键上架
@@ -548,6 +558,9 @@ const scenePrompt = ref('')
 // 本地缓存键(与弹窗组件保持一致)
 const DETAIL_MODEL_CACHE_KEY = 'model_selection_cache'
 const DETAIL_SCENE_PROMPT_CACHE_KEY = 'scene_prompt_cache'
+const DETAIL_LOGO_CACHE_KEY = 'detail_logo_cache'
+const DETAIL_DATA_TYPE_CACHE_KEY = 'detail_data_type_cache'
+const DETAIL_SERVICES_CACHE_KEY = 'detail_services_cache'
 
 // 读取本地缓存
 const loadDetailCache = () => {
@@ -571,6 +584,48 @@ const loadDetailCache = () => {
       console.log(scenePrompt.value);
     }
   } catch {}
+
+  // 加载LOGO缓存
+  try {
+    const logo = localStorage.getItem(DETAIL_LOGO_CACHE_KEY)
+    if (logo) {
+      form.logo_path = logo
+      console.log('loadDetailCache - logo:', logo);
+    }
+  } catch {}
+
+  // 加载数据类型缓存
+  try {
+    const dataType = localStorage.getItem(DETAIL_DATA_TYPE_CACHE_KEY)
+    if (dataType) {
+      form.dataType = dataType
+      console.log('loadDetailCache - dataType:', dataType);
+    }
+  } catch {}
+
+  // 加载模板缓存
+  try {
+    const template = localStorage.getItem('detail_template_cache')
+    if (template) {
+      const parsed = JSON.parse(template)
+      if (parsed && parsed.id) {
+        form.selectTemplate = parsed
+        console.log('loadDetailCache - template:', parsed);
+      }
+    }
+  } catch {}
+
+  // 加载服务选择状态缓存
+  try {
+    const services = localStorage.getItem(DETAIL_SERVICES_CACHE_KEY)
+    if (services) {
+      const parsed = JSON.parse(services)
+      if (Array.isArray(parsed)) {
+        form.services = parsed
+        console.log('loadDetailCache - services:', parsed);
+      }
+    }
+  } catch {}
 }
 
 // 保存到本地缓存(仅保存必要字段)
@@ -604,12 +659,56 @@ const saveScenePromptToCache = (prompt: string) => {
   } catch {}
 }
 
+// 保存LOGO到缓存
+const saveLogoToCache = (logoPath: string) => {
+  try {
+    if (logoPath) {
+      localStorage.setItem(DETAIL_LOGO_CACHE_KEY, logoPath)
+      console.log('saveLogoToCache:', logoPath);
+    }
+  } catch {}
+}
+
+// 保存数据类型到缓存
+const saveDataTypeToCache = (dataType: string) => {
+  try {
+    if (dataType) {
+      localStorage.setItem(DETAIL_DATA_TYPE_CACHE_KEY, dataType)
+      console.log('saveDataTypeToCache:', dataType);
+    }
+  } catch {}
+}
+
+const saveServicesToCache = (services: string[]) => {
+  try {
+    localStorage.setItem(DETAIL_SERVICES_CACHE_KEY, JSON.stringify(services))
+    console.log('saveServicesToCache:', services);
+  } catch {}
+}
+
 const openModelDialog = () => {
   modelDialogVisible.value = true
 }
 const openScenePromptDialog = () => {
   scenePromptDialogVisible.value = true
 }
+
+// 选择模板
+const selectTemplate = (template: any) => {
+  form.selectTemplate = template
+  // 保存模板选择到缓存
+  try {
+    localStorage.setItem('detail_template_cache', JSON.stringify(template))
+    console.log('selectTemplate - saved to cache:', template);
+  } catch {}
+}
+
+// 选择LOGO
+const selectLogo = (logoPath: string) => {
+  form.logo_path = logoPath
+  // 保存LOGO选择到缓存
+  saveLogoToCache(logoPath)
+}
 const handleModelSelection = (models: { female: any; male: any }) => {
   selectedModels.value = models
   saveModelsToCache(models)
@@ -746,7 +845,7 @@ const openOutputDir = () => {
     clientStore.ipc.removeAllListeners(icpList.utils.shellFun);
     clientStore.ipc.send(icpList.utils.shellFun, {
       action: 'openPath',
-      params: fullPath.replaceAll('/', '\\')
+      params: fullPath.replace(/\//g, '\\')
     });
   } catch (e) {
     console.error(e)
@@ -767,7 +866,7 @@ const generate = async function () {
       action: '点击开始生成详情页',
       services: form.services,
       dataType: form.dataType,
-      template_name: form.selectTemplate.template_name,
+      template_name: form.selectTemplate?.template_name,
       goods_count: goods_art_nos.value.length,
       goods_art_nos: goods_art_nos.value
     }
@@ -820,9 +919,9 @@ const generate = async function () {
   const params = {
     goods_art_no: JSON.parse(JSON.stringify(goods_art_nos.value)),
     logo_path: form.logo_path || '',
-    temp_name: form.selectTemplate.template_id || '',
+    temp_name: form.selectTemplate?.template_id || '',
     excel_path: form.dataType == '1' ? form.excel_path : '',
-    template_image_order: form.selectTemplate.template_image_order,
+    template_image_order: form.selectTemplate?.template_image_order,
     temp_list,
     token,
     uuid: uuidStore.getUuid || '',
@@ -1039,8 +1138,10 @@ const getLogolist = async () => {
     logoList.value = result.data || []
     console.log('getLogoList')
     console.log(result.data)
-    if(logoList.value.length){
+    if(logoList.value.length && !form.logo_path){
       form.logo_path = logoList.value[0]
+      // 保存默认LOGO到缓存
+      saveLogoToCache(form.logo_path)
     }
     clientStore.ipc.removeAllListeners(icpList.generate.getLogoList);
   })
@@ -1060,6 +1161,8 @@ const addLogo = async (path) => {
       console.log(result)
       if(result.data.logo){
         form.logo_path = result.data.logo
+        // 保存新添加的LOGO到缓存
+        saveLogoToCache(form.logo_path)
         if(logoList.value.indexOf(result.data.logo) <0){
           logoList.value.push(result.data.logo)
         }
@@ -1118,7 +1221,7 @@ const handleComplete = () => {
   clientStore.ipc.removeAllListeners(icpList.utils.shellFun);
   let params = {
     action: 'openPath',
-    params: completeDirectory.value?.replaceAll('/', '\\')
+    params: completeDirectory.value?.replace(/\//g, '\\')
   }
   clientStore.ipc.send(icpList.utils.shellFun, params);
 }

+ 4 - 15
frontend/src/views/Photography/shot.vue

@@ -253,9 +253,6 @@ const menu = computed(()=>{
       },
       {
         ...generate
-      },
-      {
-        type:'ota'
       }
     ]
   }
@@ -269,9 +266,6 @@ const menu = computed(()=>{
       },
       {
         ...generate,
-      },
-      {
-        type:'ota'
       }
     ]
   }
@@ -283,8 +277,6 @@ const menu = computed(()=>{
     },
     {
       ...generate
-    },    {
-      type:'ota'
     }
   ]
 
@@ -358,11 +350,7 @@ function scheduleSegment(goodsArtNo: string) {
     if (!isGoodsStillInList(goodsArtNo)) return
     try {
       await socketStore.connectSocket();
-      console.log('segment_progress',{
-        token:tokenInfoStore.getToken,
-        uuid: uuidStore?.getUuid || '',
-        goods_art_no: [goodsArtNo],
-      })
+
       socketStore.sendMessage({
         type: 'segment_progress',
         data: {
@@ -962,9 +950,10 @@ clientStore.ipc.on(icpList.socket.message+'_smart_shooter_photo_take', async (ev
       setLogInfo(route, { action: '单张拍摄完成', goods_art_no: result.data.goods_art_no });
       if(reNosObj.value?.goods_art_no === result.data.goods_art_no){
         runLoading.value = false;
-        // 单张重拍完成且存在重拍货号时,触发抠图队列
-        scheduleSegment(result.data.goods_art_no)
       }
+
+    // 单张重拍完成且存在重拍货号时,触发抠图队列
+    scheduleSegment(result.data.goods_art_no)
     if (smartShooterTimeout) {
       clearTimeout(smartShooterTimeout);
     }

+ 2 - 2
package.json

@@ -1,7 +1,7 @@
 {
   "name": "ZhiHuiYin",
-  "version": "1.2.5",
-  "description": "支持服装拍摄",
+  "version": "1.2.7",
+  "description": "智慧拍照机是一种结合人工智能技术与传统摄影设备的创新产品,版本号为1.2.7",
   "main": "main.js",
   "scripts": {
     "dev": "ee-bin dev",