vben-admin 改造为 electron 版本
- vben-admin 简介
- 目标
- 其他方案
- 下载 vben-admin 源码
- 使用 pnpm 安装依赖
- 运行
- electron 版本
- 使用 pnpm 安装依赖
- devDependencies
- dependencies
- 使用 yarn 安装
- devDependencies
- dependencies
- 集成electron代码
- index.ts
- rollupElectronConfig.ts
- compilerElectron.ts
- startElectron.ts
- 命令
- 运行 electron
- 打包
- 出现错误 : 找不到文件
- 使用 pnpm 安装依赖
- electron 示例
- 进程间通信
- 渲染进程
- 主进程
- 添加路由菜单
- 国际化
- 测试
- 进程间通信
- 集成串口
- 安装
- 可能出现的问题
- 国际化
- 添加路由菜单
- 串口的使用示例
- 测试
- 打包出现的问题
- serialport 打包问题最终解决方法
- 安装
- 集成 SQLite
- 安装 sqlite3
- 不要使用 pnpm
- kenx.js
- 相关代码
- 创建数据库
- 渲染进程中使用 kenx
- 添加路由
- 国际化
- 开发环境运行
- 出现的错误提示
- 打包
- 绿色版没问题
- 安装版的报错
- 打包时复制数据库文件
- 提升权限
- 安装 sqlite3
vben-admin 简介
https://vvbin.cn/doc-next/
目标
目标:授之以鱼,不如授之以渔,本文旨在教会大家自己动手改造 vben-admin 改造为 electron 版本。
下面是改造后的 electron 版本源码地址,供参考:
https://gitee.com/Artisan-k/vben-admin-electron
其他方案
vite-plugin-electron:https://github.com/electron-vite/vite-plugin-electron
下载 vben-admin 源码
完整版:
https://github.com/vbenjs/vue-vben-admin
目前最新发布版本:v2.8.0
https://github.com/vbenjs/vue-vben-admin/releases/tag/v2.8.0
轻量版:
https://github.com/vbenjs/vben-admin-thin-next
目前最新发布版本:v2.7.2
使用 pnpm 安装依赖
下载后运行:
E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0>pnpm install
?ERR_PNPM_INVALID_OVERRIDE_SELECTOR? Cannot parse the "//" selector in the overrides
出现错误,安装失败。
把 package.json 里面的
"resolutions": {
"//": "Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it",
"bin-wrapper": "npm:bin-wrapper-china",
"rollup": "^2.56.3"
},
的 "//":
一行去掉,
把 pnpm-lock.yaml 里面的
overrides:
//: Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it
bin-wrapper: npm:bin-wrapper-china
rollup: ^2.56.3
的 "//":
一行去掉,
再次运行如下命令进行安装:
E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0>pnpm install
下载完,有一个提示错误:
> vben-admin@2.7.2 prepare E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0
> husky install
fatal: not a git repository (or any of the parent directories): .git
运行如下命令:
E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0>git init
Initialized empty Git repository in E:/artisan/labs/vben-admin-electron/src/vue-vben-admin-2.8.0/.git/
再次运行如下命令进行安装:
E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0>pnpm install
运行
运行如下命令进行启动运行
E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0>pnpm serve
查看运行结果:
> npm run dev
> vben-admin@2.7.2 dev
> vite
Pre-bundling dependencies:
vue
pinia
vue-router
vue-i18n
ant-design-vue
(...and 72 more)
(this will be run only when your dependencies or config have changed)
vite v2.6.13 dev server running at:
> Network: http://192.168.0.102:3100/
> Local: http://localhost:3100/
> Network: http://172.25.32.1:3100/
> Network: http://172.23.144.1:3100/
> Network: http://172.27.112.1:3100/
> Network: http://172.28.96.1:3100/
访问:http://localhost:3100
账号密码是预先好的,直接点击登录按钮。
electron 版本
使用 pnpm 安装依赖
推荐使用 yarn,但是为了亲自体验使用 pnpm 出现的问题,这里还是使用 pnpm
devDependencies
pnpm install electron --save-dev
pnpm install electron-builder --save-dev
pnpm install electron-connect --save-dev
pnpm install electron-contextmenu-middleware --save-dev
pnpm install electron-input-menu --save-dev
pnpm install wait-on --save-dev
下面依次安装,先安装 electron
> pnpm install electron --save-dev
## devDependencies
+ electron 19.0.6
?WARN? Issues with peer dependencies found
.
├─┬ rollup-plugin-visualizer
│ └── ? missing peer rollup@^2.0.0
└─┬ vite-plugin-mock
└─┬ @rollup/plugin-node-resolve
├── ? missing peer rollup@^2.42.0
└─┬ @rollup/pluginutils
└── ? missing peer rollup@^1.20.0||^2.0.0
Peer dependencies that should be installed:
rollup@">=2.42.0 <3.0.0">
有警告,安装 rollup ,运行如下命令进行安装:
> pnpm install rollup
安装完成后,提示安装了如下依赖:
dependencies:
+ rollup 2.59.0 (2.75.7 is available)
devDependencies:
- rollup-plugin-visualizer 5.5.2
+ rollup-plugin-visualizer 5.5.2
- vite-plugin-mock 2.9.6
+ vite-plugin-mock 2.9.6
安装 electron-builder
> pnpm install electron-builder --save-dev
...
devDependencies:
+ electron-builder 23.1.0
...
安装 electron-connect
> pnpm install electron-connect --save-dev
devDependencies:
+ electron-connect 0.6.3
安装 electron-contextmenu-middleware
> pnpm install electron-contextmenu-middleware --save-dev
devDependencies:
+ electron-contextmenu-middleware 1.0.3
安装 electron-input-menu
> pnpm install electron-input-menu --save-dev
devDependencies:
+ electron-input-menu 2.1.0
安装 electron-input-menu
> pnpm install wait-on --save-dev
evDependencies:
+ wait-on 6.0.1
dependencies
pnpm install electron-is-dev
pnpm install rollup
pnpm install rollup-plugin-esbuild
pnpm install @rollup/plugin-alias
pnpm install @rollup/plugin-commonjs
pnpm install @rollup/plugin-json
pnpm install @rollup/plugin-node-resolve
pnpm install esbuild
pnpm install chalk
pnpm install install ora
下面依次安装:
> pnpm install electron-is-dev
dependencies:
+ electron-is-dev 2.0.0
> pnpm install rollup
dependencies:
+ rollup 2.59.0 (2.75.7 is available)
devDependencies:
- rollup-plugin-visualizer 5.5.2
+ rollup-plugin-visualizer 5.5.2
- vite-plugin-mock 2.9.6
+ vite-plugin-mock 2.9.6
> pnpm install rollup-plugin-esbuild
dependencies:
+ rollup-plugin-esbuild 4.9.1
?WARN? Issues with peer dependencies found
.
└─┬ @rollup/plugin-commonjs
└── ? unmet peer rollup@^2.68.0: found 2.59.0
> pnpm install @rollup/plugin-alias
dependencies:
+ @rollup/plugin-alias 3.1.9
> pnpm install @rollup/plugin-commonjs
dependencies:
+ @rollup/plugin-commonjs 22.0.0
?WARN? Issues with peer dependencies found
.
└─┬ @rollup/plugin-commonjs
└── ? unmet peer rollup@^2.68.0: found 2.59.0
> pnpm install @rollup/plugin-json
dependencies:
+ @rollup/plugin-json 4.1.0
?WARN? Issues with peer dependencies found
.
└─┬ @rollup/plugin-commonjs
└── ? unmet peer rollup@^2.68.0: found 2.59.0
> pnpm install @rollup/plugin-node-resolve
dependencies:
+ @rollup/plugin-node-resolve 13.3.0
?WARN? Issues with peer dependencies found
.
└─┬ @rollup/plugin-commonjs
└── ? unmet peer rollup@^2.68.0: found 2.59.0
----------------------------------------
## 上面的安装几个依赖包都提示:
## ? unmet peer rollup@^2.68.0: found 2.59.0
## 这里重新更新下 rollup的版本
> pnpm install rollup@^2.68.0
## 更新了各个版本,输出结果如下:
dependencies:
- @rollup/plugin-alias 3.1.9
+ @rollup/plugin-alias 3.1.9
- @rollup/plugin-commonjs 22.0.0
+ @rollup/plugin-commonjs 22.0.0
- @rollup/plugin-json 4.1.0
+ @rollup/plugin-json 4.1.0
- @rollup/plugin-node-resolve 13.3.0
+ @rollup/plugin-node-resolve 13.3.0
- rollup 2.59.0
+ rollup 2.68.0 (2.75.7 is available)
- rollup-plugin-esbuild 4.9.1
+ rollup-plugin-esbuild 4.9.1
devDependencies:
- rollup-plugin-visualizer 5.5.2
+ rollup-plugin-visualizer 5.5.2
- vite-plugin-mock 2.9.6
+ vite-plugin-mock 2.9.6
----------------------------------------
> pnpm install esbuild
dependencies:
+ esbuild 0.14.47
> pnpm install chalk
dependencies:
+ chalk 5.0.1
> pnpm install install ora
dependencies:
+ install 0.13.0
+ ora 6.1.0
使用 yarn 安装
devDependencies
yarn add electron electron-builder electron-connect electron-contextmenu-middleware electron-input-menu wait-on -D
dependencies
yarn add electron-is-dev rollup rollup-plugin-esbuild @rollup/plugin-alias @rollup/plugin-commonjs @rollup/plugin-json @rollup/plugin-json @rollup/plugin-node-resolve esbuild chalk ora -S
集成electron代码
index.ts
新建 electron-main 文件夹,新建 index.ts 文件
注意:
是项目根目录下,即:vue-vben-admin-2.8.0/electron-main
代码清单: electron-main/index.ts
import { app, BrowserWindow, screen } from 'electron';
import is_dev from 'electron-is-dev';
import { join } from 'path';
let mainWindow: BrowserWindow | null = null;
class createWin {
constructor() {
const displayWorkAreaSize = screen.getAllDisplays()[0].workArea;
mainWindow = new BrowserWindow({
width: parseInt(`${displayWorkAreaSize.width * 0.85}`, 10),
height: parseInt(`${displayWorkAreaSize.height * 0.85}`, 10),
movable: true,
// frame: false,
show: false,
center: true,
resizable: true,
// transparent: true,
titleBarStyle: 'default',
webPreferences: {
devTools: true,
contextIsolation: false,
nodeIntegration: true,
//enableRemoteModule: true,
webSecurity: false, //解决:打包后出现跨域问题
},
backgroundColor: '#fff',
});
const URL = is_dev
? `http://localhost:${process.env.PORT}` // vite 启动的服务器地址
: `file://${join(__dirname, '../index.html')}`; // vite 构建后的静态文件地址
mainWindow.loadURL(URL);
mainWindow.on('ready-to-show', () => {
mainWindow.show();
});
}
}
app.whenReady().then(() => new createWin());
const isFirstInstance = app.requestSingleInstanceLock();
if (!isFirstInstance) {
app.quit();
} else {
app.on('second-instance', () => {
if (mainWindow) {
mainWindow.focus();
}
});
}
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
new createWin();
}
});
rollupElectronConfig.ts
在目录 build/config 目录下新建 rollupElectronConfig.ts
代码清单:build/config/rollupElectronConfig.ts
import path from 'path';
import { RollupOptions } from 'rollup';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import esbuild from 'rollup-plugin-esbuild';
import alias from '@rollup/plugin-alias';
import json from '@rollup/plugin-json';
export function getRollupOptions(): RollupOptions {
return {
input: path.join(__dirname, '../../electron-main/index.ts'),
output: {
file: path.join(__dirname, '../../dist/main/build.js'),
format: 'cjs',
name: 'ElectronMainBundle',
sourcemap: true,
},
plugins: [
nodeResolve({ preferBuiltins: true, browser: true }), // 消除碰到 node.js 模块时?警告
commonjs(),
json(),
esbuild({
// All options are optional
include: /\.[jt]sx?$/, // default, inferred from `loaders` option
exclude: /node_modules/, // default
// watch: process.argv.includes('--watch'), // rollup 中有配置
sourceMap: false, // default
minify: process.env.NODE_ENV === 'production',
target: 'es2017', // default, or 'es20XX', 'esnext'
jsxFactory: 'React.createElement',
jsxFragment: 'React.Fragment',
// Like @rollup/plugin-replace
define: {
__VERSION__: '"x.y.z"',
},
// Add extra loaders
loaders: {
// Add .json files support
// require @rollup/plugin-commonjs
'.json': 'json',
// Enable JSX in .js files too
'.js': 'jsx',
},
}),
alias({
entries: [{ find: '/@main/', replacement: path.join(__dirname, '../../electron-main') }],
}),
],
external: [
'crypto',
'assert',
'fs',
'util',
'os',
'events',
'child_process',
'http',
'https',
'path',
'electron',
],
};
}
compilerElectron.ts
在 build/script 目录下新建 compilerElectron.ts
代码清单: build/script/compilerElectron.ts
import rollup, { OutputOptions } from 'rollup';
import chalk from 'chalk';
import ora from 'ora';
import waitOn from 'wait-on';
import net from 'net';
import { URL } from 'url';
import minimist from 'minimist';
import electronConnect from 'electron-connect';
import { getRollupOptions } from '../config/rollupElectronConfig';
const argv = minimist(process.argv.slice(2));
const TAG = '[compiler-electron]';
export function startCompilerElectron(port = 80) {
// 因为 vite 不会重定向到 index.html,所以直接写 index.html 路由。
const ELECTRON_URL = `http://localhost:${port}/index.html`;
const spinner = ora(`${TAG} Electron build...`);
const electron = electronConnect.server.create({ stopOnClose: true });
const rollupOptions = getRollupOptions();
function watchFunc() {
// once here, all resources are available
const watcher = rollup.watch(rollupOptions);
watcher.on('change', (filename) => {
const log = chalk.green(`change -- ${filename}`);
console.log(TAG, log);
});
watcher.on('event', (ev) => {
if (ev.code === 'END') {
// init-未启动、started-第一次启动、restarted-重新启动
electron.electronState === 'init' ? electron.start() : electron.restart();
} else if (ev.code === 'ERROR') {
console.log(ev.error);
}
});
}
if (argv.watch) {
waitOn(
{
resources: [ELECTRON_URL],
timeout: 5000,
},
(err) => {
if (err) {
const { hostname } = new URL(ELECTRON_URL);
const serverSocket = net.connect(port, hostname, () => {
watchFunc();
});
serverSocket.on('error', (e) => {
console.log(err);
console.log(e);
process.exit(1);
});
} else {
watchFunc();
}
},
);
} else {
spinner.start();
rollup
.rollup(rollupOptions)
.then((build) => {
spinner.stop();
console.log(TAG, chalk.green('Electron build successed.'));
build.write(rollupOptions.output as OutputOptions);
})
.catch((error) => {
spinner.stop();
console.log(`\n${TAG} ${chalk.red('构建报错')}\n`, error, '\n');
});
}
}
startElectron.ts
在 build/script 目录下新建 startElectron.ts
代码清单: build/script/startElectron.ts
import { createServer } from 'vite';
import path from 'path';
import { startCompilerElectron } from './compilerElectron';
import minimist from 'minimist';
(async () => {
const argv = minimist(process.argv.slice(2));
console.log(argv);
const isDev = argv.env === 'development';
let port: number | undefined = undefined;
if (isDev) {
const server = await createServer({
root: path.resolve(__dirname, '../../'),
});
const app = await server.listen();
port = app.config.server.port;
process.env.PORT = `${port}`;
}
startCompilerElectron(port);
})();
命令
在 package.json 添加如下命令
{
...
"main": "dist/main/build.js",
"build": {
"appId": "xxx@gmail.com",
"electronDownload": {
"mirror": "https://npm.taobao.org/mirrors/electron/"
},
"files": [
"!node_modules",
"dist/**",
],
"asar": false,
"mac": {
"artifactName": "${productName}_setup_${version}.${ext}",
"target": [
"dmg"
]
},
"linux": {
"icon": "build/icons/512x512.png",
"target": [
"deb"
]
},
"win": {
"target": [
{
"target": "nsis",
"arch": [
"x64"
]
}
],
"artifactName": "${productName}_setup_${version}.${ext}"
},
"nsis": {
"oneClick": false,
"perMachine": false,
"allowToChangeInstallationDirectory": true,
"deleteAppDataOnUninstall": false
}
},
"scripts": {
...
"start": "node ./build/script/electron/dev",
"dev:app": "esno ./build/script/startElectron.ts --env=development --watch",
"build:app": "npm run build && esno ./build/script/startElectron.ts --env=production && electron-builder ",
}
...
}
- main:electron程序入口
- build:打包
- scripts:electron 相关命令,
- dev:app:开发环境运行
- build:app:项目打包
运行 electron
> pnpm dev:app
点击登录按钮:
打包
> pnpm build:app
执行打包命令后,在项目的目录下,自动生成一个 dist 目录
出现错误 : 找不到文件
打开绿色版,会出现如下错误:
图片、js 的地址错误,找不到文件。
打开 .env.production 文件
找到
# public path
VITE_PUBLIC_PATH = /
将其修改为:
# public path
## 注意:生产环境发布为 Electron 客户端时得修改为 ./ ,否则Electron 客户端找不到文件
VITE_PUBLIC_PATH = ./
重新打包:
> pnpm build:app
打开绿色版,运行一切正常
点击登录按钮:
electron 示例
进程间通信
渲染进程
代码清单: src/views/electron/process/ipc.vue
<template>
<PageWrapper
title="示例:进程间通信"
contentBackground
contentClass="p-4"
content="在这里将演示如何在主进程与渲染进程间进行通信"
>
<Divider>向主进程发送消息</Divider>
<Alert
class="mt-4"
type="info"
message="点击按钮后请查看编译器(Visual Code)控制台消息"
show-icon
/>
<div class="mt-4">
<a-button type="primary" size="small" @click="hanleSendMessageToMain">
向主进程发送消息
</a-button>
</div>
<Divider>向主进程发送消息并接收主进程的消息</Divider>
<div class="mt-4">
<a-button type="primary" size="small" @click="hanleSendMessageToMainNeedReply">
向主进程发送消息并接收主进程的消息
</a-button>
<p> 主进程返回的消息: {{ dataFromMain }} </p>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue';
import { Alert, Divider } from 'ant-design-vue';
import { PageWrapper } from '/@/components/Page';
const { ipcRenderer } = require('electron');
//import { ipcRenderer } from 'electron'; // 这样引用,打包后报错,请使用:require('electron')
export default defineComponent({
components: { PageWrapper, Alert, Divider },
setup() {
const dataFromMain = ref('');
const hanleSendMessageToMain = () => {
ipcRenderer.send('event_from_renderer', { param: 123 });
};
const hanleSendMessageToMainNeedReply = () => {
ipcRenderer.send('event_from_renderer_need_replay', { param: 'abc' });
};
const registerEvents = () => {
ipcRenderer.on('event_from_main_replay', (_, data) => {
console.log('In renderer:event_from_main_replay-->data:', data);
dataFromMain.value += data;
});
};
onMounted(() => {
registerEvents();
});
return {
hanleSendMessageToMain,
hanleSendMessageToMainNeedReply,
dataFromMain,
};
},
});
</script>
<style lang="less" scoped></style>
其中,
-
渲染进程发送消息到主进程
const hanleSendMessageToMainNeedReply = () => { ipcRenderer.send('event_from_renderer_need_replay', { param: 'abc' }); };
-
监听来自主进程的响应消息
const registerEvents = () => { ipcRenderer.on('event_from_main_replay', (_, data) => { console.log('In renderer:event_from_main_replay-->data:', data); dataFromMain.value += data; }); }; onMounted(() => { registerEvents(); });
主进程
主进程的代码写在 electron-baisc.ts 文件中
代码清单: electron-main/main/electron-baisc.ts
import { ipcMain } from 'electron';
const registerEvents = () => {
ipcMain.on('event_from_renderer', (event, data) => {
console.log('event_from_renderer->data:', data);
});
ipcMain.on('event_from_renderer_need_replay', (event, data) => {
console.log('event_from_renderer_need_replay->data:', data);
event.reply('event_from_main_replay', '【渲染进程,你的消息我已收到】');
});
};
export default {
registerEvents,
};
然后在主进程 index.ts 中使用
代码清单: electron-main/index.ts
...
import electronBaisc from './main/electron-baisc';
class createWin {
...
mainWindow.on('ready-to-show', () => {
mainWindow.show();
registerEvents();
});
}
}
//-----------------------------------------------------------------------------------
// 在这个文件中,你可以包含应用程序剩余的所有部分的代码,
// 也可以拆分成几个文件,然后用 require 导入。
//-----------------------------------------------------------------------------------
function registerEvents() {
electronBaisc.registerEvents();
}
添加路由菜单
新建 electron.ts,系统会根据路由自动生成菜单项
代码清单:src/routes/modules/electron.ts
import type { AppRouteModule } from '/@/router/types';
import { getParentLayout, LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';
const electron: AppRouteModule = {
path: '/electron',
name: 'Electron',
component: LAYOUT,
redirect: '/electron/process/ipc',
meta: {
orderNo: 1999,
icon: 'ant-design:share-alt-outlined',
title: t('routes.electron.RouteName'),
},
children: [
{
path: 'Process',
name: 'ElectronProcess',
component: getParentLayout('ElectronProcess'),
meta: {
title: t('routes.electron.Process'),
},
redirect: '/electron/process/ipc',
children: [
{
path: 'ipc',
name: 'ElectronIPC',
meta: {
title: t('routes.electron.InterProcessCommunication'),
},
component: () => import('/@/views/electron/process/Ipc.vue'),
},
],
},
],
};
export default electron;
其中:
- routes.electron.xxx: 是支持国际化的字符串
国际化
路由菜单支持国际化,需要新建两个多语言资源,新建如下两个文件,新建后系统会自动加载,可以直接使用。
- 英文
代码清单:src/locales/lang/en/routes/electron.ts
export default {
RouteName: 'Electron demo',
Process: 'Process',
InterProcessCommunication: 'IPC',
};
- 简体中文
代码清单:src/locales/lang/zh-CN/routes/electron.ts
export default {
RouteName: 'Electron示例',
Process: '进程',
InterProcessCommunication: '进程间通信',
};
测试
运行开发环境
> pnpm dev:app
点击【 向主进程发送消息 】按钮,向主进程发送消息,在 Visual Code 的控制台中可以看到输出日志:
event_from_renderer->data: { param: 123 }
点击【 向主进程发送消息并接收主进程的消息 】按钮, 向主进程发送消息并接收主进程的消息,如下图所示:
集成串口
Github: https://github.com/serialport/node-serialport
官网:https://serialport.io/
文档:https://serialport.io/docs/
https://serialport.io/docs/guide-electron
其它资料:
使用SerialPort库进行Node物联网项目开发:https://zhuanlan.zhihu.com/p/98050314
安装
https://serialport.io/docs/guide-installation
运行如下命令进行安装
> pnpm install serialport
dependencies:
+ serialport 10.4.0 #目前最新版本
可能出现的问题
10.x.x 版本已经允许编译了各个平台的lib,并支持 Typescript,一般不会出现以下的问题。
10.x.x 版本以下可能会出现 serialport 不能使用的问题
若 serialport 不能使用,重新编译 serialport 的模块,这个看情况而定!
安装:node-v16.14.0-x64.msi
安装:python-3.10.2-amd64.exe
pnpm install global node-gyp
pnpm install serialport@9.2.8
pnpm install electron-rebuild
.\node_modules\.bin\electron-rebuild.cmd
serialport@9.x 支持 windows 7、8、10+
serialport@10.x 支持 windows 10+
国际化
路由菜单支持国际化,需要新建两个多语言资源,新建如下两个文件,新建后系统会自动加载,可以直接使用。
- 英文
代码清单:src/locales/lang/en/routes/electron.ts
export default {
...
SerialPort: 'SerialPort',
SerialPortAssitant: 'SP-Assitant',
};
- 简体中文
代码清单:src/locales/lang/zh-CN/routes/electron.ts
export default {
...
SerialPort: '串口',
SerialPortAssitant: '串口助手',
};
添加路由菜单
在 src/routes/modules/electron.ts,添加菜单
代码清单:src/routes/modules/electron.ts
import type { AppRouteModule } from '/@/router/types';
import { getParentLayout, LAYOUT } from '/@/router/constant';
import { t } from '/@/hooks/web/useI18n';
const electron: AppRouteModule = {
path: '/electron',
name: 'Electron',
component: LAYOUT,
redirect: '/electron/process/ipc',
meta: {
orderNo: 1999,
icon: 'ant-design:share-alt-outlined',
title: t('routes.electron.RouteName'),
},
children: [
{
// 进程间通信路由
...
},
{
path: 'SerialPort',
name: 'SerialPort',
component: getParentLayout('SerialPort'),
meta: {
title: t('routes.electron.SerialPort'),
},
redirect: '/electron/serialport/assistant',
children: [
{
path: 'ipc',
name: 'SPAssistant',
meta: {
title: t('routes.electron.SerialPortAssitant'),
},
component: () => import('/@/views/electron/serialport/Assistant.vue'),
},
],
},
],
};
export default electron;
串口的使用示例
代码清单: src/views/electron/serialport/Assistant.vue
<template>
<PageWrapper
title="示例:串口助手"
contentBackground
contentClass="p-4"
content="在这里将演示如何在Electron中使用 Node-SerialPort, 官网:https://serialport.io/"
>
<div>
<a-alert type="error" v-if="errorMsg" :message="errorMsg" banner closable />
<a-alert type="success" v-if="message" :message="message" show-icon closable />
<div style="margin-top: 10px">
<a-row>
<a-col :span="18">
<div>
<h1>接收缓存区</h1>
<a-row>
<textarea cols="80" rows="10" v-model="recievedData"></textarea>
</a-row>
<a-row>
<a-space>
<a-button class="" @click="handleClearRecievedData"> 清空 </a-button>
</a-space>
</a-row>
</div>
<div>
<h1>发送缓存区</h1>
<a-row>
<textarea cols="80" rows="10" v-model="toSendData"></textarea>
</a-row>
<a-row>
<a-space>
<a-button @click="handleSendData"> 发送 </a-button>
<a-button @click="handleClearSendData"> 清空 </a-button>
</a-space>
</a-row>
</div>
</a-col>
<a-col :span="6">
<div>
<a-form :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item label="串口" v-bind="validateInfos.path">
<a-input v-model:value="portOptionsRef.path" />
</a-form-item>
<a-form-item label="波特率" v-bind="validateInfos.baudRate">
<a-input v-model:value="portOptionsRef.baudRate" />
</a-form-item>
<a-form-item :wrapper-col="{ span: 14, offset: 10 }">
<a-button type="primary" size="small" @click.prevent="handleOpenSerilPort">
打开串口
</a-button>
<a-button type="primary" size="small" danger @click="handleClosSerilPort"
>关闭串口</a-button
>
</a-form-item>
</a-form>
</div>
</a-col>
</a-row>
</div>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, reactive, toRaw, onMounted, toRefs, onUnmounted } from 'vue';
import { PageWrapper } from '/@/components/Page';
import { Form, Alert, Row, Col, message } from 'ant-design-vue';
const { SerialPort } = require('serialport');
// import { SerialPort } from 'serialport';
const useForm = Form.useForm;
export default defineComponent({
components: {
PageWrapper,
'a-row': Row,
'a-col': Col,
'a-form': Form,
'a-form-item': Form.Item,
'a-alert': Alert,
},
setup() {
let port;
const state = reactive({
errorMsg: '',
message: '',
toSendData: '',
recievedData: '',
});
const portOptionsRef = reactive({
path: 'COM1',
baudRate: 9600, //波特率
dataBits: 8, //数据位
parity: 'none', //奇偶校验
stopBits: 1, //停止位
flowControl: false,
autoOpen: false, //不自动打开
});
const { validate, validateInfos } = useForm(
portOptionsRef,
reactive({
path: [
{
required: true,
message: '请输入串口',
},
],
baudRate: [
{
required: true,
message: '请输入波特率',
},
],
}),
);
onMounted(() => {
getSerialPortList();
createeSerialPort();
});
const getSerialPortList = () => {
SerialPort.list().then(
(ports) => {
if (!ports || ports.length == 0) {
state.errorMsg = '未监测到串口信息';
} else {
// state.message = `监测到可用串口:${ports.map((e) => e.path)}`;
ports.forEach(console.log);
}
},
(err) => console.error(err),
);
};
const createeSerialPort = () => {
if (!port) {
port = new SerialPort(toRaw(portOptionsRef));
registerEvents();
}
};
const handleOpenSerilPort = () => {
validate()
.then(() => {
openSerialPort();
})
.catch((err) => {
console.log('handleOpenSerilPort validate error', err);
});
};
const openSerialPort = () => {
const result = openPort();
if (!result.success) {
message.error(result.message);
} else {
message.success(result.message);
}
};
const openPort = () => {
const result = {
success: true,
message: '',
};
console.log('---------1-------------');
console.log('openPort-port:', port);
console.log('openPort-por1t.isOpen:', port?.isOpen);
if (port == null) {
console.log('---------2-------------');
createeSerialPort();
}
console.log('---------3-------------');
console.log('openPort-port:', port);
console.log('openPort-port.isOpen:', port.isOpen);
if (port.isOpen) {
console.log('---------4-------------');
result.message = `串口 ${portOptionsRef.path} 已打开成功`;
return result;
} else {
port.open((err) => {
console.assert('dddddd');
console.log('---------5-------------');
if (err) {
console.log('---------7-------------');
result.success = false;
result.message = `串口 ${portOptionsRef.path} 打开失败:${err}`;
return result;
} else {
console.log('---------8-------------');
result.message = `串口 ${portOptionsRef.path} 打开成功`;
return result;
}
});
}
console.log('---------9-------------');
return result;
};
const registerEvents = () => {
// 监听接收串口数据
// Switches the port into "flowing mode"
port.on('data', function (data) {
console.log('RecievedData:', data);
handleRecievedData(data);
});
};
const handleRecievedData = (data) => {
const str = Buffer.from(data).toString().toString();
state.recievedData += str;
};
const handleClearRecievedData = () => {
state.recievedData = '';
};
const handleClearSendData = () => {
state.toSendData = '';
};
const handleSendData = () => {
sendDateToPort(state.toSendData);
};
const sendDateToPort = (data) => {
port.write(data);
//port.write(Buffer.from(data));
};
const handleClosSerilPort = () => {
const result = closeSerialPort();
if (!result.success) {
message.error(result.message);
} else {
message.success(result.message);
}
};
const closeSerialPort = () => {
const result = {
success: true,
message: '',
};
if (port && port.isOpen) {
port.close((err) => {
if (err) {
result.success = false;
result.message = `串口 ${portOptionsRef.path} 关闭失败:${err}`;
return result;
}
});
}
port = null;
result.message = `串口 ${portOptionsRef.path} 已关闭`;
return result;
};
onUnmounted(() => {
closeSerialPort();
});
return {
labelCol: {
span: 10,
},
wrapperCol: {
span: 14,
},
validateInfos,
...toRefs(state),
portOptionsRef,
handleOpenSerilPort,
handleClosSerilPort,
handleSendData,
handleClearRecievedData,
handleClearSendData,
};
},
});
</script>
<style lang="less" scoped></style>
注意:这里使用的是
const { SerialPort } = require('serialport');
而不是
import { SerialPort } from 'serialport';
测试
运行开发环境
> pnpm dev:app
打开菜单:
Tips:
1.电脑没有串口的,可以安装虚拟串口软件:Virtual Serial Port Driver
2.可以自行查找串口调试助手,比如 STC公司的:stc-isp
下面进行测试:
先使用 Virtual Serial Port Driver 添加了两个虚拟串口
然后使用串口通讯助手测试数据通信,如下图所示:
打包出现的问题
打包后运行程序,出现了错误,如下图所示:
Tips:
按组合键 【ctrl + shift + i】打开开发者工具(Developer Tools)
错误信息如下:
vendor.b7ee0cad.js:147 Error: Cannot find module '@serialport/parser-byte-length'
Require stack:
- E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0\dist\win-unpacked\resources\app\node_modules\serialport\dist\index.js
...
index.ca01a1fc.js:1 Error: Cannot find module '@serialport/parser-byte-length'
Require stack:
- E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0\dist\win-unpacked\resources\app\node_modules\serialport\dist\index.js
- E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0\dist\win-unpacked\resources\app\dist\index.html
...
serialport 打包问题最终解决方法
最终的解决方案是:
把 package.json
文件中的如下配置:
...
"build": {
...
"files": [
"!node_modules",
"dist/**"
],
....
}
...
中的 "!node_modules", 删除掉,即最终的配置为:
...
"build": {
...
"files": [
"dist/**"
],
....
}
...
使用 pnpm 进行打包
pnpm build:app
不幸的是,使用 pnpm 会出现如下错误:
> pnpm build:app
[compiler-electron] Electron build successed.
? electron-builder version=23.1.0 os=10.0.19044
? loaded configuration file=package.json ("build" field)
? description is missed in the package.json appPackageFile=E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0\package.json
? writing effective config file=dist\builder-effective-config.yaml
? rebuilding native dependencies dependencies=@serialport/bindings-cpp@10.7.0 platform=win32 arch=x64
? cannot execute cause=fork/exec C:\Users\wei\AppData\Roaming\npm\node_modules\pnpm\bin\pnpm.cjs: %1 is not a valid Win32 application.
command='C:\Users\wei\AppData\Roaming\npm\node_modules\pnpm\bin\pnpm.cjs' rebuild @serialport/bindings-cpp@10.7.0
workingDir=
?ELIFECYCLE? Command failed with exit code 1.
更换 pnpm 为 yarn:
-
删除掉 node_modules 文件夹
-
删除文件:pnpm-lock.yaml
-
安装依赖
yarn install
-
打包
yarn build:app
这样执行打包后,打包文件把 node_modules也打包到如下文件夹:
dist\win-unpacked\resources\app\node_modules
如下图所示:
点击安装文件【vben-admin_setup_2.8.0.exe】,进行安装,安装后的如下目录中,可以看到被打包的 node_modules 文件夹
安装文件也变大了,但是这是目前能找到的唯一解决方案。
TODO:看看怎么优化下,只导入 serialprot及其依赖
集成 SQLite
https://blog.csdn.net/jiyulonely/article/details/77089701
菜鸟教程:https://www.runoob.com/sqlite/sqlite-where-clause.html
安装 sqlite3
yarn add sqlite3
不要使用 pnpm
使用 pnpm 安装 sqlite3
pnpm install sqlite3 --save
pnpm install electron-rebuild --save
.\node_modules\.bin\electron-rebuild.cmd
或者
> npm install sqlite3 --build-from-source --runtime=electron --target=17.1.0 --disturl=https://atom.io/download/electron
- --target=17.1.0: 是项目中使用的 electron 版本
使用 pnpm 安装 sqlite3,会出现各种各样的问题,比如如下错误:
> pnpm install
? Building module: sqlite3, Completed: 1在此解决方案中一次生成一个项目。若要启用并行生成,请添加“-m”开关。
? Building module: sqlite3, Completed: 1C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Microsoft\VC\v170\Microsoft.CppBuild.targets(989,5): error MSB3191: 无法创建目录“E:\artisan\labs\antdv-pr
o\src\electron\node_modules\.pnpm\registry.npmmirror.com+sqlite3@5.0.2\registry.npmmirror.com+node-addon-api@3.2.1\node_modules\node-addon-api\Release\obj\nothing\reg
istry.npmmirror.com+node-addon-api@3.2.1\node_modules\node-addon-api”。指定的路径或文件名太长,或者两者都太长。完全限定文件名必须少于 260 个字符,并且目录名必须少于 248 个字符。 [E:\artisan\labs\antdv-pro\src\electr
......
用 npm 或 yarn 命令安装,不用 electron-rebuild
npm install sqlite3 --build-from-source --runtime=electron --target=13.0.0 --dist-url=https://atom.io/download/electron
或
yarn add sqlite3 --build-from-source --runtime=electron --target=13.0.0 --dist-url=https://atom.io/download/electron
在vben-admin中,直接使用如下命令安装即可:
yarn add sqlite3
electron-builder 会自动帮我们编译出对应 electron 版本的 sqlite3
kenx.js
https://knexjs.org/
https://github.com/knex/knex
knex js 是一个sql 指令构建器,可以操作 sqlite、mysql等
安装 kenx.js
yarn add knex -S
knex.js 链接 sqlite3时,还得安装 @vscode/sqlite3
yarn add @vscode/sqlite3
相关代码
创建数据库
代码清单:electron-main/common/database-sqlite.ts
require('sqlite3'); //确保knex使用sqlite3时它已加载
console.log('------1--------');
const knex = require('knex');
console.log('------2--------');
const database = knex({
client: 'sqlite3',
connection: {
filename: './vben-admin-db.sqlite',
},
useNullAsDefault: true,
});
console.log('------3--------');
// 检查数据库是否存在数据库表,不存在则创建
database.schema.hasTable('users').then((exists) => {
console.log('------4--------', `!exists: ${!exists}`);
if (!exists) {
console.log('------5--------');
return database.schema.createTable('users', (t) => {
t.increments('id').primary(); //创建自增的id列,并设置为主键
t.string('name', 100);
t.boolean('isActived');
});
}
});
console.log('------6--------');
export default database;
渲染进程中使用 kenx
代码清单:src/views/electron/sqlite/SqliteKenx.vue
<template>
<PageWrapper
title="示例:SQLite & Knex.js"
contentBackground
contentClass="p-4"
content="在这里将演示如何在Electron中使用 SQLite & Knex.js, 官网:https://knexjs.org"
>
<div>
<div style="margin-top: 10px">
<h2>Users</h2>
<a-button @click="hanldeAddUser">添加</a-button>
</div>
<div>序号| Id | name | isActived</div>
<div v-for="item in users" :key="item.id">
{{ item.id }} | {{ item.name }} | {{ item.isActived }} |
<a-button @click="hanldeDelUser(item.id)">删除</a-button>
</div>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, onMounted } from 'vue';
import { PageWrapper } from '/@/components/Page';
import { User } from './types';
import database from '../../../../electron-main/common/database-sqlite';
export default defineComponent({
components: {
PageWrapper,
},
setup() {
const state = reactive({
users: new Array<User>(),
});
onMounted(() => {
getUserList();
});
const getUserList = () => {
database('users')
.select()
.orderBy('id', 'desc')
.then((items) => {
console.log('users:', items);
state.users = items;
})
.catch(console.error);
};
const hanldeAddUser = () => {
const user = {
name: 'users' + Math.random() * 10000,
isActived: false,
};
database('users')
.insert(user)
.then(() => {
getUserList();
})
.catch(console.error);
};
const hanldeDelUser = (id) => {
console.log('id:', id);
database('users')
.where('id', id)
.delete()
.then(() => {
getUserList();
})
.catch(console.error);
};
return {
...toRefs(state),
hanldeAddUser,
hanldeDelUser,
};
},
});
</script>
<style lang="less" scoped></style>
代码清单:src/views/electron/sqlite/types.ts
export interface User {
id: number;
name: string;
isActived: boolean;
}
添加路由
代码清单:src/router/routes/modules/elctron.ts
const electron: AppRouteModule = {
path: '/electron',
name: 'Electron',
...
},
children: [
...
{
path: 'Sqlite',
name: 'Sqlite',
component: getParentLayout('Sqlite'),
meta: {
title: t('routes.electron.Sqlite'),
},
redirect: '/electron/Sqlite/sqlite-kenx',
children: [
{
path: 'sqlite-kenx',
name: 'SqliteKenx',
meta: {
title: t('routes.electron.SqliteKenx'),
},
component: () => import('/@/views/electron/sqlite/SqliteKenx.vue'),
},
],
},
]
}
国际化
路由菜单支持国际化,需要新建两个多语言资源,新建如下两个文件,新建后系统会自动加载,可以直接使用。
- 英文
代码清单:src/locales/lang/en/routes/electron.ts
export default {
...
Sqlite: 'Sqlite',
SqliteKenx: 'Sqlite-Kenx',
};
- 简体中文
代码清单:src/locales/lang/zh-CN/routes/electron.ts
export default {
...
Sqlite: 'Sqlite',
SqliteKenx: 'Sqlite-Kenx',
};
开发环境运行
yarn dev:app
程序启动后,点击菜单【Sqlite | Sqlite-Kenx】,如下图所示:
第一次运行点击菜单时,数据库还没有生成,这时会在项目的根目录下生成一个名为【vben-admin-db.sqlite】的sqlite数据库,如下图所示:
点击【添加】按钮,添加一个 User,
点击 【删除】删除 User
出现的错误提示
出现如下错误提示,但是程序还是可以运行,
X [ERROR] Could not resolve "mock-aws-s3"
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:43:28:
43 │ const AWSMock = require('mock-aws-s3');
? ~~~~~~~~~~~~~
You can mark the path "mock-aws-s3" as external to exclude it from the bundle, which will remove
this error. You can also surround this "require" call with a try/catch block to handle this
failure at run-time instead of bundle-time.
X [ERROR] Could not resolve "aws-sdk"
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:76:22:
76 │ const AWS = require('aws-sdk');
? ~~~~~~~~~
You can mark the path "aws-sdk" as external to exclude it from the bundle, which will remove this
error. You can also surround this "require" call with a try/catch block to handle this failure at
run-time instead of bundle-time.
X [ERROR] Could not resolve "nock"
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:112:23:
112 │ const nock = require('nock');
? ~~~~~~
You can mark the path "nock" as external to exclude it from the bundle, which will remove this
error. You can also surround this "require" call with a try/catch block to handle this failure at
run-time instead of bundle-time.
20:17:55 [vite] error while updating dependencies:
Error: Build failed with 3 errors:
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:43:28: ERROR: Could not resolve "mock-aws-s3"
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:76:22: ERROR: Could not resolve "aws-sdk"
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:112:23: ERROR: Could not resolve "nock"
at failureErrorWithLog (E:\artisan\labs\vben-admin-electron\src\vue-vben-admin-2.8.0\node_modules\esbuild\lib\main.js:1605:15)
...
Vite Error, /node_modules/.vite/deps/vue.js?v=db3ca739 optimized info should be defined
Vite Error, /node_modules/.vite/deps/pinia.js?v=db3ca739 optimized info should be defined
Vite Error, /node_modules/.vite/deps/vue-router.js?v=db3ca739 optimized info should be defined
Vite Error, /node_modules/.vite/deps/ant-design-vue.js?v=db3ca739 optimized info should be defined
Vite Error, /node_modules/.vite/deps/vue-i18n.js?v=db3ca739 optimized info should be defined
Vite Error, /node_modules/.vite/deps/vue.js?v=db3ca739 optimized info should be defined
Vite Error, /node_modules/.vite/deps/ant-design-vue.js?v=db3ca739 optimized info should be defined
Vite Error, /node_modules/.vite/deps/lodash-es.js?v=db3ca739 optimized info should be defined
Vite Error, /node_modules/.vite/deps/vite-plugin-theme_es_client.js?v=db3ca739 optimized info should be defined
Vite Error, /node_modules/.vite/deps/axios.js?v=db3ca739 optimized info should be defined
Vite Error, /node_modules/.vite/deps/vite-plugin-theme_es_client.js?v=db3ca739 optimized info should be defin
....
根据报错提示,
Could not resolve "mock-aws-s3"...
You can mark the path "mock-aws-s3" as external
...
Could not resolve "aws-sdk...
You can mark the path "aws-sdk" as external
...
Could not resolve "nock" ...
You can mark the path "nock" as external
安装 Vite 插件: vite-plugin-resolve-externals
yarn add vite-plugin-resolve-externals -D
然后创建插件:
代码清单: build/vite/plugin/externals.ts
import resolveExternalsPlugin from 'vite-plugin-resolve-externals';
export function configResolveExternalsPlugin() {
return resolveExternalsPlugin({
'mock-aws-s3': 'mock-aws-s3',
'aws-sdk': 'aws-sdk',
nock: 'nock',
});
}
把无法 resolve 的模块配置进去,最后把生成的插件对象添加到 vite.config.ts 的插件配置节点,
代码清单:vite.config.ts
import { createVitePlugins } from './build/vite/plugin';
...
export default ({ command, mode }: ConfigEnv): UserConfig => {
...
// The vite plugin used by the project. The quantity is large, so it is separately extracted and managed
plugins: createVitePlugins(viteEnv, isBuild),
}
代码清单:build/vite/plugin/index.ts
import { configResolveExternalsPlugin } from './externals';
...
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
const vitePlugins: (Plugin | Plugin[])[] = [...];
...
vitePlugins.push(configResolveExternalsPlugin());
return vitePlugins;
}
其中, vitePlugins.push(configResolveExternalsPlugin()); 把刚才创建的插件添加到 vite.config.ts 中。
打包
yarn build:app
也是需要把 package.json 文件
"build": {
"appId": "xxx@gmail.com",
...
"files": [
"!node_modules",
"dist/**"
],
...
中的 "!node_modules" 删掉,即最终配置为:
"build": {
"appId": "xxx@gmail.com",
...
"files": [
"dist/**"
],
...
否则,报错,如下图所示:
index.4583ee62.js:117 Error: Cannot find module 'sqlite3'
绿色版没问题
打包成功后,打开程序的绿色版,第一次运行点击菜单时,数据库还没有生成,这时会在程序的根目录下生成一个名为【vben-admin-db.sqlite】的sqlite数据库,如下图所示:
点击【添加】按钮,添加一个 User,
点击 【删除】删除 User
安装版的报错
在目录中dist 双击安装文件 vben-admin_setup_2.8.0.exe 进行安装,安装完成后,打开应用程序,会报如下错误:
错误信息如下:
Error: SQLITE_CANTOPEN: unable to open database file
没有找到数据库文件。
打包时复制数据库文件
这时需要把数据库也打包进去,修改 package.json,
"build": {
"appId": "vben-admin@gmail.com",
...
"extraFiles":["./vben-admin-db.sqlite"],
...
Tips:
extraFiles 的配置项见:
https://www.electron.build/configuration/contents.html#extrafiles
这样配置后,打包时,会把数据库文件 vben-admin-db.sqlite 复制到程序的目录下。
然后进行打包,点安装文件进行安装,
从安装目录中,可以看到数据库文件 vben-admin-db.sqlite 已经复制到程序的根目录下,如下图所示:
打开程序,读取数据库没有问题,但是如果对数据库进行新增,删除操作又报错,
attempt to write a readonly database
如下图所示:
可以修改数据库文件的属性为可以:写入,修改,如下图所示:
但是让用户安装后去修改这个显然是不可取的。
提升权限
这时需要提升权限,修改 package.json,
"build": {
"appId": "vben-admin@gmail.com",
...
"extraFiles":["./vben-admin-db.sqlite"],
...
"nsis": {
...
"perMachine": false,
"allowElevation": true,
...
}
Tips:
allowElevation 的配置项,参见:https://www.electron.build/generated/nsisoptions
oneClick
=true
Boolean - Whether to create one-click installer or assisted.
perMachine
=false
Boolean - Whether to show install mode installer page (choice per-machine or per-user) for assisted installer. Or whether installation always per all users (per-machine).If
oneClick
istrue
(default): Whether to install per all users (per-machine).If
oneClick
isfalse
andperMachine
istrue
: no install mode installer page, always install per-machine.If
oneClick
isfalse
andperMachine
isfalse
(default): install mode installer page.
allowElevation
=true
Boolean - assisted installer only. Allow requesting for elevation. If false, user will have to restart installer with elevated permissions.
allowToChangeInstallationDirectory
=false
Boolean - assisted installer only. Whether to allow user to change installation directory.
重新打包,然后进行安装,这时安装界面如下:
点下一步,请一定使用它设置的默认安装目录,
C:\Users\wei\AppData\Local\Programs\vben-admin
如下图所示:
安装成功后,这时不会再提示错误了。
特别注意:
当设置 "allowElevation"的值为 true 时,如果自定义安装目录是无法安装成功的,如下图所示,
然后安装提示错误:
所以这时可以禁止用户选择安装路径,修改 package.json 文件:
...
"nsis": {
...
"allowToChangeInstallationDirectory": false,
}
本文暂时没有评论,来添加一个吧(●'◡'●)