准备工作:确保本机已经安装了nodejs && npm
检查本地是否已经安装了vue:npm view vue version
;否则先安装vue:npm install vue
创建一个electron-vue的demo项目:vue create electron-vue-demo
安装配置:
? Please pick a preset:
default (babel, eslint)
> Manually select features
? Check the features needed for your project:
(*) Babel
( ) TypeScript
( ) Progressive Web App (PWA) Support
(*) Router
(*) Vuex
>(*) CSS Pre-processors
(*) Linter / Formatter
( ) Unit Testing
( ) E2E Testing
Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n) y
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default):
Sass/SCSS (with dart-sass)
Sass/SCSS (with node-sass)
> Less
Stylus
? Pick a linter / formatter config: (Use arrow keys)
> ESLint with error prevention only
ESLint + Airbnb config
ESLint + Standard config
ESLint + Prettier
? Pick a linter / formatter config: (Use arrow keys)
> ESLint with error prevention only
ESLint + Airbnb config
ESLint + Standard config
ESLint + Prettier
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)
>(*) Lint on save
( ) Lint and fix on commit (requires Git)
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? (Use arrow keys)
> In dedicated config files
In package.json
Save this as a preset for future projects? (y/N) N
此处安装过程需要大约2分钟(视网络决定)
进入项目目录:cd electron-vue-demo
启动 Vue.js App 项目:npm run serve
,打开浏览器登入:http://localhost:8080/
安装electron构建包:vue add electron-builder
使用打印机必须要使用electron 3.0或以下版本,此处先任意选择安装,后续单独更换electron版本。
npm uninstall electron && npm install [email protected] --save-dev
? Choose Electron Version (Use arrow keys)
^4.0.0
^5.0.0
> ^6.0.0
electron包文件较大,此处安装过程耗时较长,建议使用淘宝镜像cnpm安装。
由于electron将版本的问题,./src/background.js
中有一处需要更改配置,否则启动不了electron。
//删除第15行:
// protocol.registerSchemesAsPrivileged([{scheme: 'app', privileges: { secure: true, standard: true } }])
//新增一行:
protocol.registerStandardSchemes(['app'], { secure: true })
默认配置了热加载,代码保存后,electron窗口会体现最新效果。
安装axios:npm install axios --save-dev
封装数据请求方法:
新建文件夹:./src/axios/
新建文件并粘贴代码:
api.js
import requestAll from "./request"; //引用fetch,js
import apiHeader from './url'; //引用url.js
// 数据请求
export function Axios(method, api, params = {}) {
let header = '';
header = apiHeader.server;
// 浏览器访问
if( !window.require ) {
header = '/api';
}
// electron访问
return requestAll.Request(header+api, params, method)
}
request.js
import axios from 'axios';//引入axios
// post请求
function Request(url, data = {}, method = 'post') {
return new Promise((resolve, reject) => {
axios({
url: url,
method: method,
headers: {
'Content-Type': 'application/json'
},
timeout: 30 * 1000, // 30秒超时
data: data
})
.then(res => {
// 统一错误状态码
let errorMessage = '操作失败!';
switch (res.data.code) {
case 200: errorMessage = '请求成功'; break;
case 501: errorMessage = '参数异常'; break;
}
res.data.errorMessage = errorMessage;
//成功
resolve(res.data)
})
.catch(res => {
//失败
reject(res)
})
})
}
export default {
Request: Request
}
url.js
export default {
server: 'http://127.0.0.1:12306'
}
使用方法:
// 引入封装好的axios方法
import { Axios } from "../axios/api";
// 使用例子
Axios("post", url, params)
.then(res => {
if (res.code === 200) {
resolve(res.data);
} else {
_this.$alert(res.errorMessage, "提示", {
confirmButtonText: "知道了"
});
}
})
.catch(err => {
reject();
_this.$alert(`发生错误!\n${err}`, "提示", {
confirmButtonText: "知道了"
});
});
浏览器访问项目时,会遇到请求跨域问题,解决方法(./vue.config.js
文件内容):
// vue.config.js相关代码
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
pathRewrite: {
'^/api': '/'
}
}
}
}
}
路由设置:
./src/router.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: () => import('./views/Home.vue'),
children: [
{
path: '/',
redirect: 'test'
},
{
path: '/test',
name: 'test',
component: () => import('./components/Test.vue') // 主窗口里面的子组件
}
]
},
{
path: '/setUp',
name: 'setUp',
component: () => import('./components/SetUp') // 其他窗口的页面文件
}
]
})
对这个文件进行编写配置:./src/background.js
引入electron中的ipcMain:
import { ipcMain } from 'electron'
主窗口在项目初始化已经设置好,基本配置如下:
function createWindow() {
// Create the browser window.
win = new BrowserWindow({
width: 1336, // 窗体宽度
height: 890, // 窗体高度
frame: false, // 窗体是否需要标题栏
resizable: false, // 窗口是否可以改变尺寸
webPreferences: {
nodeIntegration: true, // 是否集成Node
webSecurity: false // 解决跨域问题
}
})
}
具体更多窗口配置:https://electronjs.org/docs/api/browser-window
electron启动后执行createWindow方法:
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
await installVueDevtools()
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString())
}
}
createWindow()
})
设置窗口访问的页面(写在createWindow方法内)
if (process.env.WEBPACK_DEV_SERVER_URL) { // 如果是开发模式
// Load the url of the dev server if in development mode
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
testWindow.loadURL(process.env.WEBPACK_DEV_SERVER_URL + "/#/test")
// 如果是开发模式,自动打开开发者调试工具
if (!process.env.IS_TEST) win.webContents.openDevTools()
if (!process.env.IS_TEST) testWindow.webContents.openDevTools()
} else { // 如果是打包模式
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
testWindow.loadURL('app://./index.html#/test')
}
设置窗口,开启关闭功能(在createWindow方法内)
win.on('closed', () => {
win = null
})
testWindow.on('close', (e) => {
e.preventDefault()
testWindow.hide();
})
文件./src/background.js
添加窗口的打开和关闭监听:
// 打开窗口
ipcMain.on('open-test', (event, arg) => {
testWindow.show();
testWindow.webContents.send('param', arg) // 触发这个事件时,传递给将要打开的窗口一个数据
})
// 关闭窗口
ipcMain.on('close-test', function () {
testWindow.hide();
})
.vue
文件里触发窗口的打开或关闭事件:
const { ipcRenderer } = require("electron"); // 文件头部新建electron控制窗口的上下文对象
ipcRenderer.send( "open-testEdit", { data: this.data } ); // 打开窗口命令,"open-testEdit"是"background.js"文件中添加的事件监听,send的第二个参数是传递给窗口的数据对象
ipcRenderer.send("close-testEdit"); // 关闭窗口
可以利用electron中的ipcRenderer.send方法给窗口传递数据;当然,在触发打开或关闭窗口的时候也可以传递数据,因为他们都在使用ipcRenderer.send方法。
如果是单纯传递数据(非窗口打开或关闭),可以这样做:
// 首先在文件"./src/background.js"中定义监听方法
ipcMain.on('testChange', (event, message) => {
win.webContents.send('getTestData', message);
}); // 当任何页面触发'testChange'方法时,win窗口可以接收到message这份数据
// 注意:接收数据的窗口(.vue页面),同时需要做好接收数据的工作
mounted() {
// 需要使用electron的ipcRenderer.on方法,参数1:接收数据事件名称,参数2:接收数据或event的方法体
ipcRenderer.on("getTestData", (e, message) => {
console.log( message );
});
},
./package.json
中给打包操作设置参数
{
"name": "WondfoDMS-Pet", // 项目名称(主要应用在软件运行中的名称)
"productName": "WondfoDMS-Pet", // 软件包名称(安装包上的名称)
"version": "6.6.6", // 软件版本号
"scripts": {
"electron:build": "vue-cli-service electron:build --windows --dir --ia32",
/*
*参数意义:
*@ --windows 打包成window软件包
*@ --dir
*@ --ia32 软件包为32位
*/
},
"win": {
"icon": "icon.ico", // 软件包图标,图标放置于项目根目录,尺寸为256*256
"extraResources": "./public" // 页面项目静态文件入口放置的位置
}
}
执行命令npm run electron:build
即可打包,软件位置:./dist_electron/win-ia32-unpacked/*.exe
新建打印中转页面printContent
(有关页面路由router配置,此处不讲述),内容:
<template>
<div class="component">
<webview id="printWebview" ref="printWebview" :src="fullPath" nodeintegration />
</div>
</template>
<script>
const { ipcRenderer } = require("electron");
import path from "path";
export default {
name: "printContent",
data() {
return {
printList: [],
printDeviceName: "",
fullPath: path.join(__static, "print.html"),
messageBox: null,
htmlData: {},
isAllowPrint: false
};
},
mounted() {
const webview = this.$refs.printWebview;
webview.addEventListener("ipc-message", event => {
if (!this.isAllowPrint) return;
if (event.channel === "webview-print-do") {
webview.print(
{
silent: true,
printBackground: true,
deviceName: this.printDeviceName
},
data => {
if (data) {
this.$message({
message: "打印成功!",
type: "success"
});
} else {
this.$alert("打印失败!", "提示", {
confirmButtonText: "知道了"
});
}
}
);
}
});
},
methods: {
print(data, isAllowPrint) {
this.htmlData = data;
// 设置打印机名称
this.printDeviceName = this.htmlData.printDeviceName;
this.isAllowPrint = isAllowPrint;
this.printRender();
},
printRender() {
// 获取节点
const webview = this.$refs.printWebview;
// 发送信息到里的页面
webview.send("webview-print-render", {
printName: this.htmlData.printDeviceName,
data: this.htmlData
});
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
webview {
width: 100%;
height: 100%;
}
</style>
文件 ./src/background.js
中需要新建获取打印列表事件的监听:
// 获取打印列表
ipcMain.on('getPrinterList', (event) => {
//主线程获取打印机列表
const list = printWindow.webContents.getPrinters();
//通过webContents发送事件到渲染线程,同时将打印机列表也传过去
printWindow.webContents.send('printerList', list);
});
触发打印的页面(即打印按钮的页面):
<template>
<div class="component">
<div class="content">
<el-form :inline="true" size="small" label-width="108px">
<el-form-item class="form_item_btn">
<!-- 打印按钮 -->
<el-button type="primary" @click="doPrint()">打印</el-button>
</el-form-item>
</el-form>
<el-row>
<el-col :span="16" style="height:540px;">
<!-- 打印预览 -->
<pinter ref="print" :html-data="HtmlData" class="print_preview"></pinter>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
const { ipcRenderer } = require("electron");
import Pinter from "./PrintContent.vue"; // 引用上面新建的组件
export default {
name: "test",
components: {
Pinter
},
data() {
return {
HtmlData: "",
selectedPrinter: "",
};
},
mounted() {
// 获取打印列表
ipcRenderer.send("getPrinterList");
ipcRenderer.on("printerList", (e, message) => {
// 设置打印机名字
this.selectedPrinter = message[0].name; // 可在页面给予选择打印机功能,此处不讲述
});
},
methods: {
// 触发打印事件
doPrint() {
this.HtmlData = '打印数据';
this.$refs.print.print(this.HtmlData);
}
}
};
</script>
新建预览文件(同时也是打印模版文件,关键!):./public/print.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!-- 如果需要使用第三方组件,需要额外放置组件文件在public中,并引用 -->
<link rel="stylesheet" href="./element-ui.css" type="text/css" />
<script src="./vue.js"></script>
<script src="./element-ui.js"></script>
<!-- 引用本页面css -->
<link rel="stylesheet" href="./print_preview.css" type="text/css" />
</head>
<body id='bd'>
<div id="app">
打印内容
</div>
<script>
// 具体数据渲染,根据实际业务开发
new Vue({
el: '#app',
data: function () {
return { data: '' // 本页需要的数据 }
},
mounted() {
const { ipcRenderer } = require('electron')
// 接收打印数据 参数2:第1步中给webview发送的数据{ printName, data }
ipcRenderer.on('webview-print-render', (event, data) => {
this.data = data.data;
ipcRenderer.sendToHost('webview-print-do');
});
}
})
</script>
</body>
</html>
打印功能参考:https://juejin.im/post/5bf8b580e51d4522143b7b03
框架搭建参考视频:https://www.youtube.com/watch?v=Fl7—SEORQ
electron构建的第三方包源码:https://github.com/nklayman/vue-cli-plugin-electron-builder