electron-vue 台称串口对接 SerialPort

大致流程

1.首先找一个串口工具(sscom5.12.1)试试读取串口是否成功连上;

2.创建electron-vue的项目;

3.安装依赖,调整版本,启动项目;(在electron中使用串口_electron 串口_Jack_Kee的博客-CSDN博客)

4.学习SerialPort Usage | Node SerialPort工具;

5.开发入口文件,测试是否成功获取串口信息

6.成功获取串口后,接收台称数据

7.根据台称说明书与协议,解析台称数据(数值,单位,正负,精度...)

8.完成称重需求

9.完成打印需求

10.完成自动更新

11.防止开启多个应用

1.串口工具大概样子

electron-vue 台称串口对接 SerialPort_第1张图片

通过切换端口号和波特率,可以查看哪个是台称串口及波特率,我本机的端口号COM3 波特率9600是台秤的串口信息。

2.创建electron项目(起步 · electron-vue)

跟着官方步骤 创建electron-vue项目,成功启动项目(启动,打包)

 "electron": "^17.4.11",

3.引入serialport
npm install serialport --save

然后,安装electron-rebuild(用来重新编译serialport包):

npm install --save-dev electron-rebuild

重新编译serialport包

.\node_modules\.bin\electron-rebuild.cmd
 
  

提示Rebuild Complete,表示编译完成

electron-vue 台称串口对接 SerialPort_第2张图片

4.按照该文章,实现最终效果注意安装依赖版本,

此注明我项目使用的各个版本

"dependencies": {
		"@serialport/parser-byte-length": "^11.0.0",
		"axios": "^0.18.0",
		"electron-updater": "^5.3.0",
		"element-ui": "^2.8.2",
		"lodash": "^4.17.21",
		"multispinner": "^0.2.1",
		"qrcodejs2": "^0.0.2",
		"serialport": "^11.0.0",
		"vue": "^2.5.16",
		"vue-electron": "^1.0.6",
		"vue-router": "^3.0.1",
		"vuex": "^3.0.1",
		"vuex-electron": "^1.0.0",
		"vuex-persistedstate": "^4.1.0"
	},
	"devDependencies": {
		"ajv": "^6.5.0",
		"babel-core": "^6.26.3",
		"babel-loader": "^7.1.4",
		"babel-minify-webpack-plugin": "^0.3.1",
		"babel-plugin-component": "^1.1.1",
		"babel-plugin-transform-runtime": "^6.23.0",
		"babel-preset-env": "^1.7.0",
		"babel-preset-stage-0": "^6.24.1",
		"babel-register": "^6.26.0",
		"cfonts": "^2.1.2",
		"chalk": "^2.4.1",
		"copy-webpack-plugin": "^4.5.1",
		"cross-env": "^5.1.6",
		"css-loader": "^0.28.11",
		"del": "^3.0.0",
		"devtron": "^1.4.0",
		"electron": "^17.4.11",
		"electron-builder": "^23.6.0",
		"electron-debug": "^1.5.0",
		"electron-devtools-installer": "^2.2.4",
		"electron-rebuild": "^3.2.9",
		"file-loader": "^1.1.11",
		"html-webpack-plugin": "^3.2.0",
		"listr": "^0.14.3",
		"mini-css-extract-plugin": "0.4.0",
		"node-loader": "^0.6.0",
		"node-sass": "^4.9.2",
		"sass-loader": "^7.0.3",
		"style-loader": "^0.21.0",
		"url-loader": "^1.0.1",
		"vue-html-loader": "^1.2.4",
		"vue-loader": "^15.2.4",
		"vue-style-loader": "^4.1.0",
		"vue-template-compiler": "^2.5.16",
		"webpack": "^4.15.1",
		"webpack-cli": "^3.0.8",
		"webpack-dev-server": "^3.1.4",
		"webpack-hot-middleware": "^2.22.2",
		"webpack-merge": "^4.1.3"
	}
 
  
4. SerialPort (注意不同版本使用方式不同)

serialport:10.x.x

import { SerialPort } from 'serialport'

serialport:7.x.x

const SerialPort = require('serialport')

项目操作案例:

const { ByteLengthParser } = require("@serialport/parser-byte-length");
let ports = []; // 串口list
let mainWindow = null;  // electron 窗口

function registerIpcEvent() {
  // 发送可选串口
  SerialPort.list().then((ports) => {
    setTimeout(() => {
      mainWindow.webContents.send("send-port-info", ports);
    }, 1000);
  });
  //  关闭窗口
  app.on("window-all-closed", function () {
    if (process.platform !== "darwin") {
      ports.forEach((e) => {
        e && e.close();
      });
      app.quit();
    }
  });
  ipcMain.on("closeApp", () => {
    ports.forEach((e) => {
      e && e.close();
    });
    app.quit();
  });

  // 重新初始化串口
  ipcMain.on("init-port", (event, args) => {
    initPort(args);
  });
  // 关闭串口
  ipcMain.on("close-serialport", (event, args) => {
    const currentPort = ports.find((i) => i.path === args.name);
    currentPort && currentPort.close();
    if (currentPort && !currentPort.isOpen) {
      event.reply("close-serialport", { name: args.name });
    }
    ports = ports.filter((e) => e !== currentPort);
  });

  // 打开某个串口
  ipcMain.on("open-serialport", (event, args) => {
    const serialport = new SerialPort(
      {
        path: args.name,
        baudRate: +args.baudRate,
      },
      (err) => {
        console.log(err);
        if (err) {
           event.reply("open-serialport", {
             hasError: true,
             ...args,
             message: err,
           });
        } else {
          event.reply("open-serialport", args);
          ports.push(serialport);
        }
      }
    );
    // 根据说明书数据量字节17一次传输 也可以通过其他方式截取
    const parser = serialport.pipe(new ByteLengthParser({ length: 17  }));
    parser.on("data", (data) => {
     // xxxxxxx 
    });
    serialport.on("open", () => {});
  });
}

function initPort(bool) {
  if (bool) {
    SerialPort.list().then((ports) => {
      setTimeout(() => {
        mainWindow.webContents.send("send-port-info", ports);
      }, 1000);
    });
  }
}

还可以通过下面的形式截断串口数据 

正则匹配:parser-regex  

 一种转换流,它使用正则表达式来拆分传入的文本。

要使用Regex解析器,请提供一个正则表达式来拆分传入的文本。数据作为可由编码选项控制的字符串发出(默认为utf8)

const { SerialPort } = require('serialport')
const { ReadlineParser } = require('@serialport/parser-readline')
const port = new SerialPort({ path: '/dev/ROBOT', baudRate: 14400 })

const parser = port.pipe(new ReadlineParser({ delimiter: '\r\n' }))
parser.on('data', console.log)

分隔符截取:parser-readline 

在接收到换行符后发出数据的转换流。若要使用Readline分析器,请提供分隔符(默认为\n)。数据以可由编码选项控制的字符串形式发出(默认为utf8)。

const { SerialPort } = require('serialport')
const { RegexParser } = require('@serialport/parser-regex')
const port = new SerialPort({ path: '/dev/ROBOT', baudRate: 14400 })

const parser = port.pipe(new RegexParser({ regex: /[\r\n]+/ }))
parser.on('data', console.log)
5.开发入口文件
const { app, BrowserWindow, ipcMain, Menu, dialog } = require("electron");
const { SerialPort } = require("serialport");
const _ = require("lodash");
const { ByteLengthParser } = require("@serialport/parser-byte-length");
import { updateHandle } from "../renderer/utils/update.js";

let ports = [];
const path = require("path");
const fs = require("fs");


//读取本地更新配置json文件 包含是否调试 请求地址 更新地址...
const File_path = path
  .join(path.resolve("./../"), "/config.json")
  .replace(/\\/g, "/");
const config_res = JSON.parse(fs.readFileSync(File_path, "utf-8"));
let mainWindow = null;
const winURL =
  process.env.NODE_ENV === "development"
    ? `http://localhost:9080`
    : `file://${__dirname}/index.html`;


app.whenReady().then(() => {
  createWindow();

  registerIpcEvent();
  // getPrinterList(); 注释打印
  config_res.IS_DEBUG && createMenu();
  app.on("activate", function () {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    frame: config_res.IS_DEBUG, // 是否有边框窗口
    fullscreen: !config_res.IS_DEBUG, // 全屏
    webPreferences: {
      // preload: path.join(__dirname, "preload.js"),
      nodeIntegration: true,
      contextIsolation: false,
      enableRemoteModule: true,
      webviewTag: true,
      webSecurity: false,
    },
  });
  config_res.IS_DEBUG ? mainWindow.webContents.openDevTools() : ""; // 生产模式调试开关

  setTimeout(() => {
    mainWindow.loadURL(winURL);
  }, 1000);
  // 监听崩溃
  mainWindow.webContents.on("crashed", () => {
    const options = {
      type: "error",
      title: "系统意外终止",
      message: "可点击以下按钮",
      buttons: ["点击重启", "直接退出"],
    };
    dialog.showMessageBox(options, (index) => {
      if (index === 0) reloadWindow(mainWindow);
      else app.quit();
    });
  });

}

function createMenu() {
  // main
  const template = [
    {
      label: "调试",
      click: function (item, focusedWindow) {
        if (focusedWindow) {
          config_res.IS_DEBUG && focusedWindow.toggleDevTools();
        }
      },
    },

  ];
  const menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
}
function initPort(bool) {
  if (bool) {
    SerialPort.list().then((ports) => {
      setTimeout(() => {
        mainWindow.webContents.send("send-port-info", ports);
      }, 1000);
    });
  }
}
 
function registerIpcEvent() {
  // 发送可选串口
  SerialPort.list().then((ports) => {
    setTimeout(() => {
      mainWindow.webContents.send("send-port-info", ports);
    }, 1000);
  });
  //  关闭窗口
  app.on("window-all-closed", function () {
    if (process.platform !== "darwin") {
      ports.forEach((e) => {
        e && e.close();
      });
      app.quit();
    }
  });
  ipcMain.on("closeApp", () => {
    ports.forEach((e) => {
      e && e.close();
    });
    app.quit();
  });

  // 重新初始化串口
  ipcMain.on("init-port", (event, args) => {
    initPort(args);
  });
  // 关闭串口
  ipcMain.on("close-serialport", (event, args) => {
    const currentPort = ports.find((i) => i.path === args.name);
    currentPort && currentPort.close();
    if (currentPort && !currentPort.isOpen) {
      event.reply("close-serialport", { name: args.name });
    }
    ports = ports.filter((e) => e !== currentPort);
  });

  // 打开某个串口
  ipcMain.on("open-serialport", (event, args) => {
    const serialport = new SerialPort(
      {
        path: args.name,
        baudRate: +args.baudRate,
      },
      (err) => {
        console.log(err);
        if (err) {
           event.reply("open-serialport", {
             hasError: true,
             ...args,
             message: err,
           });
        } else {
          event.reply("open-serialport", args);
          ports.push(serialport);
        }
      }
    );
    // 数据量字节截取拉大 获取大区间集合
    const parser = serialport.pipe(new ByteLengthParser({ length: 17 * 10 }));
    parser.on("data", (data) => {
      let dataString = data.toString("hex");
 
      sendData(event, {
       // xxxxx 需要发送给页面的信息
      });
    });
    serialport.on("open", () => {});
  });
}
function sendData(event, value) {
  event.sender.send("send-data", value);
}
 
 
6.需要展示串口信息的页面

1. 发送init-port 初始化请求

const { ipcRenderer } = require("electron");

ipcRenderer.send("init-port", true);

 2.页面接收解析后的串口信息函数

  data() {
    return {
      portNameList: [], // 多称选择
      openedPort: [], // 已经打开的称信息
      currPortData: 0.0, // 显示重量
      currName: "", // 称名称
      currRate: "9600", // 波特率
      sign: "", //秤 正 负
      rateOption: rateOption,// 波特率选择
    };
  },  
  watch: {
    currName(val, oldval) {
      if (val !== "" && oldval !== "") {
        this.openOrclose("open", oldval);
      }
    },
  },  
 methods: {
  listSerialPorts() {
      ipcRenderer.on("open-serialport", (event, args) => {
        console.log("open-serialport", args);
        if (args.hasError) {
          alert(args.message);
        } else {
          this.openedPort.push(args.name);
          console.log("open", this.openedPort);
        }
      });

      ipcRenderer.on("close-serialport", (event, args) => {
        const index = this.openedPort.findIndex((i) => i === args.name);
        this.openedPort.splice(index, 1);
        console.log("close", this.openedPort);
      });

      ipcRenderer.on("send-port-info", (event, args) => {
        for (let port of args) {
          this.portNameList.push({
            portName: `称台${this.portNameList.length + 1}`,
            name: port.path,
            path: port.path,
          });
        }

        this.currName = this.portNameList && this.portNameList[0].name;
        console.log("send-port-info获取的串口", this.portNameList);
        this.openOrclose("open");
      });

      ipcRenderer.on("send-data", (event, args) => {
        console.log("send-data", args, this.resData);
        this.currPortData = args.value; // 接收传入的信息 用于展示
       });
    },

    openOrclose(type, oldname = "") {
      if (this.portNameList.length <= 0) {
        this.$message.error("台称读取失败,请重新进入页面或重新进入系统!");
        return;
      }
      const name = this.currName;
      const baudRate = this.currRate;
      if (oldname !== "" && this.openedPort.includes(oldname)) {
        this.sendMessageToMain("close-serialport", { name: oldname });
      }
      if (type === "close") {
        if (this.openedPort.includes(name)) {
          this.sendMessageToMain("close-serialport", { name });
        }
      } else {
        this.sendMessageToMain("open-serialport", {
          name: this.currName,
          baudRate: this.currRate,
        });
      }
    },
}
7.根据台称说明书与协议,解析台称数据(数值,单位,正负,精度...)

先看说明书梅特勒托利多METTLER TOLEDO

  

electron-vue 台称串口对接 SerialPort_第3张图片

 1.从说明书的是数据含有17/18字节 我通过走数据查到是17个字节

 const bufferArray = dataString.split("0d")[1]; 
// 02 34 30 20 20 20 20 32 30 34 20 20 20 30 30 30 0d

 2.连续输出格式 5-10字节代表的称指示重量,11-16字节表示皮重重量

 

3.数据是没有小数点的要通过解析算出小数点的位置和正负号

4.状态A,状态B,状态C 需要解析分别表示一些内容

 状态A说明,main.js里面具体操作

electron-vue 台称串口对接 SerialPort_第4张图片

      let dataString = data.toString("hex");
      const bufferArray = dataString.split("0d")[1]; 
      // 02 34 30 20 20 20 20 32 30 34 20 20 20 30 30 30 0d
      const bufferfloat = bufferArray.slice(2, 4); 
      // 小数点位置 截取byte第二位 根据说明书转为二进制查看0,1,2位的值判断小数点位置
      let float = parseInt(bufferfloat, 16).toString(2).substr(-3); 
      //16进制转为2进制 截取后三位;

      let de = 1;

      if (float === "100") {
        de = 100;
      } else if (float === "101") {
        de = 1000;
      } else if (float === "110") {
        de = 10000;
      } else if (float === "111") {
        de = 100000;
      }
      
      var value = Number(weight) / de; 

  状态B说明,main.js里面具体操作

 electron-vue 台称串口对接 SerialPort_第5张图片

      let dataString = data.toString("hex");
      const bufferArray = dataString.split("0d")[1]; 
        // 02 34 30 20 20 20 20 32 30 34 20 20 20 30 30 30 0d
      const buffersign = bufferArray.slice(4, 6); 
        // 单位值位置 截取byte第2位 共2位
      let signCode = parseInt(buffersign, 16).toString(2).substr(-2, 1); 
        //16进制转为2进制 截取倒数第2位; 从倒数第2位开始截取1个长度的字符
        // signCode 0=正1=负

   状态C说明,main.js里面具体操作

 electron-vue 台称串口对接 SerialPort_第6张图片


      let dataString = data.toString("hex");
      const bufferArray = dataString.split("0d")[1]; 
        // 02 34 30 20 /20 20 20 32 30 34/ 20 20 20 30 30 30 0d
      const bufferunit = bufferArray.slice(6, 8); // 单位值位置 截取byte第3位 共2位
      let unitCode = parseInt(bufferunit, 16).toString(2).substr(-3);
      let unit = null;
      
if (unitCode === "000") {
        unit = "kg";
      } else {
        unit = "g";
      }

8.称重需求 main.js 文件处理
  // 打开某个串口
  ipcMain.on("open-serialport", (event, args) => {
    const serialport = new SerialPort(
      {
        path: args.name,
        baudRate: +args.baudRate,
      },
      (err) => {
        console.log(err);
        if (err) {
           event.reply("open-serialport", {
             hasError: true,
             ...args,
             message: err,
           });
        } else {
          event.reply("open-serialport", args);
          ports.push(serialport);
        }
      }
    );
    // 数据量字节17为一组 但发送太过频繁 所以增大10倍 减少页面渲染压力
    const parser = serialport.pipe(new ByteLengthParser({ length: 17 * 10 }));
    parser.on("data", (data) => {
      let dataString = data.toString("hex");
      const bufferArray = dataString.split("0d")[1]; 
        // 02 34 30 20 /20 20 20 32 30 34/ 20 20 20 30 30 30 0d
      const bufferfloat = bufferArray.slice(2, 4); 
        // 小数点位置 截取byte第二位 根据说明书转为二进制查看0,1,2位的值判断小数点位置
      const buffersign = bufferArray.slice(4, 6); 
        // 正负号位置 截取byte第2位 共2位
      const bufferunit = bufferArray.slice(6, 8); 
        // 单位值位置 截取byte第3位 共2位
      const bufferweight = bufferArray.slice(8, 20);
         // 重量值位置 截取byte第5-10位 共6位
      const bufferpeel = bufferArray.slice(20, 32); 
        // 皮重值位置 截取byte第11-16位 共6位

      let float = parseInt(bufferfloat, 16).toString(2).substr(-3); 
        //16进制转为2进制 截取后三位;
      let signCode = parseInt(buffersign, 16).toString(2).substr(-2, 1); 
        //16进制转为2进制 截取倒数第2位; 从倒数第2位开始截取1个长度的字符。
      let unitCode = parseInt(bufferunit, 16).toString(2).substr(-3);

      let weight = hexToString(bufferweight.toString("hex"));
      let peel = hexToString(bufferpeel.toString("hex"));

      let de = 1;
      let unit = null;
      let sign =  signCode;  // sign 0=正1=负
      if (float === "100") {
        de = 100;
      } else if (float === "101") {
        de = 1000;
      } else if (float === "110") {
        de = 10000;
      } else if (float === "111") {
        de = 100000;
      }
      
      if (unitCode === "000") {
        unit = "kg";
      } else {
        unit = "g";
      }
      var value = Number(weight) / de;
      console.log("weights",value,float,weight,unit,peel,bufferArray,dataString,sign);
      sendData(event, {
        value,
        float,
        weight,
        unit,
        de,
        peel,
        bufferArray,
        dataString,
        sign
      });
    });
    serialport.on("open", () => {});
  });

function sendData(event, value) {
  event.sender.send("send-data", value);
}

/**
 * 将16进制字符串进行分组,每两个一组
 * @param  {[String]} str [16进制字符串]
 * @return {[Array]}     [16进制数组]
 */
//处理中文乱码问题
function utf8to16(str) {
  var out, i, len, c;
  var char2, char3;
  out = "";
  len = str.length;
  i = 0;
  while (i < len) {
    c = str.charCodeAt(i++);
    switch (c >> 4) {
      case 0:
      case 1:
      case 2:
      case 3:
      case 4:
      case 5:
      case 6:
      case 7:
        out += str.charAt(i - 1);
        break;
      case 12:
      case 13:
        char2 = str.charCodeAt(i++);
        out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f));
        break;
      case 14:
        char2 = str.charCodeAt(i++);
        char3 = str.charCodeAt(i++);
        out += String.fromCharCode(
          ((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0)
        );
        break;
    }
  }
  return out;
}

function hexToString(str) {
  // 30 空格 0d跨行
  var val = "",
    len = str.length / 2;
  for (var i = 0; i < len; i++) {
    val += String.fromCharCode(parseInt(str.substr(i * 2, 2), 16));
  }
  return utf8to16(val);
}

detail.vue 页面组装秤重量

console.log("weights",value,float,weight,unit,peel,bufferArray,dataString,sign);
      // 接收到数据 处理数据展示样子 带上小数点,正负号,显示称设置的皮重
      ipcRenderer.on("send-data", (event, args) => {
        if (
          this.resData &&
          ["kg", "千克", "公斤"].includes(this.resData.unit)
        ) {
          if (args.unit === "kg") {
            this.currPortData = args.value;
          } else {
            this.currPortData = args.value / 1000;
          }
        } else if (
          ["g", "克", "斤", "ml", "毫升"].includes(this.resData.unit)
        ) {
          if (args.unit === "g") {
            this.currPortData = args.value;
          } else {
            this.currPortData = args.value * 1000;
          }
        }
        if (args.sign !== "") {
          this.sign = args.sign === "0" ? "" : "-";
        }
      });
9.完成打印需求

1.主进程中初始化的时候请求打印机列表

2.发送事件到渲染进程,带上获取到的打印机列表参数

3.编写页面: webview 标签嵌入静态html文件path 获取打印机列表函数  触发打印的函数 监听渲染完成事件

4.外部触发打印组件的打印函数 传入需要打印的数据

5.打印组件 执行打印函数 获取列表 判断打印机状态 执行打印 获取打印参数 发起webview.send 传输数据

6.静态文件通过ipcRenderer.on("webview-print-render", xxx )接收打印组件发起的数据传输

7.静态文件通过传入的参数 渲染出正确的html页面 通过webview-print-do 发出渲染完成的消息

8.打印组件通过监听webview.addEventListener("ipc-message" ,xxx)执行

 webview.print  打印成功 

 查询打印机列表方法

1.主进程中初始化的时候请求打印机列表

2.发送事件到渲染进程,带上获取到的打印机列表参数

function getPrinterList() {
  // 在主线程下,通过ipcMain对象监听渲染线程传过来的getPrinterList事件
  ipcMain.on("getPrinterList", async (event) => {
    //在主线程中获取打印机列表
    const list = await mainWindow.webContents.getPrintersAsync();
    console.log(list, "getPrinterList");

    //通过webContents发送事件到渲染线程,同时将打印机列表也传过去
    mainWindow.webContents.send("getPrinterList", list);
  });
}

 app初始化的时候获取打印机列表


app.whenReady().then(() => {
  
  getPrinterList();

});

  打印机监控页面print.vue组件 等待发起打印需求

3.编写页面: webview 标签嵌入静态html文件path 获取打印机列表函数  触发打印的函数 监听渲染完成事件




 发起打印请求页面,传入参数

4.外部触发打印组件的打印函数 传入需要打印的数据

    



    goPrint() {
         this.BQHtmlData = {
               //xxxx
          };
           // 调用print组件的打印函数
          this.$refs.pos_bq_print.print(this.BQHtmlData); // 打印杯贴       
      } 
     

5.打印组件 执行打印函数 获取列表 判断打印机状态 执行打印 获取打印参数 发起webview.send 传输数据

6.静态文件通过ipcRenderer.on("webview-print-render", xxx )接收打印组件发起的数据传输

7.静态文件通过传入的参数 渲染出正确的html页面 通过webview-print-do 发出渲染完成的消息

静态html文件






  
  
  
  
  
  标签
  



  
产品: {{product.prodName }}
xxxx
xxxx
xxxx
{{ product.date }}

8.打印组件通过监听webview.addEventListener("ipc-message" ,xxx)执行

 webview.print  打印成功 

10.完成自动更新

 自动更新监控文件


import { autoUpdater } from "electron-updater";

import { ipcMain } from "electron";

import { app } from "electron";

let mainWindow = null;

export function updateHandle(window, feedUrl) {
  mainWindow = window;
  let message = {
    error: "检查更新出错",
    checking: "正在检查更新……",
    updateAva: "检测到新版本,正在下载……",
    updateNotAva: "现在使用的就是最新版本,不用更新",
  };
  //设置更新包的地址
  autoUpdater.setFeedURL(feedUrl);
  //监听升级失败事件
  autoUpdater.on("error", function (error) {
    sendUpdateMessage({
      cmd: "error",
      message: error,
    });
  });
  //监听开始检测更新事件
  autoUpdater.on("checking-for-update", function (message) {
    sendUpdateMessage({
      cmd: "checking-for-update",
      message: message,
    });
  });

  //监听发现可用更新事件
  autoUpdater.on("update-available", function (message) {
    sendUpdateMessage({
      cmd: "update-available",
      message: message,
    });
  });

  //监听没有可用更新事件
  autoUpdater.on("update-not-available", function (message) {
    sendUpdateMessage({
      cmd: "update-not-available",
      message: message,
    });
  });

  // 更新下载进度事件
  autoUpdater.on("download-progress", function (progressObj) {
    sendUpdateMessage({
      cmd: "download-progress",
      message: progressObj,
    });
  });
  //监听下载完成事件
  autoUpdater.on(
    "update-downloaded",
    function (event, releaseNotes, releaseName, releaseDate, updateUrl) {
      sendUpdateMessage({
        cmd: "update-downloaded",
        message: {
          releaseNotes,
          releaseName,
          releaseDate,
          updateUrl,
        },
      });
      //退出并安装更新包
      autoUpdater.quitAndInstall();
    }
  );

  // 接收渲染进程消息,开始检查更新
  ipcMain.on("checkForUpdate", (e, arg) => {
    //执行自动更新检查
    // sendUpdateMessage({cmd:'checkForUpdate',message:arg})
    autoUpdater.checkForUpdates();
  });

}

//给渲染进程发送消息
function sendUpdateMessage(text) {
  mainWindow.webContents.send("message", text);
}

mian.js进程 引入文件

import { updateHandle } from "../renderer/utils/update.js";

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    frame: config_res.IS_DEBUG, // 是否有边框窗口
    fullscreen: !config_res.IS_DEBUG, // 全屏
    webPreferences: {
      // preload: path.join(__dirname, "preload.js"),
      nodeIntegration: true,
      contextIsolation: false,
      enableRemoteModule: true,
      webviewTag: true,
      webSecurity: false,
    },
  });
 
  /**
   * 检测版本更新
   * 设置版本更新地址,即将打包后的latest.yml文件和exe文件同时放在
   * http://xxxx/test/version/对应的服务器目录下,该地址和package.json的publish中的url保持一致
   */
  let feedUrl = config_res.UPDATE_URL; // 更新地址
  updateHandle(mainWindow, feedUrl);
  // 监听崩溃
}

 以下两个文件为主要资源包 均需要放在在线更新地址里,检测到更新后才会拉去更新

yml文件是主要更新监控是否有更新 一定要替换该文件在在线更新地址里,不然检测不到更新

electron-vue 台称串口对接 SerialPort_第7张图片

11.防止开启多个应用

已经开启后 再次双击打开会打开当前的窗口至于首层

// 防止开启多个应用
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
  app.quit();
} else {
  app.on("second-instance", (event, commandLine, workingDirectory) => {
    //输入从第二个实例中接收到的数据
    //有人试图运行第二个实例,我们应该关注我们的窗口
    if (mainWindow && !mainWindow.isDestroyed()) {
      if (mainWindow.isMinimized()) {
        mainWindow.restore();
      }
      mainWindow.focus();
    }
  });
  //创建myWindow,加载应用的其余部分,etc...

你可能感兴趣的:(electron,SerialPort,vue,electron,javascript,前端)