Bläddra i källkod

feat(setting): 实现配置同步功能

- 在登录成功后同步系统配置和动作配置到Python端
- 在健康检查成功后自动执行数据同步- 更新左右脚程序设置和相机配置的Tab切换逻辑-修复action_config.vue中默认选中第一个tab的问题
- 在切换公司时也触发数据同步- 添加syncAfterLogin函数用于登录后的数据同步- 在所有配置变更操作后同步到Python端
- 移除不必要的appModel条件判断- 添加调试日志便于追踪同步过程
panqiuyao 2 månader sedan
förälder
incheckning
531a4a952d

+ 2 - 0
electron/api/setting.js

@@ -111,6 +111,7 @@ module.exports = {
 
   //同步配置接口
   syncSysConfigs(data){
+    console.log("syncSysConfigs===============", data);
     return post({
       url: '/sync_sys_configs',
       data: data
@@ -121,6 +122,7 @@ module.exports = {
 
   //同步左右脚配置
   syncActions(data){
+    console.log("syncActions===============", data);
     return post({
       url: '/sync_actions',
       data: data

+ 132 - 11
frontend/src/apis/setting.ts

@@ -1,4 +1,7 @@
 import { GET,POST  } from "@/utils/http";
+import client from "@/stores/modules/client";
+import icpList from '@/utils/ipc'
+import tokenInfo from '@/stores/modules/token';
 
 //获取配置
 export async function getAllUserConfigs(data){
@@ -9,8 +12,22 @@ export async function getAllUserConfigs(data){
 
 //更新配置
 export async function setAllUserConfigs(data){
-    return POST('/api/ai_image/camera_machine/update_all_user_configs',data)
-
+    const result = await POST('/api/ai_image/camera_machine/update_all_user_configs',data);
+
+    // 同步到Python
+    try {
+        const clientStore = client();
+        const tokenInfoStore = tokenInfo();
+        const token = tokenInfoStore.getToken;
+        await clientStore.ipc.invoke(icpList.setting.syncSysConfigs, {
+            token: token || ''
+        });
+    } catch (error) {
+        console.error('同步系统配置到Python失败:', error);
+        // 同步失败不影响主流程
+    }
+
+    return result;
 }
 
 
@@ -31,28 +48,93 @@ export async function getDeviceConfigs(data){
 
 //点击切换执行配置
 export async function setLeftRightConfig(data){
-    return POST('/api/ai_image/camera_machine/update_left_right_config',data)
-
+    const result = await POST('/api/ai_image/camera_machine/update_left_right_config',data);
+
+    // 同步到Python
+    try {
+        const clientStore = client();
+        const tokenInfoStore = tokenInfo();
+        const token = tokenInfoStore.getToken;
+        await clientStore.ipc.invoke(icpList.setting.syncActions, {
+            token: token || '',
+            action: 'update',
+            data: data
+        });
+    } catch (error) {
+        console.error('同步左右脚配置到Python失败:', error);
+        // 同步失败不影响主流程
+    }
+
+    return result;
 }
 
 
 //重置配置
 export async function restConfig(data){
-    return POST('/api/ai_image/camera_machine/reset_config',data)
-
+    const result = await POST('/api/ai_image/camera_machine/reset_config',data);
+
+    // 同步到Python
+    try {
+        const clientStore = client();
+        const tokenInfoStore = tokenInfo();
+        const token = tokenInfoStore.getToken;
+        await clientStore.ipc.invoke(icpList.setting.syncActions, {
+            token: token || '',
+            action: 'reset',
+            tab_id: data.tab_id
+        });
+    } catch (error) {
+        console.error('同步重置配置到Python失败:', error);
+        // 同步失败不影响主流程
+    }
+
+    return result;
 }
 
 
 //更新顶部TOP
 export async function setTabName(data){
-    return POST('/api/ai_image/camera_machine/update_tab_name',data)
-
+    const result = await POST('/api/ai_image/camera_machine/update_tab_name',data);
+
+    // 同步到Python
+    try {
+        const clientStore = client();
+        const tokenInfoStore = tokenInfo();
+        const token = tokenInfoStore.getToken;
+        await clientStore.ipc.invoke(icpList.setting.syncActions, {
+            token: token || '',
+            action: 'rename',
+            id: data.id,
+            mode_name: data.mode_name
+        });
+    } catch (error) {
+        console.error('同步重命名配置到Python失败:', error);
+        // 同步失败不影响主流程
+    }
+
+    return result;
 }
 
 //删除可执行命令
 export async function delDviceConfig(data){
-    return POST('/api/ai_image/camera_machine/remove_device_config',data)
-
+    const result = await POST('/api/ai_image/camera_machine/remove_device_config',data);
+
+    // 同步到Python
+    try {
+        const clientStore = client();
+        const tokenInfoStore = tokenInfo();
+        const token = tokenInfoStore.getToken;
+        await clientStore.ipc.invoke(icpList.setting.syncActions, {
+            token: token || '',
+            action: 'delete',
+            id: data.id
+        });
+    } catch (error) {
+        console.error('同步删除配置到Python失败:', error);
+        // 同步失败不影响主流程
+    }
+
+    return result;
 }
 
 
@@ -74,7 +156,46 @@ export async function getDeviceConfigDetailQuery(data){
 
 //创建或者保存动作
 export async function saveDeviceConfig(data){
-    return POST('/api/ai_image/camera_machine/save_device_config',data)
+    const result = await POST('/api/ai_image/camera_machine/save_device_config',data);
+
+    // 同步到Python
+    try {
+        const clientStore = client();
+        const tokenInfoStore = tokenInfo();
+        const token = tokenInfoStore.getToken;
+        await clientStore.ipc.invoke(icpList.setting.syncActions, {
+            token: token || '',
+            action: 'save',
+            data: data
+        });
+    } catch (error) {
+        console.error('同步保存配置到Python失败:', error);
+        // 同步失败不影响主流程
+    }
+
+    return result;
+}
 
+// 登录后同步数据
+export async function syncAfterLogin(token: string) {
+    try {
+        const clientStore = client();
+        // 同步系统配置
+        await clientStore.ipc.invoke(icpList.setting.syncSysConfigs, {
+            token: token
+        });
+
+        // 同步动作配置
+        await clientStore.ipc.invoke(icpList.setting.syncActions, {
+            token: token,
+            action: 'sync_all'
+        });
+
+        console.log('登录后数据同步成功');
+    } catch (error) {
+        console.error('登录后数据同步失败:', error);
+        // 同步失败不影响登录流程
+    }
 }
 
+

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

@@ -149,6 +149,7 @@ export default defineComponent({
 import { ref, reactive, computed } from 'vue'
 import { ElMessage } from 'element-plus'
 import { getAccountCompany, selectCompany, sendCode } from '@/apis/user';
+import { syncAfterLogin } from "@/apis/setting";
 import useUserInfo from "@/stores/modules/user";
 import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
 import { publicKey } from '@/utils/publickey'
@@ -211,6 +212,8 @@ const handleLogin = async () => {
       break;
     default:
       await  useUserInfoStore.getInfo()
+      // 登录成功后同步数据
+      await syncAfterLogin(res.data.token)
       useUserInfoStore.updateLoginShow(false)
       setTimeout(()=>{
       //  window.location.reload()
@@ -247,10 +250,12 @@ async function  showCompany() {
 // 切换组织
 async function toggleCompany() {
   if (!companyId.value) return false
-  await selectCompany({
+  const res = await selectCompany({
     id: companyId.value
   })
   await  useUserInfoStore.getInfo()
+  // 选择公司后也需要同步数据
+  await syncAfterLogin(res.data.token)
   useUserInfoStore.updateLoginShow(false)
   setTimeout(()=>{
     // window.location.reload()

+ 42 - 3
frontend/src/views/Home/index.vue

@@ -31,11 +31,17 @@ import client from "@/stores/modules/client";
 import icpList from '@/utils/ipc';
 import packageJson from '@/../../package.json';
 import { getRouterUrl } from '@/utils/appfun';
+import useUserInfo from "@/stores/modules/user";
+import tokenInfo from "@/stores/modules/token";
 
-const configInfoStore = configInfo();
 const router = useRouter();
 const loading = ref(true);
 
+// 用户状态管理 - 在 onMounted 中初始化
+let configInfoStore: any;
+let useUserInfoStore: any;
+let tokenInfoStore: any;
+
 // 版本检查相关
 const currentVersion = ref(packageJson.version);
 const latestVersion = ref('');
@@ -51,6 +57,12 @@ function socketConnect(){
 }
 
 const goCheck = () => {
+  // 检查登录状态
+  if (!tokenInfoStore.getToken) {
+    useUserInfoStore.updateLoginShow(true);
+    return;
+  }
+  
   configInfoStore.updateAppModel(1);
   router.push({
     name: 'PhotographyCheck'
@@ -58,6 +70,12 @@ const goCheck = () => {
 };
 
 const goShot = () => {
+  // 检查登录状态
+  if (!tokenInfoStore.getToken) {
+    useUserInfoStore.updateLoginShow(true);
+    return;
+  }
+  
   socketConnect();
   configInfoStore.updateAppModel(2);
   router.push({
@@ -71,6 +89,22 @@ const checkHealth = async () => {
     const response = await axios.get('http://127.0.0.1:7074');
     if (response.status === 200) {
       loading.value = false; // 健康检查成功,关闭 loading
+      
+      // 健康检查成功后,如果用户已登录则执行数据同步
+      if (tokenInfoStore && tokenInfoStore.getToken) {
+        const token = tokenInfoStore.getToken;
+        if (token && token.trim() !== '') {
+          try {
+            // 导入同步函数
+            const { syncAfterLogin } = await import('@/apis/setting');
+            await syncAfterLogin(token);
+            console.log('健康检查后数据同步成功');
+          } catch (syncError) {
+            console.error('健康检查后数据同步失败:', syncError);
+            // 同步失败不影响主流程
+          }
+        }
+      }
     }
   } catch (error) {
     console.error('健康检查失败:', error);
@@ -97,9 +131,8 @@ function handleSettingClick() {
   }, 3000); // 3秒内未再次点击则重置计数器
 }
 
-const clientStore = client();
-
 function openResourceDirectory() {
+  const clientStore = client();
   clientStore.ipc.removeAllListeners(icpList.utils.shellFun);
   let params = {
     action: 'openPath',
@@ -130,6 +163,7 @@ const openOTA = () => {
     name: 'ota'
   });
 
+  const clientStore = client();
   clientStore.ipc.removeAllListeners(icpList.utils.openMain);
   let params = {
     title: '版本更新',
@@ -185,6 +219,11 @@ const checkForUpdates = async () => {
 
 // 在组件挂载时执行健康检查和版本检查
 onMounted(() => {
+  // 初始化 store
+  configInfoStore = configInfo();
+  useUserInfoStore = useUserInfo();
+  tokenInfoStore = tokenInfo();
+  
   checkHealth();
   // 延迟执行版本检查,避免影响健康检查
   setTimeout(() => {

+ 4 - 0
frontend/src/views/Setting/components/action_config.vue

@@ -164,6 +164,10 @@ const getTopList = async ()=>{
         return true;
       }
     })
+    if(!selectID.value){
+      selectID.value = tabs.value[0].id
+      activeTab.value =  tabs.value[0]
+    }
 
     getList()
   }

+ 2 - 2
frontend/src/views/Setting/index.vue

@@ -10,7 +10,7 @@
         <img src="@/assets/images/setting/icon1a.png" class="nav-icon" v-else/>
         <span>基础配置</span>
       </div>
-      <div class="nav-item" v-if="configInfoStore.appModel === 1" :class="{'active': activeIndex === 3}" @click="toggleTab(3)" v-log="{ describe: { action: '点击切换设置Tab', tab: '相机配置' } }">
+      <div class="nav-item" :class="{'active': activeIndex === 3}" @click="toggleTab(3)" v-log="{ describe: { action: '点击切换设置Tab', tab: '相机配置' } }">
         <img src="@/assets/images/setting/icon2.png" class="nav-icon" v-if="activeIndex !== 3"/>
         <img src="@/assets/images/setting/icon2a.png" class="nav-icon" v-else/>
         <span>相机配置</span>
@@ -20,7 +20,7 @@
         <img src="@/assets/images/setting/icon3a.png" class="nav-icon" v-else/>
         <span>其他设置</span>
       </div>
-      <div class="nav-item" v-if="configInfoStore.appModel === 1"  :class="{'active': activeIndex === 4}"  @click="toggleTab(4)" v-log="{ describe: { action: '点击切换设置Tab', tab: '左右脚程序设置' } }">
+      <div class="nav-item"   :class="{'active': activeIndex === 4}"  @click="toggleTab(4)" v-log="{ describe: { action: '点击切换设置Tab', tab: '左右脚程序设置' } }">
         <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>