vue结合vue-electron创建应用程序

news2024/11/15 10:34:06

这里写自定义目录标题

    • 安装electron
      • 第一种方式:vue init electron-vue
      • 第二种方式:vue add electron-builder
    • 启动electron
    • 调试功能:background操作和使用
      • 1、覆盖窗口的菜单上下文、右键菜单
      • 2、监听关闭事件、阻止默认行为
      • 3、创建悬浮窗口
      • 4、窗口置顶
      • 5、绑定全局快捷键
    • 完整代码background.js
    • 打包问题
      • 1、electron-v12.2.3-win32-x64.zip
      • 2、winCodeSign-2.6.0.7z.zip
      • 3、nsis 和 nsis-resources

官方文档:
https://www.electronjs.org/zh/docs/latest/api/menu

安装electron

安装electron有两种方式,两种方式创建的项目结构大不相同

以下内容主要讲解第二种方式,在现有的vue项目中使用electron

// 方式一:基于electron-vue 创建新项目
vue init simulatedgreg/electron-vue 项目名 

// 方式二:在现有的vue项目中安装electron包
vue add electron-builder

第一种方式:vue init electron-vue

vue init simulatedgreg/electron-vue 项目名 

在这里插入图片描述
整体结构如下:
在这里插入图片描述
package.json大致的结构如下:

{
  "name": "name",
  "version": "0.0.1",
  "author": "jmyinjg <jmyinjg@163.com>",
  "description": "An electron-vue project",
  "license": null,
  "main": "./dist/electron/main.js",
  "scripts": {
    "build": "node .electron-vue/build.js && electron-builder",
    "build:dir": "node .electron-vue/build.js && electron-builder --dir",
    "build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js",
    "build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js",
    "dev": "node .electron-vue/dev-runner.js",
    "pack": "npm run pack:main && npm run pack:renderer",
    "pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
    "pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
    "postinstall": ""
  },
  "build": {
    "productName": "midtools",
    "appId": "com.jthe.midtools",
    "directories": {
      "output": "build"
    },
    "files": [
      "dist/electron/**/*"
    ],
    "dmg": {
      "contents": [
        {
          "x": 410,
          "y": 150,
          "type": "link",
          "path": "/Applications"
        },
        {
          "x": 130,
          "y": 150,
          "type": "file"
        }
      ]
    },
    "mac": {
      "icon": "build/icons/icon.icns"
    },
    "win": {
      "icon": "build/icons/icon.ico"
    },
    "linux": {
      "icon": "build/icons"
    }
  },
  "dependencies": {
    "@jmyinjg/topsdk": "^1.6.0",
    "ali-oss": "^6.9.0",
    "axios": "^0.18.0",
    "cheerio": "^1.0.0-rc.3",
    "iconv-lite": "^0.5.1",
    "mysql": "^2.18.1",
    "request": "^2.88.2",
    "superagent": "^5.2.2",
    "uuid": "^8.1.0",
    "view-design": "^4.2.0",
    "vue": "^2.5.16",
    "vue-electron": "^1.0.6",
    "vue-router": "^3.0.1",
    "vuex": "^3.0.1",
    "vuex-electron": "^1.0.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-import": "^1.13.0",
    "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": "^2.0.4",
    "electron-builder": "^20.19.2",
    "electron-debug": "^1.5.0",
    "electron-devtools-installer": "^2.2.4",
    "file-loader": "^1.1.11",
    "html-webpack-plugin": "^3.2.0",
    "mini-css-extract-plugin": "0.4.0",
    "multispinner": "^0.2.1",
    "node-loader": "^0.6.0",
    "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"
  }
}

第二种方式:vue add electron-builder

vue add electron-builder

整体结构如下:
在这里插入图片描述
package.json大致的结构如下:

{
  "name": "name",
  "productName": "应用名称",
  "version": "1.5.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "electron:build": "vue-cli-service electron:build",
    "electron:serve": "vue-cli-service electron:serve",
    "postinstall": "electron-builder install-app-deps",
    "postuninstall": "electron-builder install-app-deps"
  },
  "main": "background.js",
  "dependencies": {
    "axios": "~0.21.1",
    "core-js": "~3.15.2",
    "vue": "~3.1.5",
    "vue-axios": "~3.2.4",
    "vue-router": "~4.0.10",
    "vuex": "~4.0.2",
    ......
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.13",
    "@vue/cli-plugin-eslint": "~4.5.13",
    "@vue/cli-plugin-router": "~4.5.13",
    "@vue/cli-plugin-vuex": "~4.5.13",
    "@vue/cli-service": "~4.5.13",
    "@vue/compiler-sfc": "~3.1.5",
    "babel-eslint": "~10.1.0",
    "compression-webpack-plugin": "~6.1.1",
    "electron": "^12.0.0",
    "electron-devtools-installer": "^3.1.0",
    "eslint": "~6.8.0",
    "eslint-plugin-vue": "~7.13.0",
    "postcss": "~8.3.5",
    "vue-cli-plugin-electron-builder": "~2.1.1"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}




启动electron

安装完成后,直接启动一下,看看效果

npm run electron:serve

Reload Ctrl+R 刷新
Toggle Developer Tools Ctrl+Shift+I 打开开发者工具
在这里插入图片描述




调试功能:background操作和使用

'use strict'

import { app, protocol, BrowserWindow } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
const isDevelopment = process.env.NODE_ENV !== 'production'

protocol.registerSchemesAsPrivileged([
  { scheme: 'app', privileges: { secure: true, standard: true } }
])

// 创建主窗口
async function createWindow() {
  const win = new BrowserWindow({
    width: 800, // 窗口的默认宽度
    height: 600, // 窗口的默认高度
    title: '当前窗口的标题',
    webPreferences: {
      nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
      contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION
    }
  })

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
    // 开发环境下,打开调试工具
    if (!process.env.IS_TEST) win.webContents.openDevTools()
  } else {
    createProtocol('app')
    // 主窗口创建完成后进入到
    win.loadURL('app://./index.html')
  }
}
// 监听整个应用的关闭操作
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
  	// 关闭应用
    app.quit()
  }
})

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) createWindow()
})

app.on('ready', async () => {
  if (isDevelopment && !process.env.IS_TEST) {
    try {
      await installExtension(VUEJS_DEVTOOLS)
    } catch (e) {
      console.error('Vue Devtools failed to install:', e.toString())
    }
  }
  createWindow()
})

if (isDevelopment) {
  if (process.platform === 'win32') {
    process.on('message', (data) => {
      if (data === 'graceful-exit') {
        app.quit()
      }
    })
  } else {
    process.on('SIGTERM', () => {
      app.quit()
    })
  }
}

1、覆盖窗口的菜单上下文、右键菜单

在这里插入图片描述

import {BrowserWindow, Menu } from 'electron'
let mainWin

// 主窗口的顶栏菜单
const template = [
  {
    label: '后退',
    click: () => {
    // 判断 浏览器是否可以后退到前一页面。
      if (mainWin.webContents.canGoBack()) {
      // 使浏览器回退到上一个页面。
        mainWin.webContents.goBack();
      }
    }
  },
  {
    label: '前进',
    click: () => {
   	 // 判断 浏览器是否可以前进到下一页面。
      if (mainWin.webContents.canGoForward()) {
      // 使浏览器前进到下一个页面。
        mainWin.webContents.goForward();
      }
    }
  },
  {
    label: '刷新',
    click: () => {
      mainWin.webContents.reloadIgnoringCache();
    }
  },
];


// 右键菜单
const contextMenu = Menu.buildFromTemplate([
  { label: '复制', role: 'copy' },
  { label: '粘贴', role: 'paste' }
]);

async function createWindow() {
	mainWin = new BrowserWindow({
		...
	})
	
   // 部署顶栏菜单
  const menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);

  // 部署右键菜单
  mainWin.webContents.on('context-menu', () => {
    contextMenu.popup({ window: mainWin }); // 在指定窗口上显示上下文菜单
  });
}

2、监听关闭事件、阻止默认行为

dialog.showMessageBoxSync会返回buttons的索引值,
例如点击了“否”,则choice=0;点击了“是”,则choice=1
注意:如果点击了右上角的叉,则choice=0,所以,“是”只能写在“否”后面
不理解为什么要这么设计

在这里插入图片描述

import { BrowserWindow, dialog } from 'electron'
let mainWin

async function createWindow() {
	mainWin = new BrowserWindow({
		...
	})
	
  // 监听 主窗口 右上角的关闭事件
  mainWin.on('close', (e) => {
    e.preventDefault(); // 阻止窗口默认的关闭行为

    /**
	* 弹出 对话框询问用户是否真的想要退出
	* 这里的choice 值是buttons的索引,
	* 例如点击了“否”,则choice=0;点击了“是”,则choice=1
	* 注意:如果点击了右上角的叉,则choice=0,所以,“是”只能写在“否”后面
	*/ 
    const choice = dialog.showMessageBoxSync(null, {
      type: 'info',
      buttons: ['否', '是'],
      title: '退出',
      message: '确定要退出吗?再次开启需重新登录',
    });
    // 如果用户选择“是”,则关闭窗口
    if (choice === 1) {
      // 关闭窗口并释放内存
      mainWin.destroy(); // 关闭主窗口
      app.quit()
    }
  })
}

3、创建悬浮窗口

创建第二个窗口——悬浮球,透明背景,点击悬浮球,促使主窗口进入到某个页面

包含的知识点:
1、多个窗口,悬浮球窗口
2、其他窗口促使主窗口路由跳转
3、区分当前环境是electron,还是浏览器
4、窗口的显示和隐藏
在这里插入图片描述

background.js

import { BrowserWindow, ipcMain, screen } from 'electron'

let mainWinRoutePath = '/'; // 主窗口的当前路由
let mainWin, ballWin

async function createSBallWindow() {
	mainWin = new BrowserWindow({
		...
	})
	...
}

// 创建 工具球窗口
async function createSBallWindow() {
  ballWin = new BrowserWindow({
    width: 65, // 悬浮窗口的宽度 比实际DIV的宽度要多5px,用于处理阴影
    height: 65, // 悬浮窗口的高度 比实际DIV的高度要多5px,用于处理阴影
    type: 'toolbar',  //创建的窗口类型为工具栏窗口
    frame: false,  // 是否创建有边框的窗口(含最大化/最小化/关闭功能),false创建无边框窗口
    resizable: false, // 是否允许窗口大小缩放
    movable: true, // 是否可以移动
    show: true,  // 是否在创建完成后,让窗口显示,如果希望在用户登录后再显示工具球,则设置成false,再通过消息来打开窗口
    transparent: true, // 设置透明,只有在frame为false时才能生效
    hasShadow: false, // 是否显示阴影
    alwaysOnTop: true, // 窗口是否总是显示在其他窗口之前
    webPreferences: {
      // devTools: true, // 是否打开调试工具,如果要打开调试,最好把窗口的默认大小调大一些,且不要设置初始位置
      nodeIntegration: true,
      contextIsolation: false,
    }
  })
  // 通过获取用户屏幕的宽高来设置悬浮球的初始位置,最好把窗口的默认大小调大一些,且不要设置初始位置
  const size = screen.getPrimaryDisplay().workAreaSize
  ballWin.setPosition(size.width - 150, size.height - 200) // 设置悬浮球位置

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    await ballWin.loadURL(`${process.env.WEBPACK_DEV_SERVER_URL}ball`)
    // ballWin.webContents.openDevTools(); // 打开调试工具
  } else {
    await ballWin.loadURL('app://./ball.html')
  }

  ballWin.on('close', () => {
    ballWin = null
  })
}

// 监听 open-mainWin-window 消息,  显示 主窗口
ipcMain.on('open-mainWin-window', () => {
  if (!mainWin) {
    return
  }
  mainWin.maximize()  // 窗口最大化
})

// 显示 工具球窗口
ipcMain.on('open-ballWin-window', () => {
  if (ballWin) {
    ballWin.show()
  } else {
  // 若窗口不存在,则先创建窗口再显示
    createSBallWindow()
    ballWin.show()
  }
});

// 隐藏 工具球窗口
ipcMain.on('hide-ballWin-window', () => {
  ballWin.hide()
});

// 保存主窗口的当前路由
ipcMain.on('route-change', (event, route) => {
  mainWinRoutePath = route;
});

// 返回保存的路由信息
ipcMain.handle('get-mainWin-route', () => {
  return mainWinRoutePath;
});

// 进入 主窗口的某个页面
ipcMain.on('change-page', async (event, url) => {
  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // 去除url开头的/ 避免造成 http://localhost:8080//...
    await mainWin.loadURL(`${process.env.WEBPACK_DEV_SERVER_URL}${url.replace(/^\//, '')}`)
  } else {
    await mainWin.loadURL(`app://.${url}`)
  }
})

悬浮球页面 ball.vue

<template>
  <div class="ball" @click="goFast">
    <!-- dblclick双击 -->
    <span>工具球</span>
  </div>
</template>

<script>
// 判断是否在 electron 平台的环境当中
let ipcRenderer = null
const isElectron = navigator.userAgent.toLowerCase().indexOf(' electron/') >= 0
if (isElectron) {
  ipcRenderer = require('electron').ipcRenderer
}

export default {
  name: "ball",
  data() {
    return {
    }
  },
  created() {
  },
  methods: {
    goFast() {
      if (isElectron) {
        ipcRenderer.invoke('get-mainWin-route').then((route) => {
          if (route != '/moral/record') {
            // 消息名称、传参arg
            ipcRenderer.send('change-page', '/ball')
          }
          // 发送 open-ballWin-window 消息
          ipcRenderer.send('open-mainWin-window')
        });
      }
    }
  },
}
</script>

<style>
body {
  background: transparent;
  display: flex;
  justify-content: center;
  height: 100%;
  // 这个是用于 移动窗口
  /* -webkit-app-region: drag; */
}

.ball {
  width: 60px;
  height: 60px;
  line-height: 60px;
  border-radius: 50%;
  box-sizing: border-box;
  background: var(--primary-color);
  box-shadow: 0 3px 5px 0 rgba(0, 0, 0, 0.4);
  color: #fff;
  background: linear-gradient(#40a9ff, #096dd9);
  text-align: center;
  cursor: pointer;
  font-size: 14px;
  /* -webkit-app-region: no-drag; */
}
</style>

router.js

import { createRouter, createWebHistory } from 'vue-router';
// 判断是否在 electron 平台的环境当中
const isElectron = navigator.userAgent.toLowerCase().indexOf(' electron/') >= 0
let ipcRenderer = null
if (isElectron) {
  ipcRenderer = require('electron').ipcRenderer
}

const router = createRouter({
  routes: [
	  {
	    path: '/index',
   		component: () => import('@/views/index'),
	    meta: { title: '首页' },
	  },
	  {
	    path: '/ball',
	    component: () => import('@/views/ball'),
	    meta: { title: '工具球' }
	  },
	  ...
  ],
  history: createWebHistory()
});
// 路由守卫
router.beforeEach((to, from, next) => {
  if (isElectron) {
    ipcRenderer.send('route-change', to.path)
  }
  next()
})

export default router;

注意如果使用了screen,一定要引入,否则会启动失败
在这里插入图片描述


4、窗口置顶

// 置顶并显示 主窗口
ipcMain.on('open-mainWin-window', () => {
  if (!mainWin) {
    return
  }
  mainWin.maximize()  // 窗口最大化
  // 当 ballWin 失去置顶状态时,将 mainWin 的置顶级别设为较高值
  mainWin.setAlwaysOnTop(true, 'normal', 1);
  mainWin.focus(); // 让窗口获得焦点
  mainWin.setAlwaysOnTop(false); // 如果不希望它一直保持在最上层,可以之后立即取消置顶,即点击其他应用时,当前窗口会下移一层
})

5、绑定全局快捷键

这里会有个问题,注册之后,只要这个窗口没有隐藏,快捷键响应后,还是会触发事件
例如下方是F5,假设你在QQ应用上,点了F5,当前这个应用也会刷新

import { globalShortcut } from 'electron'
let mainWin, ballWin 
...

 // 全局快捷键 刷新
  globalShortcut.register('F5', () => {
    if (mainWin && ballWin) {
      mainWin.webContents.reloadIgnoringCache();
      ballWin.webContents.reloadIgnoringCache();
    }
  })

完整代码background.js

包含:
1、窗口的菜单、右键菜单、全局快捷键
2、监听窗口的关闭事件,阻止默认行为
3、创建悬浮窗口、窗口的隐藏/显示,促使其他窗口路径跳转
4、窗口置顶事件
5、区分当前环境是electron,还是浏览器

'use strict'

import { app, protocol, BrowserWindow, ipcMain, dialog, Menu, screen, globalShortcut } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'

const path = require('path');
const isDevelopment = process.env.NODE_ENV !== 'production'

protocol.registerSchemesAsPrivileged([
  { scheme: 'app', privileges: { secure: true, standard: true } }
])

let mainWinRoutePath = '/'; // 主窗口的当前路由
let mainWin, ballWin

// 主窗口的顶栏菜单
const template = [
  {
    label: '后退',
    click: () => {
      if (mainWin.webContents.canGoBack()) {
        mainWin.webContents.goBack();
      }
    }
  },
  {
    label: '前进',
    click: () => {
      if (mainWin.webContents.canGoForward()) {
        mainWin.webContents.goForward();
      }
    }
  },
  {
    label: '刷新',
    click: () => {
      mainWin.webContents.reloadIgnoringCache();
    }
  },
];

// 右键菜单
const contextMenu = Menu.buildFromTemplate([
  { label: '复制', role: 'copy' },
  { label: '粘贴', role: 'paste' }
]);


async function createWindow() {
  mainWin = new BrowserWindow({
    width: 800,
    height: 800,
    title: '智慧校园',
    icon: path.join(__dirname, process.env.VUE_APP_LOGO),
    webPreferences: {
      nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
      contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION
    }
  })

  const menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);

  mainWin.webContents.on('context-menu', () => {
    contextMenu.popup({ window: mainWin }); // 在指定窗口上显示上下文菜单
  });

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    await mainWin.loadURL(process.env.WEBPACK_DEV_SERVER_URL + 'index')
    if (!process.env.IS_TEST) mainWin.webContents.openDevTools()
  } else {
    createProtocol('app')
    mainWin.loadURL('app://./index.html')
  }

  // 监听 主窗口 右上角的关闭事件
  mainWin.on('close', (e) => {
    e.preventDefault(); // 阻止窗口默认的关闭行为

    // 显示一个对话框询问用户是否真的想要退出
    const choice = dialog.showMessageBoxSync(null, {
      type: 'info',
      buttons: ['否', '是'],
      title: '退出',
      message: '确定要退出吗?再次开启需重新登录',
    });
    // 如果用户选择“是”,则关闭窗口
    if (choice === 1) {
      // 关闭窗口并释放内存
      mainWin.destroy(); // 关闭主窗口
      app.quit()
    }
  })

  mainWin.once('ready-to-show', () => {
    mainWin.show()
  })
}

// 创建 工具球窗口
async function createSBallWindow() {
  ballWin = new BrowserWindow({
    width: 65, // 悬浮窗口的宽度 比实际DIV的宽度要多5px
    height: 65, // 悬浮窗口的高度 比实际DIV的高度要多5px
    type: 'toolbar',  //创建的窗口类型为工具栏窗口
    frame: false,  // 是否创建有边框的窗口(含最大化/最小化/关闭功能),false创建无边框窗口
    resizable: false, // 是否允许窗口大小缩放
    movable: true, // 是否可以移动
    show: true,  // 是否在创建完成后,让窗口显示
    transparent: true, // 设置透明
    hasShadow: false, // 是否显示阴影
    alwaysOnTop: true, // 窗口是否总是显示在其他窗口之前
    webPreferences: {
      // devTools: true, // 是否打开调试工具,打开调试则最好调整窗口的大小
      nodeIntegration: true,
      contextIsolation: false,
    }
  })
  // 通过获取用户屏幕的宽高来设置悬浮球的初始位置
  const size = screen.getPrimaryDisplay().workAreaSize
  ballWin.setPosition(size.width - 150, size.height - 200) // 设置悬浮球位置

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    await ballWin.loadURL(`${process.env.WEBPACK_DEV_SERVER_URL}ball`)
    // ballWin.webContents.openDevTools(); // 打开调试工具
  } else {
    await ballWin.loadURL('app://./ball.html')
  }

  ballWin.on('close', () => {
    ballWin = null
  })
}

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

// 置顶并显示 主窗口
ipcMain.on('open-mainWin-window', () => {
  if (!mainWin) {
    return
  }
  mainWin.maximize()  // 窗口最大化

  if (ballWin) {
    ballWin.on('always-on-top-changed', (event, isAlwaysOnTop) => {
      if (isAlwaysOnTop) {
        // 当 ballWin 处于最顶端时,将 mainWin 的置顶级别设为较低值
        mainWin.setAlwaysOnTop(true, 'normal', -1);
        mainWin.focus(); // 让窗口获得焦点
      } else {
        // 当 ballWin 失去置顶状态时,将 mainWin 的置顶级别设为较高值
        mainWin.setAlwaysOnTop(true, 'normal', 1);
        mainWin.focus();
        mainWin.setAlwaysOnTop(false); // 如果不希望它一直保持在最上层,可以之后立即取消置顶
      }
    })
    ballWin.setAlwaysOnTop(true, 'normal', 1)
  } else {
    mainWin.setAlwaysOnTop(true);
    mainWin.focus();
    mainWin.setAlwaysOnTop(false);
  }
})

// 隐藏 主窗口
ipcMain.on('hide-mainWin-window', () => {
  mainWin.hide()
})

// 显示 工具球窗口
ipcMain.on('open-ballWin-window', () => {
  if (ballWin) {
    ballWin.show()
  } else {
    createSBallWindow()
    ballWin.show()
  }
});

// 隐藏 工具球窗口
ipcMain.on('hide-ballWin-window', () => {
  ballWin.hide()
});

// 保存主窗口的当前路由
ipcMain.on('route-change', (event, route) => {
  mainWinRoutePath = route;
});

// 返回保存的路由信息
ipcMain.handle('get-mainWin-route', () => {
  return mainWinRoutePath;
});

// 进入 主窗口的某个页面
ipcMain.on('change-page', async (event, url) => {
  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // 去除url开头的/ 避免造成 http://localhost:8080//...
    await mainWin.loadURL(`${process.env.WEBPACK_DEV_SERVER_URL}${url.replace(/^\//, '')}`)
  } else {
    await mainWin.loadURL(`app://.${url}`)
  }
})

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) createWindow()
})

app.on('ready', async () => {
  if (isDevelopment && !process.env.IS_TEST) {
    try {
      await installExtension(VUEJS_DEVTOOLS)
    } catch (e) {
      console.error('Vue Devtools failed to install:', e.toString())
    }
  }
  //创建主窗口
  createWindow()

  //创建球形窗口
  createSBallWindow()

  // 注册 全局快捷键 刷新
   globalShortcut.register('F5', () => {
    if (mainWin && ballWin) {
      mainWin.webContents.reloadIgnoringCache();
      ballWin.webContents.reloadIgnoringCache();
    }
   })
})

if (isDevelopment) {
  if (process.platform === 'win32') {
    process.on('message', (data) => {
      if (data === 'graceful-exit') {
        app.quit()
      }
    })
  } else {
    process.on('SIGTERM', () => {
      app.quit()
    })
  }
}





打包问题

electron-builder打包electron-v12.2.3-win32-x64.zip下载失败,这种情况通常是有electron的4个包,无法下载成功,手动下载后放入到系统对应的文件夹中即可。
分别是
electron-v12.2.3-win32-x64.zip
winCodeSign-2.6.0.7z.zip
nsis-resources3.4.1
nsis-3.4.1

 INFO  Building app with electron-builder:
  • electron-builder  version=22.14.13 os=10.0.22621
  • description is missed in the package.json  appPackageFile=D:\web\campus\wisdom-campus-desktop\dist_electron\bundled\package.json
  • author is missed in the package.json  appPackageFile=D:\web\campus\wisdom-campus-desktop\dist_electron\bundled\package.json
  • writing effective config  file=dist_electron\builder-effective-config.yaml
  • packaging       platform=win32 arch=x64 electron=12.2.3 appOutDir=dist_electron\win-unpacked
  ⨯ Get "https://github.com/electron/electron/releases/download/v12.2.3/electron-v12.2.3-win32-x64.zip": proxyconnect tcp: dial tcp :0: connectex: The requested address is not valid in its context.

在这里插入图片描述

按照错误提示,是electron-v12.2.3-win32-x64.zip这个文件下载失败,可以利用报错信息中的https://github.com/electron/electron/releases/download/v12.2.3/electron-v12.2.3-win32-x64.zip
这个地址进行下载,或者到淘宝的镜像地址里去下载,

1、electron-v12.2.3-win32-x64.zip

到淘宝的镜像地址里去下载,https://registry.npmmirror.com/binary.html?path=electron/,依次找到 12.2.3版本下 -> electron-v12.2.3-win32-x64.zip
通常系统会默认隐藏AppData,关闭隐藏即可
下载后,将压缩包解压到这个目录里:
C:\Users\你的用户名\AppData\Local\electron\Cache
在这里插入图片描述
解压后的文件
在这里插入图片描述

2、winCodeSign-2.6.0.7z.zip

https://registry.npmmirror.com/binary.html?path=electron-builder-binaries/winCodeSign-2.6.0/winCodeSign-2.6.0.7z

下载后,将压缩包解压解压到这个目录里:
C:\Users\你的用户名\AppData\Local\electron-builder\Cache\winCodeSign

注意:这里最后一层是winCodeSign,有的文章里显示的是在Cache文件里解压,这样是错误的

在这里插入图片描述
解压后的:
在这里插入图片描述

3、nsis 和 nsis-resources

https://registry.npmmirror.com/binary.html?path=electron-builder-binaries/
在这里插入图片描述

下载后,将2个压缩包都解压到nsis文件价中,没有nsis文件夹就自己创建一个:
C:\Users\你的用户名\AppData\Local\electron-builder\Cache\nsis

在这里插入图片描述
解压后的
在这里插入图片描述
在这里插入图片描述

最后再改一下镜像

用cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org

或者直接用npm
npm config set registry https://registry.npmmirror.com

https://registry.npm.taobao.org 淘宝镜像在2024年1月份已经过期了,目前虽然还能使用,
但是可以观察到,最终还是代理到了 https://registry.npmmirror.com,所以不如直接用 ** https://registry.npmmirror.com**

再次打包electron 应该就没有什么问题了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1503057.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024想要赚点小钱真的很容易!帮你们找的10个搞钱第二职业

我们都希望在空闲时间里增加一些额外收入&#xff0c;并有机会找到自己热爱的事业&#xff0c;每天贝兼几十上百元是一个不错的开始&#xff0c;小钱也是钱&#xff0c; 搞钱的经验会积少成多。今天分享10个搞钱第二职业&#xff0c;2024想要赚点小钱真的很容易。 一.摆摊卖花 …

计算机找不到msvcr120.dll的五种修复方法,轻松搞定msvcr120.dll丢失问题

当计算机系统中msvcr120.dll文件丢失时&#xff0c;可能会引发一系列运行问题和故障现象。msvcr120.dll是Microsoft Visual C Redistributable Package的一部分&#xff0c;对于许多Windows应用程序的正常运行至关重要。由于msvcr120.dll是许多软件在运行过程中依赖的重要动态链…

blender怎么导入stl格式文件?

stl格式&#xff0c;一般是用来3D打印用的文件&#xff0c;这种模型一般很小&#xff0c;经常做来做一些DIY的配件&#xff0c;如下图&#xff0c;一共有七八个模型&#xff0c;3D打印机把每个模型实体打出来后&#xff0c;就可以给小朋友组装当智益玩具玩了&#xff0c;我们把…

前端面试练习24.3.8

防抖和节流 防抖&#xff08;Debouncing&#xff09;&#xff1a; 防抖是指在短时间内连续触发同一事件时&#xff0c;只执行最后一次触发的事件处理函数。 在实际应用中&#xff0c;常常用于处理用户输入的搜索框或者滚动事件。例如&#xff0c;当用户连续输入搜索关键词时&am…

基于SpringBoot校园失物招领系统的设计与实现(程序+数据库+文档)

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一、研究背景…

python基础9_序列类型

回顾: 什么是变量?,有什么用? 可以变化的量, 就是个容器,多次变化,方便后续使用, 前面介绍了哪些数据类型? bool, str, int, float 用什么函数查看数据的类型? a "hello" print(type(a)) 到了这一步,,我们认识了哪些数据类型呢? int 整型(整数), float…

Python快速入门系列-1

Python快速入门系列 第一章: Python简介1.1 Python的历史与发展1.2 Python的优势与特点1.2.1 易学易用1.2.2 动态类型1.2.3 丰富的标准库与第三方库1.2.4 面向对象与函数式编程1.2.5 广泛应用领域 1.3 Python的应用领域 第一章: Python简介 1.1 Python的历史与发展 Python是一…

IRLINK(红外遥控器)

工具 1.Proteus 8 仿真器 2.keil 5 编辑器 原理图 讲解 简介 红外遥控&#xff1a;是利用红外线进行通信的设备&#xff0c;由红外LED调制后的信号发出&#xff0c;由专用的红外接头进行解调&#xff1b; 通信方式&#xff1a;单工、异步&#xff1b; 红外LED波长&#x…

【JavaEE进阶】 @Transactional详解

文章目录 &#x1f343;前言&#x1f332;rollbackFor&#xff08;异常回滚属性&#xff09;&#x1f384;事务隔离级别&#x1f6a9;MySQL事务隔离级别&#x1f6a9;Spring事务隔离级别 &#x1f38b;Spring事务传播机制&#x1f6a9;什么是事务传播机制&#x1f6a9;事务有哪…

一大波你可能不知道的 Linux 网络工具

如果要在你的系统上监控网络&#xff0c;那么使用命令行工具是非常实用的&#xff0c;并且对于 Linux 用户来说&#xff0c;有着许许多多现成的工具可以使用&#xff0c;如&#xff1a;nethogs, ntopng, nload, iftop, iptraf, bmon, slurm, tcptrack, cbm, netwatch, collectl…

SMT32 TIM1 PWM(发送固定脉冲数)步进电机梯形图加速

&#xff08;因为电机的启停惯性和步进电机越慢扭力越大的原因&#xff09;&#xff1b;所以步进电机使用梯形加速&#xff0c;可以实现更小的丢步 思路&#xff1a;在PWM中断中做计数&#xff0c;前20个脉冲和后20个脉冲频率设置一样低&#xff0c;中间的脉冲频率设置快一点

【Docker】了解Docker Desktop桌面应用程序,TA是如何管理和运行Docker容器(3)

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Docker容器》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…

java-新手笔记(线程篇)

线程 线程是操作系统能进行算法调度的最小单位&#xff0c;它被包含在进程中&#xff0c;是进程中的实际操作单位。程序员可以通过线程进行多处理器编程&#xff0c;使用多线程对运算密集型任务提速. 线程的生命周期与状态&#xff1a;线程具有新建&#xff08;New&#xff0…

“轻松入门Electron:一步步构建梦想中的桌面软件

在数字化的浪潮中&#xff0c;桌面应用依旧占据着其独特而重要的位置&#xff0c;不论是在企业解决方案、专业工具软件还是个性化应用领域中都是如此。随着技术的演进&#xff0c;创建这些应用的过程已经变得更为简单和可行&#xff0c;尤其是随着Electron等框架的出现。Electr…

Android14之禁止vbmeta.img签名校验(一百九十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

动态规划(带你了解 原理 实践)

目录 引言 一、动态规划的基本概念 二、动态规划的应用 1. 背包问题 2. 最短路径问题 3. 0-1背包问题的变种 4. 字符串匹配与编辑距离 5. 金融投资组合优化 6. 生产调度问题 7. 项目管理中的资源分配 三、动态规划算法的优缺点 优点 1 效率高 2 通用性强 缺点&a…

Java实现归并排序算法

Java实现归并排序算法 以下是Java中的归并排序算法实现示例&#xff1a; public class MergeSort {// 归并排序入口函数public static void sort(int[] array) {if (array null || array.length < 1)return;mergeSort(array, 0, array.length - 1);}// 归并排序递归函数pr…

配置oh-my-posh

在windows上的powershell上配置oh-my-posh&#xff0c;使其更像在linux用oh-my-zsh。 首先打开powershell&#xff0c;输入&#xff1a; winget install JanDeDobbeleer.OhMyPosh -s winget安装on-my-posh.exe和oh-my-posh上最新的主题。 之后重启powershell。 打开配置文件…

华为ce12800交换机m-lag(V-STP模式)配置举例

配置## 标题思路 采用如下的思路配置M-LAG双归接入IP网络&#xff1a; 1.在Switch上配置上行接口绑定在一个Eth-Trunk中。 2.分别在SwitchA和SwitchB上配置V-STP、DFS Group、peer-link和M-LAG接口。 3.分别在SwitchA和SwitchB上配置LACP M-LAG的系统优先级、系统ID。 4.分别在…

【粉丝福利第四期】:《低代码平台开发实践:基于React》(文末送书)

文章目录 前言一、React与低代码平台的结合优势二、基于React的低代码平台开发挑战三、基于React的低代码平台开发实践四、未来展望《低代码平台开发实践&#xff1a;基于React》五、粉丝福利 前言 随着数字化转型的深入&#xff0c;企业对应用开发的效率和灵活性要求越来越高…