Browse Source

Merge branch 'dev-frontend' of http://gitlab.pubdata.cn/liangyibo/CameraMachine into dev-frontend

kongwenhao 9 months ago
parent
commit
ca4897d905

+ 58 - 0
electron/api/camera.js

@@ -0,0 +1,58 @@
+const axios = require('axios')
+
+const service = axios.create()
+const baseUrl = 'http://localhost:5513/'
+const timeout = 60000
+
+
+
+function get(config = {
+  url: '',
+}) {
+  return service.get(baseUrl  + config.url, {
+    headers: config.headers || {},
+    timeout: config.timeout ||  timeout
+  })
+}
+
+
+
+module.exports = {
+  liveShow(){
+    return get({
+      url: '?CMD=LiveViewWnd_Show'
+    })
+  },
+  liveHide(){
+    return get({
+      url: '?CMD=LiveViewWnd_Hide'
+    })
+  },
+  capture(){
+    return get({
+      url: '?CMD=LiveView_Capture'
+    })
+  },
+  setParams(params){
+    return get({
+      url: `?slc=set&param1=${params.key}&param2=${params.value}`
+    })
+  },
+}
+
+
+/*
+*
+* 设置   iso
+* 光圈  aperture
+* 拍摄模式  mode
+* 快门速度  shutterSpeed
+* 白平衡    whitebalance
+* 曝光补偿  ExposureCompensation
+* 对焦模式 focusmode
+*
+*
+曝光度:exposure
+压缩:compression
+测光点:metering
+* */

+ 47 - 0
electron/api/imageMatting.js

@@ -0,0 +1,47 @@
+const { post } = require('./request')
+
+module.exports = {
+  checkSelectImages(data){
+    return post({
+      url: '/check_select_images',
+      data: data
+    })
+  },
+
+  segmentImages(data){
+    return post({
+      url: '/segment_images',
+      data: data,
+      headers:{
+        'Content-Type':"application/json",
+      }
+    })
+  },
+
+
+  //ÈĘ̈ͼ
+  modelFormSegment(data){
+    return post({
+      url: '/model_form_segment',
+      data: data,
+      headers:{
+        'Content-Type':"application/json",
+      }
+    })
+  },
+
+ // ²é½ø¶È
+  searchBacthProgress(data){
+    return post({
+      url: '/search_bacth_progress',
+      data: data,
+      headers:{
+        'Content-Type':"application/json",
+      }
+    })
+  },
+
+
+
+
+}

+ 65 - 0
electron/api/request.js

@@ -0,0 +1,65 @@
+const axios = require('axios')
+
+
+/* axios.defaults.withCredentials = true*/
+
+// create an axios instance
+const service = axios.create()
+const baseUrl = 'http://127.0.0.1:7074/'
+const basePrefixUrl = 'api'
+const timeout = 600000
+const uploadTimeout = 600000
+
+ function request(config) {
+  return service(config)
+}
+
+/* get 默认取数据 */
+ function get(config) {
+  return _get({ ...config, baseUrl })
+}
+ function getPay(config) {
+  return _get({ ...config, basePayUrl })
+}
+function _get(config = {
+  baseUrl,
+  url: '',
+  data: {}
+}) {
+
+  const thisBasePrefixUrl = config.basePrefixUrl !== undefined ? config.basePrefixUrl : basePrefixUrl
+  return service.get(config.baseUrl + thisBasePrefixUrl + config.url, {
+    params: {
+      ...config.data
+    },
+    headers: config.headers || {},
+    timeout: config.timeout ||  timeout
+  })
+}
+ function post(config) {
+  return _post({ ...config, baseUrl })
+}
+ function postPay(config) {
+  return _post({ ...config, baseUrl: basePayUrl })
+}
+/* post 默认提交数据 */
+function _post(config = {
+  baseUrl,
+  url: '',
+  data: {}
+}) {
+  const thisBasePrefixUrl = config.basePrefixUrl !== undefined ? config.basePrefixUrl : basePrefixUrl
+  return service.post(config.baseUrl + thisBasePrefixUrl + config.url, {
+    ...config.data
+  }, {
+    headers: config.headers || {},
+    timeout: config.timeout ||  timeout,
+    roles:config.roles,
+  })
+}
+
+
+module.exports = {
+  get,
+  post
+}

+ 24 - 18
electron/config/config.default.js

@@ -12,7 +12,7 @@ module.exports = (appInfo) => {
   /**
    * 开发者工具
    */
-  config.openDevTools = false;
+  config.openDevTools = true;
 
   /**
    * 应用程序顶部菜单
@@ -24,24 +24,30 @@ module.exports = (appInfo) => {
    */
   config.windowsOption = {
     title: 'EE框架',
-    width: 980,
-    height: 650,
+    width: 3840,
+    height: 2160,
     minWidth: 400,
     minHeight: 300,
+    frame: true,
+  //  fullscreen: true,
+   // titleBarStyle: 'hiddenInset', // 部分系统可能需要自定义标题栏样式
     webPreferences: {
       //webSecurity: false,
       contextIsolation: false, // false -> 可在渲染进程中使用electron的api,true->需要bridge.js(contextBridge)
       nodeIntegration: true,
       //preload: path.join(appInfo.baseDir, 'preload', 'bridge.js'),
     },
-    frame: true,
     show: false,
     icon: path.join(appInfo.home, 'public', 'images', 'logo-32.png'),
+
+    // 添加全屏配置
+  //  fullscreen: true, // 启用全屏
+  //  resizable: false // 禁用窗口缩放(全屏时通常不需要)
   };
 
   /**
    * ee框架日志
-   */  
+   */
   config.logger = {
     encoding: 'utf8',
     level: 'INFO',
@@ -51,20 +57,20 @@ module.exports = (appInfo) => {
     rotator: 'day',
     appLogName: 'ee.log',
     coreLogName: 'ee-core.log',
-    errorLogName: 'ee-error.log' 
+    errorLogName: 'ee-error.log'
   }
 
   /**
    * 远程模式-web地址
-   */    
+   */
   config.remoteUrl = {
-    enable: false,
-    url: 'http://electron-egg.kaka996.com/'
+    enable: true,
+    url: 'http://localhost:3000/home'
   };
 
   /**
    * 内置socket服务
-   */   
+   */
   config.socketServer = {
     enable: false,
     port: 7070,
@@ -82,11 +88,11 @@ module.exports = (appInfo) => {
 
   /**
    * 内置http服务
-   */     
+   */
   config.httpServer = {
     enable: false,
     https: {
-      enable: false, 
+      enable: false,
       key: '/public/ssl/localhost+1.key',
       cert: '/public/ssl/localhost+1.pem'
     },
@@ -111,11 +117,11 @@ module.exports = (appInfo) => {
 
   /**
    * 主进程
-   */     
+   */
   config.mainServer = {
     protocol: 'file://',
     indexPath: '/public/dist/index.html',
-  }; 
+  };
 
   /**
    * 硬件加速
@@ -138,7 +144,7 @@ module.exports = (appInfo) => {
    */
   config.jobs = {
     messageLog: true
-  };  
+  };
 
   /**
    * 插件功能
@@ -162,11 +168,11 @@ module.exports = (appInfo) => {
     },
     autoUpdater: {
       enable: true,
-      windows: false, 
-      macOS: false, 
+      windows: false,
+      macOS: false,
       linux: false,
       options: {
-        provider: 'generic', 
+        provider: 'generic',
         url: 'http://kodo.qiniu.com/'
       },
       force: false,

+ 12 - 4
electron/config/config.local.js

@@ -6,12 +6,11 @@
 module.exports = (appInfo) => {
   const config = {};
 
+
   /**
    * 开发者工具
    */
-  config.openDevTools = {
-    mode: 'undocked'
-  };
+  config.openDevTools = true;
 
   /**
    * 应用程序顶部菜单
@@ -23,7 +22,16 @@ module.exports = (appInfo) => {
    */
   config.jobs = {
     messageLog: true
-  };   
+  };
+
+
+  /**
+   * 远程模式-web地址
+   */
+  config.remoteUrl = {
+    enable: true,
+    url: 'http://localhost:3000/home'
+  };
 
   return {
     ...config

+ 3 - 3
electron/config/config.prod.js

@@ -9,19 +9,19 @@ module.exports = (appInfo) => {
   /**
    * 开发者工具
    */
-  config.openDevTools = false;
+  config.openDevTools = true;
 
   /**
    * 应用程序顶部菜单
    */
-  config.openAppMenu = false;
+  config.openAppMenu = true;
 
   /**
    * jobs
    */
   config.jobs = {
     messageLog: false
-  }; 
+  };
 
   return {
     ...config

+ 69 - 0
electron/controller/camera.js

@@ -0,0 +1,69 @@
+'use strict';
+
+const { Controller } = require('ee-core');
+const { liveShow,liveHide,setParams,capture }  = require('../api/camera')
+
+
+
+
+class CameraController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+  }
+
+  /**
+   * 启动预览
+   */
+  async liveShow() {
+    try {
+      await liveShow();
+      const win = this.app.electron.mainWindow;
+      win.setAlwaysOnTop(true); // 置顶
+      return true;
+    } catch (error) {
+      console.error('eeee启动直播失败:', error);
+      throw error;
+    }
+  }
+
+
+  /**
+   * 结束预览
+   */
+  async liveHide() {
+    try {
+      await liveHide();
+      const win = this.app.electron.mainWindow;
+      win.setAlwaysOnTop(false); // 置顶
+      return true;
+    } catch (error) {
+      throw error;
+    }
+  }
+
+
+  /**
+   * 设置参数
+   */
+  async setParams(params) {
+    try {
+      console.log(params);
+      await setParams(params);
+      return true;
+    } catch (error) {
+      throw error;
+    }
+  }
+
+  async takePictures() {
+    try {
+      await capture();
+      return true;
+    } catch (error) {
+      throw error;
+    }
+  }
+}
+
+CameraController.toString = () => '[class CameraController]';
+module.exports = CameraController;

+ 83 - 0
electron/controller/socket.js

@@ -0,0 +1,83 @@
+'use strict';
+
+const { Controller } = require('ee-core');
+const Log = require('ee-core/log');
+const CoreWindow = require('ee-core/electron/window');
+const WebSocket = require('ws'); // 引入原生 ws 库
+
+class SocketController extends Controller {
+  constructor(ctx) {
+    super(ctx);
+    this.socket = null;
+  }
+
+  /**
+   * Connect to WebSocket server
+   */
+  connect() {
+    this.socket = new WebSocket('ws://10.56.42.176:7074/ws');
+
+    // 监听连接成功事件
+    this.socket.on('open', () => {
+      Log.info('Connected to WebSocket server');
+      this.sendPing(); // 连接成功后发送 ping
+    });
+
+    // 监听消息事件
+    this.socket.on('message', (data) => {
+      Log.info('Received:', data.toString());
+    });
+
+    // 监听连接关闭事件
+    this.socket.on('close', () => {
+      Log.info('Disconnected from WebSocket server');
+    });
+
+    // 监听错误事件
+    this.socket.on('error', (err) => {
+      Log.error('WebSocketError:', err);
+    });
+
+
+    setInterval(()=>{
+
+      const win = CoreWindow.getMainWindow();
+      win.webContents.send('controller.socket.message', '这是一个消息');
+    },2000)
+  }
+
+  /**
+   * 发送 ping 消息
+   */
+  sendPing() {
+    Log.info('sendPing:');
+    const message = JSON.stringify({ data: 'node', type: 'ping' });
+    this.sendMessage(message);
+  }
+
+  /**
+   * 发送消息到服务器
+   * @param {string} message - JSON 字符串
+   */
+  sendMessage(message) {
+    // 检查连接状态
+    if (this.socket.readyState === WebSocket.OPEN) {
+      this.socket.send(message); // 使用 send() 发送
+      Log.info('Sent:', message);
+    } else {
+      Log.error('WebSocket未连接或未就绪');
+    }
+  }
+
+  /**
+   * 断开连接
+   */
+  disconnect() {
+    if (this.socket) {
+      this.socket.close(); // 使用 close() 方法
+    }
+  }
+}
+
+SocketController.toString = () => '[class SocketController]';
+module.exports = SocketController;

+ 6 - 1
frontend/src/apis/user.ts

@@ -22,4 +22,9 @@ export function upload(params: any) {
         'Content-Type': 'multipart/form-data',
       },
     })
-  }
+  }
+// 获取验证码
+export function sendCode(data:UserRequest) {
+    return POST('/api/auth/send_code',data)
+  }
+  

BIN
frontend/src/assets/images/check/icon1.png


BIN
frontend/src/assets/images/check/icon2.png


+ 12 - 0
frontend/src/assets/images/login/login-code.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>safety certificate</title>
+    <g id="切图" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g transform="translate(-562.000000, -57.000000)" fill-rule="nonzero" id="safety-certificate">
+            <g transform="translate(562.000000, 57.000000)">
+                <rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="24" height="24"></rect>
+                <path d="M20.3179688,3.98203125 L12.3539063,1.26796875 C12.2578125,1.23515625 12.1289063,1.21875 12,1.21875 C11.8710938,1.21875 11.7421875,1.23515625 11.6460938,1.26796875 L3.68203125,3.98203125 C3.4875,4.04765625 3.328125,4.27265625 3.328125,4.47890625 L3.328125,15.7851562 C3.328125,15.9914062 3.46171875,16.2632812 3.6234375,16.3921875 L11.7023438,22.6875 C11.784375,22.7507813 11.8898438,22.7835938 11.9976563,22.7835938 C12.1054688,22.7835938 12.2132813,22.7507813 12.2929688,22.6875 L20.371875,16.3921875 C20.5335938,16.265625 20.6672883,15.99375 20.6672883,15.7851562 L20.6672883,4.47890625 C20.671875,4.27265625 20.5125,4.05 20.3179688,3.98203125 Z M18.984375,15.3351562 L12,20.7773438 L5.015625,15.3351562 L5.015625,5.31328125 L12,2.93203125 L18.984375,5.31328125 L18.984375,15.3351562 Z M9.4734375,10.6242187 C9.403125,10.528125 9.290625,10.4695312 9.16875,10.4695312 L7.875,10.4695312 C7.72265625,10.4695312 7.63359375,10.6429687 7.72265625,10.7671875 L10.6851562,14.8453125 C10.8351562,15.0515625 11.1445312,15.0515625 11.2945312,14.8453125 L16.2773438,7.98515625 C16.3664062,7.8609375 16.2773438,7.6875 16.125,7.6875 L14.83125,7.6875 C14.7117187,7.6875 14.596875,7.74609375 14.5265625,7.8421875 L10.9898437,12.7125 L9.4734375,10.6242187 Z" id="形状" fill="#606266"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 12 - 0
frontend/src/assets/images/login/login-lock.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>lock</title>
+    <g id="切图" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g transform="translate(-487.000000, -57.000000)" fill-rule="nonzero" id="lock">
+            <g transform="translate(487.000000, 57.000000)">
+                <rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="24" height="24"></rect>
+                <path d="M19.5,10.875 L17.90625,10.875 L17.90625,5.625 C17.90625,3.96796875 16.5632813,2.625 14.90625,2.625 L9.09375,2.625 C7.43671875,2.625 6.09375,3.96796875 6.09375,5.625 L6.09375,10.875 L4.5,10.875 C4.08515625,10.875 3.75,11.2101563 3.75,11.625 L3.75,20.625 C3.75,21.0398437 4.08515625,21.375 4.5,21.375 L19.5,21.375 C19.9148437,21.375 20.25,21.0398437 20.25,20.625 L20.25,11.625 C20.25,11.2101563 19.9148437,10.875 19.5,10.875 Z M7.78125,5.625 C7.78125,4.90078125 8.36953125,4.3125 9.09375,4.3125 L14.90625,4.3125 C15.6304687,4.3125 16.21875,4.90078125 16.21875,5.625 L16.21875,10.875 L7.78125,10.875 L7.78125,5.625 Z M18.5625,19.6875 L5.4375,19.6875 L5.4375,12.5625 L18.5625,12.5625 L18.5625,19.6875 Z M11.34375,16.4296875 L11.34375,17.671875 C11.34375,17.775 11.428125,17.859375 11.53125,17.859375 L12.46875,17.859375 C12.571875,17.859375 12.65625,17.775 12.65625,17.671875 L12.65625,16.4296875 C12.9398438,16.2257812 13.125,15.8929688 13.125,15.515625 C13.125,14.8945312 12.6210938,14.390625 12,14.390625 C11.3789062,14.390625 10.875,14.8945312 10.875,15.515625 C10.875,15.8929688 11.0601562,16.2257812 11.34375,16.4296875 Z" id="形状" fill="#606266"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 12 - 0
frontend/src/assets/images/login/login-mobile.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>mobile</title>
+    <g id="切图" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g transform="translate(-527.000000, -57.000000)" fill-rule="nonzero" id="mobile">
+            <g transform="translate(527.000000, 57.000000)">
+                <rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="24" height="24"></rect>
+                <path d="M17.4375,1.453125 L6.5625,1.453125 C5.73515625,1.453125 5.0625,2.12578125 5.0625,2.953125 L5.0625,20.953125 C5.0625,21.7804687 5.73515625,22.453125 6.5625,22.453125 L17.4375,22.453125 C18.2648437,22.453125 18.9375,21.7804687 18.9375,20.953125 L18.9375,2.953125 C18.9375,2.12578125 18.2648437,1.453125 17.4375,1.453125 Z M17.25,20.765625 L6.75,20.765625 L6.75,3.140625 L17.25,3.140625 L17.25,20.765625 Z M11.0625,18.375 C11.0625,18.892767 11.482233,19.3125 12,19.3125 C12.517767,19.3125 12.9375,18.892767 12.9375,18.375 C12.9375,17.857233 12.517767,17.4375 12,17.4375 C11.482233,17.4375 11.0625,17.857233 11.0625,18.375 Z" id="形状" fill="#606266"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 12 - 0
frontend/src/assets/images/login/login-user.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>user</title>
+    <g id="切图" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g transform="translate(-450.000000, -57.000000)" fill-rule="nonzero" id="user">
+            <g transform="translate(450.000000, 57.000000)">
+                <rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="24" height="24"></rect>
+                <path d="M20.1210938,17.896875 C19.678125,16.846875 19.040625,15.9046875 18.2320313,15.0960937 C17.4234375,14.2875 16.48125,13.6523438 15.43125,13.2070312 C15.421875,13.2023437 15.4125,13.2 15.403125,13.1953125 C16.8632813,12.140625 17.8125,10.4226562 17.8125,8.484375 C17.8125,5.2734375 15.2109375,2.671875 12,2.671875 C8.7890625,2.671875 6.1875,5.2734375 6.1875,8.484375 C6.1875,10.4226562 7.13671875,12.140625 8.596875,13.1976563 C8.5875,13.2023438 8.578125,13.2046875 8.56875,13.209375 C7.51875,13.6523438 6.5765625,14.2875 5.76796875,15.0984375 C4.959375,15.9070313 4.32421875,16.8492188 3.87890625,17.8992188 C3.44296875,18.9257812 3.2109375,20.015625 3.18745233,21.1359375 C3.18515625,21.2414062 3.26953125,21.328125 3.375,21.328125 L4.78125,21.328125 C4.884375,21.328125 4.96640625,21.2460938 4.96875,21.1453125 C5.015625,19.3359375 5.7421875,17.6414063 7.0265625,16.3570313 C8.35546875,15.028125 10.1203125,14.296875 12,14.296875 C13.8796875,14.296875 15.6445313,15.028125 16.9734375,16.3570313 C18.2578125,17.6414063 18.984375,19.3359375 19.03125,21.1453125 C19.0335938,21.2484375 19.115625,21.328125 19.21875,21.328125 L20.625,21.328125 C20.7304688,21.328125 20.8148438,21.2414062 20.8125477,21.1359375 C20.7890625,20.015625 20.5570313,18.9257812 20.1210938,17.896875 Z M12,12.515625 C10.9242188,12.515625 9.91171875,12.0960938 9.15,11.334375 C8.38828125,10.5726563 7.96875,9.56015625 7.96875,8.484375 C7.96875,7.40859375 8.38828125,6.39609375 9.15,5.634375 C9.91171875,4.87265625 10.9242188,4.453125 12,4.453125 C13.0757813,4.453125 14.0882813,4.87265625 14.85,5.634375 C15.6117188,6.39609375 16.03125,7.40859375 16.03125,8.484375 C16.03125,9.56015625 15.6117188,10.5726563 14.85,11.334375 C14.0882813,12.0960938 13.0757813,12.515625 12,12.515625 Z" id="形状" fill="#606266"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 186 - 0
frontend/src/components/check/index.vue

@@ -0,0 +1,186 @@
+<template>
+  <el-dialog
+    v-model="visible"
+    :title="title"
+    :close-on-click-modal="false"
+    width="500px"
+    class="hardware-check-dialog"
+  >
+    <div class="hardware-check-container">
+      <el-timeline>
+        <!-- Step 1 -->
+        <el-timeline-item
+          :type="'primary'"
+          :hollow="true">
+          <template #dot>
+            <img src="@/assets/images/check/icon1.png" class="custom-timeline-icon" />
+          </template>
+          <div class="step-content">
+            <div class="step-title">
+              <template v-if="checkLoading">正在初始化检测硬件......</template>
+              <template v-else-if="checkSuccess">硬件检测完成</template>
+              <template v-else>初始化检测硬件失败</template>
+            </div>
+            <el-progress 
+              :percentage="progress" 
+              :colors="['#2FB0FF', '#B863FB']"
+              :stroke-width="8"
+              v-if="checkLoading"
+            ></el-progress>
+            <div class="check-error-result" v-else-if="!checkSuccess">失败的原因,比如请检查硬件链接</div>
+          </div>
+        </el-timeline-item>
+        
+        <!-- Step 2 -->
+        <el-timeline-item
+          :type="'primary'"
+          :hollow="true">
+          <template #dot>
+            <img src="@/assets/images/check/icon2.png" class="custom-timeline-icon" />
+          </template>
+          <div class="step-content">
+            <div class="step-title">自我检查</div>
+            <div class="check-result">
+              <p>补光灯电源和链接器是否正常连上。</p>
+              <p>请检查红外线器是否正常显示。</p>
+            </div>
+          </div>
+        </el-timeline-item>
+      </el-timeline>
+    </div>
+    <template #footer v-if="!checkLoading">
+        <div class="flex" v-if="!checkSuccess">
+            <div class="check-btn" @click="reCheck">重新监测</div>
+        </div>
+        <div class="flex" v-else>
+            <div class="check-btn">正常,开始下一步</div>
+        </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, computed, watch, onBeforeUnmount, nextTick } from 'vue';
+
+const props = defineProps({
+  modelValue: {
+    type: Boolean,
+    default: false
+  },
+  title: {
+    type: String,
+    default: '检测硬件'
+  }
+});
+const checkSuccess = ref(false);
+const checkLoading = ref(false)
+const emit = defineEmits(['update:modelValue', 'confirm']);
+
+const progress = ref(80);
+let timer = null;
+
+const visible = computed({
+  get() {
+    return props.modelValue;
+  },
+  set(val) {
+    emit('update:modelValue', val);
+  }
+});
+
+function startProgress() {
+  progress.value = 0;
+  checkLoading.value = true;
+  timer = setInterval(() => {
+    if (progress.value < 80) {
+      progress.value += 1;
+    } else {
+      checkSuccess.value = false;
+      clearInterval(timer);
+    }
+  }, 50);
+}
+
+function reCheck() {
+    startProgress();
+}
+
+
+function handleConfirm() {
+  emit('confirm');
+  visible.value = false;
+}
+
+watch(visible, (val) => {
+  if (val) {
+    nextTick(() => {
+      startProgress();
+    });
+  } else {
+    clearInterval(timer);
+  }
+});
+
+onBeforeUnmount(() => {
+  clearInterval(timer);
+});
+</script>
+
+<style lang="scss" scoped>
+.hardware-check-container {
+  padding: 20px 0;
+}
+
+.step-content {
+  flex: 1;
+}
+
+.step-title {
+font-weight: 600;
+font-size: 14px;
+color: #000000;
+  margin-bottom: 15px;
+  padding-top: 4px;
+  text-align: left;
+}
+
+.progress-text {
+  text-align: right;    
+  margin-top: 5px;
+  color: #606266;
+}
+
+.check-result {
+    color: rgba(0,0,0,0.45);
+  text-align: left;
+  p {
+    margin: 8px 0;
+  }
+}
+
+:deep(.el-progress-bar__inner) {
+  background-image: linear-gradient(to right, #409EFF, #7e57c2);
+}
+.custom-timeline-icon{
+    width: 24px;
+    height: 24px;
+    position: relative;
+    left: -7px;
+}
+.check-btn{
+    width: 100px;
+    height: 40px;
+    background: linear-gradient( 135deg, #2FB0FF 0%, #B863FB 100%);
+    border-radius: 4px;
+    font-weight: 400;
+    font-size: 16px;
+    color: #FFFFFF;
+    text-align: center;
+    line-height: 40px;
+}
+.check-error-result{
+    font-size: 14px;
+    text-align: left;
+    color: #FF4C00;
+}
+</style>

+ 381 - 0
frontend/src/components/login/index.vue

@@ -0,0 +1,381 @@
+<template>
+  <div class="login-box">
+    <el-dialog
+      v-model="showDialog"
+      width="354px"
+      custom-class="login-dialog"
+      :close-on-click-modal="false"
+      :show-close="true"
+      @close="emit('update:dialogVisible',false)"
+    >
+    <div class="login-box">
+      <el-tabs
+        class="login-box__tabs"
+        v-model="activeTab"
+      >
+        <el-tab-pane label="账号密码登录" name="0" />
+        <el-tab-pane label="验证码登录" name="1" />
+      </el-tabs>
+
+      <el-form
+        ref="loginForm"
+        :model="loginForm"  
+        :rules="loginRules"
+        autocomplete="on"
+        label-position="left"
+      >
+        <div class="title__main">欢迎!</div>
+        <div class="title_sub">登录拍照机系统</div>
+
+        <el-form-item class="login-input" prop="username">
+          <div class="login-icon">
+            <img src="@/assets/images/login/login-user.svg" v-if="activeTab === '0'"/>
+            <img src="@/assets/images/login/login-code.svg" v-else/>
+          </div>
+          <el-input
+            v-model="loginForm.username"
+            placeholder="请输入用户名"
+            name="username"
+            type="text"
+            maxlength="30"
+            style="width: 270px;"
+            tabindex="1"
+            autocomplete="on"
+          >
+          </el-input>
+        </el-form-item>
+
+        <template v-if="activeTab === '0'">
+          <el-form-item
+            class="login-input"
+            prop="password"
+          >
+            <div class="login-icon">
+             <img src="@/assets/images/login/login-lock.svg" />
+            </div>
+            <el-input
+              v-model="loginForm.password"
+              placeholder="请输入密码"
+              type="password"
+            style="width: 270px;"
+              show-password
+              maxlength="30"
+              tabindex="2"
+              autocomplete="on"
+            >
+            </el-input>
+          </el-form-item>
+        </template>
+
+        <template v-else>
+          <el-form-item
+            class="login-input"
+            prop="code"
+          >
+            <div class="login-icon">
+              <img src="@/assets/images/login/login-lock.svg" />
+            </div>
+            <el-input
+              v-model="loginForm.password"
+              placeholder="请输入验证码"
+              type="text"
+              maxlength="6"
+              tabindex="2"
+              style="width: 270px;"
+              autocomplete="off"
+            >
+              <template #append>
+                <el-button 
+                  :disabled="isCodeSending" 
+                  @click="sendVerificationCode"
+                >
+                  {{ codeButtonText }}
+                </el-button>
+              </template>
+            </el-input>
+          </el-form-item>
+        </template>
+
+        <el-button class="login-button" type="primary" :loading="loading" @click="handleLogin">
+          登录
+        </el-button>
+      </el-form>
+    </div>
+  </el-dialog>
+  </div>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+
+export default defineComponent({
+  name: 'Login',
+})
+</script>
+<script setup>
+import { ref, reactive, computed } from 'vue'
+import { ElMessage } from 'element-plus'
+import { login, getUserInfo, sendCode } from '@/apis/user';
+import useUserInfo from "@/stores/modules/user";
+const showDialog = computed(() => props.dialogVisible)
+
+const props = defineProps({
+  dialogVisible: {
+    type: Boolean,
+    default: false
+  }
+})
+const emit = defineEmits(['update:dialogVisible', 'login-success'])
+
+const useUserInfoStore = useUserInfo();
+const activeTab = ref('0')
+const loading = ref(false)
+const isCodeSending = ref(false)
+const countdown = ref(60)
+const loginForm = reactive({
+  username: '',
+  password: '',
+  code: ''
+})
+
+const codeButtonText = computed(() => {
+  return isCodeSending.value ? `${countdown.value}s后重新获取` : '获取验证码'
+})
+
+const loginRules = reactive({
+  username: [
+    { required: true, message: '请输入用户名', trigger: 'blur' },
+    { min: 3, max: 30, message: '长度在 3 到 30 个字符', trigger: 'blur' }
+  ],
+  password: [
+    { required: true, message: '请输入密码', trigger: 'blur' },
+    { min: 6, max: 30, message: '长度在 6 到 30 个字符', trigger: 'blur' }
+  ],
+  code: [
+    { required: true, message: '请输入验证码', trigger: 'blur' },
+    { len: 6, message: '验证码长度为6位', trigger: 'blur' }
+  ]
+})
+
+
+const handleLogin = async () => {
+  const res = await login({
+    "site":1,
+    "username":loginForm.username,
+    "password":loginForm.password,
+    "type": activeTab.value,
+    "device":"aigc-photo"
+  })
+
+  const userRes = await getUserInfo({
+    "site":1,
+    "token":res.data.token
+  })
+
+  useUserInfoStore.updateUserInfo(userRes.data)
+}
+
+const sendVerificationCode = () => {
+  if (!loginForm.username) {
+    ElMessage.warning('请先输入手机号')
+    return
+  }
+  
+  sendCode({
+    phone: loginForm.username
+  }).then(() => {
+    isCodeSending.value = true
+    countdown.value = 60
+    
+  // 这里应该调用发送验证码API
+  // await sendCode(loginForm.username)
+  
+  const timer = setInterval(() => {
+    countdown.value--
+    if (countdown.value <= 0) {
+      clearInterval(timer)
+      isCodeSending.value = false
+    }
+  }, 1000)
+  })
+}
+
+</script>
+
+<style lang="scss" scoped>
+.login-box {
+  width: 100%;
+  min-height: 404px;
+  background-color: #fff;
+
+  .title__main {
+    font-size: 20px;
+    font-weight: bold;
+    color: #333;
+    line-height: 1.4;
+    text-align: left;
+  }
+  .title_sub {
+    font-size: 16px;
+    color: #666;
+    text-align: left;
+    line-height: 1.4;
+  }
+
+  .login-input {
+    :deep(.el-form-item__content) {
+      display: flex;
+      align-items: center;
+    }
+    :deep(.el-input__inner) {
+      height: 40px;
+      line-height: 40px;
+      border-top-left-radius: 0;
+      border-bottom-left-radius: 0;
+    }
+    :deep(.el-input-group__prepend) {
+      padding: 0;
+      background-color: initial;
+    }
+    :deep(.el-input__suffix-inner) {
+      line-height: 40px;
+      padding: 0 10px;
+    }
+
+    .login-icon {
+      flex-shrink: 0;
+      width: 40px;
+      height: 40px;
+      border: 1px solid #dcdfe6;
+      border-top-left-radius: 4px;
+      border-bottom-left-radius: 4px;
+      border-right: 0;
+
+      font-size: 22px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      img{
+        width: 22px;
+        height: 22px;
+      }
+    }
+  }
+
+  .login-button {
+    width: 100%;
+    margin-top: 10px;
+    background: linear-gradient( 135deg, #2FB0FF 0%, #B863FB 100%);
+    
+  }
+  :deep(.el-form-item) + .login-button {
+    margin-top: 20px;
+  }
+
+  .login-box__tabs {
+    :deep(.el-tabs__nav) {
+      float: initial;
+      display: flex;
+    }
+    :deep(.el-tabs__item) {
+      flex-grow: 1;
+      text-align: center;
+      height: 50px;
+      line-height: 50px;
+      padding: 0;
+      margin: 0;
+      &.is-active {
+        color: #333;
+      }
+    }
+
+    :deep(.el-tabs__active-bar) {
+      height: 3px;
+    }
+    :deep(.el-tabs__nav-wrap::after) {
+      height: 1px;
+      background-color: #ebeef5;
+    }
+    :deep(.el-tabs__header) {
+      margin-bottom: 0;
+    }
+    :deep(.el-tabs__active-bar) {
+      background-color: initial;
+      &::before {
+        content: '';
+        display: flex;
+        width: 50px;
+        height: 100%;
+        background-color: var(--el-color-primary);
+        margin: 0 auto;
+      }
+    }
+    :deep(.el-tabs__content) {
+      padding: 0;
+    }
+  }
+
+  :deep(.el-form) {
+    padding: 20px 0;
+  }
+
+  :deep(.el-form-item) {
+    margin-bottom: 0;
+    margin-top: 20px;
+  }
+
+  .company-list {
+    height: 220px;
+    overflow-y: auto;
+    .item {
+      background: #fff;
+      border-radius: 3px;
+      overflow: hidden;
+      margin-top: 10px;
+      cursor: pointer;
+      &:hover {
+        opacity: 0.9;
+      }
+      &:active {
+        opacity: 0.8;
+      }
+
+      .abbreviation {
+        background: #4f4f4f;
+        height: 42px;
+        line-height: 42px;
+        padding: 0 10px;
+        color: #fff;
+        font-size: 14px;
+      }
+      &.active {
+        position: relative;
+      }
+      &.active:before {
+        content: '';
+        display: block;
+        width: 44px;
+        height: 42px;
+        background: url('/static/images/active.png');
+        position: absolute;
+        right: 0px;
+      }
+    }
+  }
+}
+.login-box {
+    :deep(.login-dialog) {
+      border-radius: 10px;
+    }
+    :deep(.el-dialog__header) {
+      padding: 0;
+    }
+    :deep(.el-dialog__body) {
+      padding-top: 10px;
+    }
+    :deep(.el-input-group__append){
+      background-color: #fff;
+      color: #2FB0FF;
+    }
+}
+</style>

+ 14 - 0
frontend/src/stores/modules/client.ts

@@ -0,0 +1,14 @@
+
+import { defineStore } from 'pinia';
+const electron = window.require && window.require('electron') || {}
+
+
+const client = defineStore('client', {
+    state: () => ({
+        electron: electron,
+        ipc: electron.ipcRenderer || undefined,
+        isClient: electron.ipcRenderer  ? true : false,
+    }),
+});
+
+export default client;

+ 16 - 0
frontend/src/utils/ipc.ts

@@ -0,0 +1,16 @@
+const icpList = {
+    camera:{
+        PreviewShow: 'controller.camera.liveShow',
+        PreviewHide: 'controller.camera.liveHide',
+        setParams:"controller.camera.setParams",
+        takePictures:"controller.camera.takePictures"
+    },
+    socket:{
+        connect: 'controller.socket.connect',
+        message: 'controller.socket.message',
+        disconnect: 'controller.socket.disconnect',
+    }
+}
+
+
+export default icpList;

+ 172 - 12
frontend/src/views/Home/index.vue

@@ -1,27 +1,73 @@
 <template>
 
-  <div>{{useUserInfoStore.userInfo.account_name}}</div>
-  <div>{{useUserInfoStore.userInfo.phone}}</div>
 
+  <div class="mb-4">
+
+    <template v-if="!show">
+      <div>{{useUserInfoStore.userInfo.account_name}}</div>
+      <div>{{useUserInfoStore.userInfo.phone}}</div>
+    </template>
+    <template v-else>
+      <img :src="src" width="500px" height="500px"/>
+      <img :src="preview" width="500px" height="500px"/>
+  </template>
+
+    <el-button @click="showVideo">实时预览</el-button>
+    <el-button @click="hideVideo">关闭预览</el-button>
+    <el-button @click="takePictures">拍照</el-button>
+    <el-button @click="socketConnect">socket 连接</el-button>
+    <el-button @click="socketDisconnect">socket 断开</el-button>
 
 
-  <div class="mb-4">
     <el-button @click="loginError">登录失败</el-button>
-    <el-button type="primary"  @click="loginIn">登录成功</el-button>
-    <el-button type="success">Success</el-button>
-    <el-button type="info">Info</el-button>
+    <el-button type="primary" @click="loginIn">登录成功</el-button>
+    <el-button type="success" @click="showLoginDialog">登录</el-button>
+    <el-button type="info" @click="checkVisible = true">软件检查</el-button>
     <el-button type="warning">Warning</el-button>
     <el-button type="danger">Danger</el-button>
+    <hardware-check v-model="checkVisible" @confirm="onCheckComplete"></hardware-check>
+    <Login v-model:dialogVisible="dialogVisible" @login-success="handleLoginSuccess"/>
+    <el-button type="primary"  @click="loginIn">登录成功</el-button>
+  </div>
+
+
+  <div class="mb-4">
+      <el-input v-model="paramsKey" placeholder="参数名称"></el-input>
+      <el-input v-model="paramsValue" placeholder="参数值"></el-input>
+      <el-button @click="setParams">保存</el-button>
   </div>
+
 </template>
 
 <script setup lang="ts">
+import { ref } from 'vue'
 import useUserInfo from "@/stores/modules/user";
+import { login, getUserInfo } from '@/apis/user';
+import Login from '@/components/login/index.vue';
+import HardwareCheck from '@/components/check/index.vue'
+
+
 const useUserInfoStore = useUserInfo();
-import { login,getUserInfo } from '@/apis/user'
+const dialogVisible = ref(false);
+const checkVisible = ref(false)
+import client from "@/stores/modules/client";
+import  icpList from '@/utils/ipc'
+const clientStore = client();
+console.log(icpList);
 
+function showLoginDialog() {
+  dialogVisible.value = true;
+}
+
+function handleLoginSuccess() {
+  // 处理登录成功后的逻辑
+  console.log('登录成功');
+}
+function onCheckComplete(){
+
+}
 
-async  function loginIn() {
+async function loginIn() {
   console.log('aaa')
   const res = await login({
     "site":1,
@@ -37,12 +83,9 @@ async  function loginIn() {
   })
 
   useUserInfoStore.updateUserInfo(userRes.data)
-
-
-
 }
+
 async function loginError() {
-  console.log('bbb')
   const res = await login({
     "site":1,
     "username":"18679381902",
@@ -51,6 +94,123 @@ async function loginError() {
   })
   console.log(res)
 }
+
+const show = ref(false)
+const imgKey = ref(0)
+const src = ref('http://localhost:5513/liveview.jpg')
+let interval:any = null
+function showVideo(){
+  if(clientStore.isClient){
+
+    console.log(clientStore.ipc)
+    clientStore.ipc.removeAllListeners(icpList.camera.PreviewShow);
+
+    clientStore.ipc.send(icpList.camera.PreviewShow);
+    clientStore.ipc.on(icpList.camera.PreviewShow, async (event, result) => {
+
+      show.value = true;
+      src.value = `http://localhost:5513/liveview.jpg?key=${imgKey.value}`
+      interval = setInterval(()=>{
+        imgKey.value++;
+        src.value = `http://localhost:5513/liveview.jpg?key=${imgKey.value}`
+      },100)
+
+    })
+  }
+
+}
+
+const preview = ref('http://localhost:5513/preview.jpg')
+const previewKey = ref(0)
+
+function hideVideo(){
+  if(clientStore.isClient){
+
+    clientStore.ipc.removeAllListeners(icpList.camera.PreviewHide);
+
+    clientStore.ipc.send(icpList.camera.PreviewHide);
+    clientStore.ipc.on(icpList.camera.PreviewHide, async (event, result) => {
+      show.value = false;
+      if(interval) clearInterval(interval)
+    })
+  }
+
+}
+
+const paramsKey = ref('')
+const paramsValue = ref('')
+
+
+function setParams(){
+  if(clientStore.isClient){
+
+    clientStore.ipc.removeAllListeners(icpList.camera.setParams);
+
+    clientStore.ipc.send(icpList.camera.setParams,{
+      key:paramsKey.value,
+      value:paramsValue.value
+    });
+  }
+
+}
+
+
+function takePictures(){
+  if(clientStore.isClient){
+
+    clientStore.ipc.removeAllListeners(icpList.camera.takePictures);
+
+    clientStore.ipc.send(icpList.camera.takePictures);
+
+    clientStore.ipc.on(icpList.camera.takePictures, async (event, result) => {
+
+
+      setTimeout(()=>{
+        previewKey.value++;
+        preview.value = `http://localhost:5513/preview.jpg?key=${previewKey.value}`
+      },1000)
+    })
+  }
+}
+
+
+function socketConnect(){
+
+  if(clientStore.isClient){
+
+    clientStore.ipc.removeAllListeners(icpList.socket.connect);
+
+    clientStore.ipc.send(icpList.socket.connect);
+
+    clientStore.ipc.on(icpList.socket.connect, async (event, result) => {
+      console.log(result);
+    })
+
+    clientStore.ipc.on(icpList.socket.message, async (event, result) => {
+      console.log('message');
+      console.log(result);
+    })
+  }
+
+
+
+}
+
+
+
+function socketDisconnect(){
+
+  if(clientStore.isClient){
+
+    clientStore.ipc.removeAllListeners(icpList.socket.disconnect);
+
+    clientStore.ipc.send(icpList.socket.disconnect);
+
+    clientStore.ipc.on(icpList.socket.disconnect, async (event, result) => {
+      console.log(result);
+    })
+  }
+}
 </script>
 
 <style scoped>

+ 13 - 11
package.json

@@ -45,19 +45,21 @@
   "author": "哆啦好梦, Inc <530353222@qq.com>",
   "license": "Apache",
   "devDependencies": {
-    "@electron/rebuild": "^3.2.13",
-    "debug": "^4.3.3",
+    "@electron/rebuild": "3.2.13",
+    "debug": "4.3.3",
     "ee-bin": "1.8.3",
-    "electron": "^21.4.4",
-    "electron-builder": "^23.6.0",
-    "eslint": "^5.13.0",
-    "eslint-plugin-prettier": "^3.0.1",
-    "nodemon": "^2.0.16"
+    "electron": "21.4.4",
+    "electron-builder": "23.6.0",
+    "eslint": "5.13.0",
+    "eslint-plugin-prettier": " -3.0.1",
+    "nodemon": "2.0.16"
   },
   "dependencies": {
-    "dayjs": "^1.10.7",
+    "axios": "^1.7.9",
+    "dayjs": "1.10.7",
     "ee-core": "2.12.0",
-    "electron-updater": "^5.3.0",
-    "lodash": "^4.17.21"
+    "electron-updater": "5.3.0",
+    "lodash": "4.17.21",
+    "ws": "8.18.1"
   }
-}
+}