Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
electron-printer
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
zhuzhequan
electron-printer
Commits
cd3269e1
Commit
cd3269e1
authored
May 11, 2026
by
wusiyi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
更新readme
parent
947867a5
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
202 additions
and
149 deletions
+202
-149
README.md
+202
-149
No files found.
README.md
View file @
cd3269e1
### 前言:
# 生产软件
## 项目启动 (2026.05.11)
1.
安装 vue 脚手架 3.0
```
shell
# 卸载1.x或2.x旧版本
npm uninstall
-g
@vue/cli
# 安装@vue/cli 版本=3.0.0
npm install
-g
@vue/cli@3.0.0
# 查看vue-cli版本
vue
-V
```
2. 使用 cnpm 安装依赖, cnpm 版本
=
7.1.1
```
shell
npm install
-g
cnpm@7.1.1
cnpm
-v
cnpm install
```
3. 启动项目
```
shell
# 测试环境
npm run electron:serve:test
# 生产环境
npm run electron:serve:prod
```
## 项目打包
```
shell
# 测试环境
npm run electron:build:test
# 生产环境
npm run electron:build:prod
```
- 如果打包报错 rcedit 相关,关闭杀毒软件并使用管理员权限运行命令
## 项目搭建
### 前言
本文讲述 Vue 3.0 + Electron + Express + Lowdb 框架搭建过程, 以及少量示例代码;
本文讲述 Vue 3.0 + Electron + Express + Lowdb 框架搭建过程, 以及少量示例代码;
...
@@ -11,7 +56,7 @@
...
@@ -11,7 +56,7 @@
3. 部署 Express 充当 Vue+Electron 的 Web Restful Api 后端, 并通过 Lowdb 记录一些 App 的系统设置信息到文件中, 以便于下一次启动时仍能访问(不同于 Browser 端保存)。
3. 部署 Express 充当 Vue+Electron 的 Web Restful Api 后端, 并通过 Lowdb 记录一些 App 的系统设置信息到文件中, 以便于下一次启动时仍能访问(不同于 Browser 端保存)。
4. 让 Vue+Electron+Express 三者共同协作, 看起来是某一个云上服务的 App 形式的 Client 端。
4. 让 Vue+Electron+Express 三者共同协作, 看起来是某一个云上服务的 App 形式的 Client 端。
### 准备部分
:
### 准备部分
- 安装 node.js
- 安装 node.js
...
@@ -99,7 +144,7 @@
...
@@ -99,7 +144,7 @@
```scss
```scss
// 全局CSS常量定义
// 全局CSS常量定义
@import
'./config.scss'
;
@import
"./config.scss"
;
// 所有修改element-ui的样式, 以避免单页面scoped中修改权限不够的问题
// 所有修改element-ui的样式, 以避免单页面scoped中修改权限不够的问题
//@import "./elementui.scss"
//@import "./elementui.scss"
```
```
...
@@ -152,46 +197,46 @@
...
@@ -152,46 +197,46 @@
- 手工生成 vue.config.js
- 手工生成 vue.config.js
```javascript
```javascript
const path
=
require
(
'path'
)
const path = require(
"path");
module.exports = {
module.exports = {
// 基本路径
// 基本路径
publicPath: process.env.NODE_ENV
===
'production'
?
''
:
'/'
,
publicPath: process.env.NODE_ENV === "production" ? "" : "/"
,
// 输出文件目录
// 输出文件目录
outputDir: process.env.NODE_ENV
===
'production'
?
'dist'
:
'devdist'
,
outputDir: process.env.NODE_ENV === "production" ? "dist" : "devdist"
,
// eslint-loader 是否在保存的时候检查
// eslint-loader 是否在保存的时候检查
lintOnSave: false,
lintOnSave: false,
/** vue3.0内置了webpack所有东西,
/** vue3.0内置了webpack所有东西,
* webpack配置,see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
* webpack配置,see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
**/
**/
chainWebpack:
(
config
)
=>
{
chainWebpack: config
=> {
const svgRule
=
config.module.rule
(
'svg'
)
const svgRule = config.module.rule("svg");
svgRule.uses.clear
()
svgRule.uses.clear();
svgRule
svgRule
.use
(
'svg-sprite-loader'
)
.use("svg-sprite-loader"
)
.loader
(
'svg-sprite-loader'
)
.loader("svg-sprite-loader"
)
.options({
.options({
symbolId:
'icon-[name]'
,
symbolId: "icon-[name]"
,
include:
[
'./src/icons'
]
,
include: ["./src/icons"]
})
});
config.module
config.module
.rule
(
'pug'
)
.rule("pug"
)
.test(/\.pug$/)
.test(/\.pug$/)
.use
(
'pug-html-loader'
)
.use("pug-html-loader"
)
.loader
(
'pug-html-loader'
)
.loader("pug-html-loader"
)
.end
()
.end();
},
},
configureWebpack:
(
config
)
=>
{
configureWebpack: config
=> {
config.resolve = {
config.resolve = {
// 配置解析别名
// 配置解析别名
extensions:
[
'.js'
,
'.json'
,
'.vue'
]
, // 自动添加文件名后缀
extensions: [".js", ".json", ".vue"
], // 自动添加文件名后缀
alias: {
alias: {
vue:
'vue/dist/vue.js'
,
vue: "vue/dist/vue.js",
'@'
: path.resolve
(
__dirname,
'./src'
)
,
"@": path.resolve(__dirname, "./src"),
'@c'
: path.resolve
(
__dirname,
'./src/components'
)
,
"@c": path.resolve(__dirname, "./src/components")
}
,
}
}
};
},
},
// 生产环境是否生成 sourceMap 文件
// 生产环境是否生成 sourceMap 文件
productionSourceMap: false,
productionSourceMap: false,
...
@@ -204,15 +249,15 @@
...
@@ -204,15 +249,15 @@
// css预设器配置项
// css预设器配置项
loaderOptions: {
loaderOptions: {
sass: {
sass: {
prependData:
`
@import
"./src/styles/main.scss"
;
`
,
prependData: `@import "./src/styles/main.scss";`
}
,
}
},
},
// 启用 CSS modules for all css / pre-processor files.
// 启用 CSS modules for all css / pre-processor files.
requireModuleExtension:
true
,
// 是否开启支持‘foo.module.css’样式
requireModuleExtension: true
// 是否开启支持‘foo.module.css’样式
},
},
// use thread-loader for babel & TS in production build
// use thread-loader for babel & TS in production build
// enabled by default if the machine has more than 1 cores
// enabled by default if the machine has more than 1 cores
parallel: require
(
'os'
)
.cpus
()
.length
>
1,
parallel: require("os"
).cpus().length > 1,
/**
/**
* PWA 插件相关配置,see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
* PWA 插件相关配置,see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
*/
*/
...
@@ -220,35 +265,35 @@
...
@@ -220,35 +265,35 @@
// webpack-dev-server 相关配置
// webpack-dev-server 相关配置
devServer: {
devServer: {
open: false, // 编译完成是否打开网页
open: false, // 编译完成是否打开网页
host:
'0.0.0.0'
, // 指定使用地址,默认localhost,0.0.0.0代表可以被外界访问
host: "0.0.0.0"
, // 指定使用地址,默认localhost,0.0.0.0代表可以被外界访问
port: 8090, // 访问端口
port: 8090, // 访问端口
https: false, // 编译失败时刷新页面
https: false, // 编译失败时刷新页面
hot: true, // 开启热加载
hot: true, // 开启热加载
hotOnly: false,
hotOnly: false,
proxy: {
proxy: {
// 配置跨域
// 配置跨域
'/devApi'
:
{
"/devApi"
: {
//要访问的跨域的api的域名
//要访问的跨域的api的域名
target:
'http://www.web-jshtml.cn'
,
target: "http://www.web-jshtml.cn"
,
ws: true,
ws: true,
changOrigin: true,
changOrigin: true,
pathRewrite: {
pathRewrite: {
'^/devApi'
:
'/productapi'
,
"^/devApi": "/productapi"
}
,
}
}
,
}
},
},
overlay: {
overlay: {
// 全屏模式下是否显示脚本错误
// 全屏模式下是否显示脚本错误
warnings: true,
warnings: true,
errors:
true
,
errors: true
}
,
}
// before: app => {}
// before: app => {}
},
},
/**
/**
* 第三方插件配置
* 第三方插件配置
*/
*/
pluginOptions:
{}
,
pluginOptions: {}
}
}
;
```
```
- 改造其中的 vue 为 pug 格式
- 改造其中的 vue 为 pug 格式
...
@@ -285,14 +330,14 @@
...
@@ -285,14 +330,14 @@
export const lang = {
export const lang = {
slice: {
slice: {
placeholder: {
placeholder: {
name:
'请输出切片名称'
,
name: "请输出切片名称"
,
nst:
'请选择NST模板'
,
nst: "请选择NST模板"
},
},
tips: {
tips: {
sla:
'带宽:{0} Mbps, 时延: {1} ms'
,
sla: "带宽:{0} Mbps, 时延: {1} ms"
}
,
}
}
,
}
}
};
```
```
- 把 i18n 加入到 Vue, 修改 src/main.js:
- 把 i18n 加入到 Vue, 修改 src/main.js:
...
@@ -400,64 +445,64 @@ vue add electron-builder
...
@@ -400,64 +445,64 @@ vue add electron-builder
修改了 App 窗口大小, 取消了跨域限制, 取消了菜单栏, 修改了 App 的窗口 Icon:
修改了 App 窗口大小, 取消了跨域限制, 取消了菜单栏, 修改了 App 的窗口 Icon:
```
javascript
```
javascript
'use strict'
"use strict";
import { app, protocol, BrowserWindow, Menu } from
'electron'
import { app, protocol, BrowserWindow, Menu } from
"electron";
import { createProtocol } from
'vue-cli-plugin-electron-builder/lib'
import { createProtocol } from
"vue-cli-plugin-electron-builder/lib";
const isDevelopment = process.env.NODE_ENV !==
'production'
const isDevelopment = process.env.NODE_ENV !==
"production";
let win
let win
;
protocol.registerSchemesAsPrivileged(
[
protocol.registerSchemesAsPrivileged(
[
{ scheme: 'app', privileges: { secure: true, standard: true } },
{ scheme: "app", privileges: { secure: true, standard: true } }
])
])
;
function createWindow() {
function createWindow() {
win = new BrowserWindow({
win = new BrowserWindow({
width: 1200,
width: 1200,
height: 800,
height: 800,
icon: './src/assets/logo.png'
,
icon: "./src/assets/logo.png"
,
webPreferences: {
webPreferences: {
webSecurity: false,
webSecurity: false,
nodeIntegration: true,
nodeIntegration: true
},
}
})
});
// 取消菜单
// 取消菜单
Menu.setApplicationMenu(null)
Menu.setApplicationMenu(null);
if (process.env.WEBPACK_DEV_SERVER_URL) {
if (process.env.WEBPACK_DEV_SERVER_URL) {
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL);
if (!process.env.IS_TEST) win.webContents.openDevTools()
if (!process.env.IS_TEST) win.webContents.openDevTools();
} else {
} else {
createProtocol('app')
createProtocol("app");
win.loadURL('app://./index.html')
win.loadURL("app://./index.html");
}
}
win.on('closed'
, () => {
win.on("closed"
, () => {
win = null
win = null;
})
});
}
}
app.on(
'activate'
, () => {
app.on(
"activate"
, () => {
if (win === null) {
if (win === null) {
createWindow()
createWindow();
}
}
})
})
;
app.on(
'ready'
, async () => {
app.on(
"ready"
, async () => {
createWindow()
createWindow();
})
})
;
if (isDevelopment) {
if (isDevelopment) {
if (process.platform === 'win32'
) {
if (process.platform === "win32"
) {
process.on('message', (data)
=> {
process.on("message", data
=> {
if (data === 'graceful-exit'
) {
if (data === "graceful-exit"
) {
app.quit()
app.quit();
}
}
})
});
} else {
} else {
process.on('SIGTERM'
, () => {
process.on("SIGTERM"
, () => {
app.quit()
app.quit();
})
});
}
}
}
}
```
```
...
@@ -529,72 +574,72 @@ vue add electron-builder
...
@@ -529,72 +574,72 @@ vue add electron-builder
- src/backend/store/db.js: 提供数据对象访问能力
- src/backend/store/db.js: 提供数据对象访问能力
```
javascript
```
javascript
import Datastore from
'lowdb'
import Datastore from
"lowdb";
import FileSync from
'lowdb/adapters/FileSync'
import FileSync from
"lowdb/adapters/FileSync";
import path from
'path'
import path from
"path";
import fs from
'fs-extra'
import fs from
"fs-extra";
import LodashId from
'lodash-id'
import LodashId from
"lodash-id";
// 引入remote模块
// 引入remote模块
import { app, remote } from
'electron'
import { app, remote } from
"electron";
// 根据process.type来分辨在哪种模式使用哪种模块,
// 根据process.type来分辨在哪种模式使用哪种模块,
// 在主进程调用 为 browser, 在渲染进程调用为 renderer
// 在主进程调用 为 browser, 在渲染进程调用为 renderer
const APP = process.type ===
'renderer' ? remote.app : app
const APP = process.type ===
"renderer" ? remote.app : app;
// 获取用户目录 C:
\U
sers
\s
hihe
\A
ppData
\R
oaming
\v
ue-node-lowdb
// 获取用户目录 C:
\U
sers
\s
hihe
\A
ppData
\R
oaming
\v
ue-node-lowdb
const STORE_PATH = APP.getPath(
'userData')
const STORE_PATH = APP.getPath(
"userData");
if (process.type !==
'renderer'
) {
if (process.type !==
"renderer"
) {
// 如果不存在路径,创建
// 如果不存在路径,创建
if (!fs.pathExistsSync(STORE_PATH)) {
if (!fs.pathExistsSync(STORE_PATH)) {
fs.mkdirpSync(STORE_PATH)
fs.mkdirpSync(STORE_PATH);
}
}
}
}
const adapter = new FileSync(path.join(STORE_PATH,
'database.json'))
// 初始化lowdb读写的json文件名以及存储路径
const adapter = new FileSync(path.join(STORE_PATH,
"database.json"));
// 初始化lowdb读写的json文件名以及存储路径
const db = Datastore(adapter) // lowdb接管该文件
const db = Datastore(adapter)
;
// lowdb接管该文件
//通过lodash-id这个插件可以很方便地为每个新增的数据自动加上一个唯一标识的id字段
//通过lodash-id这个插件可以很方便地为每个新增的数据自动加上一个唯一标识的id字段
db._.mixin(LodashId)
db._.mixin(LodashId)
;
// 初始化 ( 示例 )
// 初始化 ( 示例 )
if (
if (
!db
!db
.read()
.read()
.has('NSTs'
)
.has("NSTs"
)
.value()
.value()
) {
) {
db.set('NSTs',
[
]).write()
db.set("NSTs",
[
]).write();
db.get('NSTs'
)
db.get("NSTs"
)
.insert({ label: '差动保护', value: 'nst_001'
})
.insert({ label: "差动保护", value: "nst_001"
})
.write()
.write();
db.get('NSTs'
)
db.get("NSTs"
)
.insert({ label: '龙门吊', value: 'nst_002'
})
.insert({ label: "龙门吊", value: "nst_002"
})
.write()
.write();
}
}
if (
if (
!db
!db
.read()
.read()
.has('PLMNs'
)
.has("PLMNs"
)
.value()
.value()
) {
) {
db.read()
db.read()
.set('PLMNs'
,
[
])
.set("PLMNs"
,
[
])
.write()
.write();
db.read()
db.read()
.get('PLMNs'
)
.get("PLMNs"
)
.insert({ label: '中国移动01', value: '960-001'
})
.insert({ label: "中国移动01", value: "960-001"
})
.write()
.write();
db.read()
db.read()
.get('PLMNs'
)
.get("PLMNs"
)
.insert({ label: '中国联通03', value: '960-003'
})
.insert({ label: "中国联通03", value: "960-003"
})
.write()
.write();
db.read()
db.read()
.get('PLMNs'
)
.get("PLMNs"
)
.insert({ label: '中国电信07', value: '960-007'
})
.insert({ label: "中国电信07", value: "960-007"
})
.write()
.write();
}
}
// ES6写法: 暴露
// ES6写法: 暴露
export { db as default }
export { db as default }
;
```
```
其他 lowdb 的详细信息可以参考 LowDB.md 文件, 以及网址: https://www.jianshu.com/p/d46abfa4ddc9
其他 lowdb 的详细信息可以参考 LowDB.md 文件, 以及网址: https://www.jianshu.com/p/d46abfa4ddc9
...
@@ -612,39 +657,39 @@ vue add electron-builder
...
@@ -612,39 +657,39 @@ vue add electron-builder
代码: src/backend/webserver/index.js:
代码: src/backend/webserver/index.js:
```
javascript
```
javascript
import express from
'express'
import express from
"express";
import router from
'./routes/index.js'
import router from
"./routes/index.js";
const PORT = 3000
const PORT = 3000
;
const webApp = express()
const webApp = express()
;
//webApp.use(logger("./logs"));
//webApp.use(logger("./logs"));
webApp.use(express.json())
webApp.use(express.json())
;
webApp.use(express.urlencoded({ extended: false }))
webApp.use(express.urlencoded({ extended: false }))
;
webApp.use(
'/', router)
webApp.use(
"/", router);
// catch 404
// catch 404
webApp.use((req, res, next) => {
webApp.use((req, res, next) => {
res.status(404).send('Sorry! 404 Error.')
res.status(404).send("Sorry! 404 Error.");
})
})
;
// error handler, 4个参数
// error handler, 4个参数
webApp.use((err, req, res, next) => {
webApp.use((err, req, res, next) => {
// set locals, only providing error in development
// set locals, only providing error in development
res.locals.message = err.message
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {}
res.locals.error = req.app.get("env") === "development" ? err : {};
// render the error page
// render the error page
res.status(err.status || 500)
res.status(err.status || 500);
res.json({
res.json({
message: err.message,
message: err.message,
error: err,
error: err
})
});
})
})
;
webApp.set(
'port', PORT)
webApp.set(
"port", PORT);
webApp.listen(PORT, () => console.log(
`App listening on port ${PORT}`
))
webApp.listen(PORT, () => console.log(
`App listening on port ${PORT}`
))
;
export { webApp as default }
export { webApp as default }
;
```
```
* Express 的 API 路由:
* Express 的 API 路由:
...
@@ -652,24 +697,24 @@ vue add electron-builder
...
@@ -652,24 +697,24 @@ vue add electron-builder
代码: src/backend/webserver/routes/index.js
代码: src/backend/webserver/routes/index.js
```
javascript
```
javascript
import express from
'express'
import express from
"express";
// 导入slice mgt的具体数据操作函数
// 导入slice mgt的具体数据操作函数
import sliceMgt from
'../entity/function.js'
import sliceMgt from
"../entity/function.js";
// 导入system config 的具体数据操作函数
// 导入system config 的具体数据操作函数
// let sysConfig = require("../entity/be_sysConfig.js");
// let sysConfig = require("../entity/be_sysConfig.js");
import sysConfig from
'../entity/be_sysConfig.js'
import sysConfig from
"../entity/be_sysConfig.js";
// 生成路由对象
// 生成路由对象
let router = express.Router()
let router = express.Router()
;
// 设置路由
// 设置路由
router.get(
'/nsmf/v1/nsts', sliceMgt.getNSTs)
router.get(
"/nsmf/v1/nsts", sliceMgt.getNSTs);
router.get(
'/nsmf/v1/plmns', sliceMgt.getPLMNs)
router.get(
"/nsmf/v1/plmns", sliceMgt.getPLMNs);
router.get(
'/nsmf/v1/nsmfConfig', sysConfig.getNSMFConfig)
router.get(
"/nsmf/v1/nsmfConfig", sysConfig.getNSMFConfig);
router.put(
'/nsmf/v1/nsmfConfig', sysConfig.setNSMFConfig)
router.put(
"/nsmf/v1/nsmfConfig", sysConfig.setNSMFConfig);
// ES6写法: 暴露路由
// ES6写法: 暴露路由
export { router as default }
export { router as default }
;
```
```
* sliceMgt 实体文件:
* sliceMgt 实体文件:
...
@@ -677,9 +722,9 @@ vue add electron-builder
...
@@ -677,9 +722,9 @@ vue add electron-builder
代码: src/backend/webserver/entity/function.js
代码: src/backend/webserver/entity/function.js
```
javascript
```
javascript
import db from
'@/backend/store/db.js'
import db from
"@/backend/store/db.js";
let sliceMgt = new Object()
let sliceMgt = new Object()
;
// wrap函数把不支持promise的库转为支持, 可以支持异步
// wrap函数把不支持promise的库转为支持, 可以支持异步
// const wrap = fn => (...args) => fn(...args).catch(args
[
2
]
);
// const wrap = fn => (...args) => fn(...args).catch(args
[
2
]
);
// router.get(
// router.get(
...
@@ -692,18 +737,18 @@ vue add electron-builder
...
@@ -692,18 +737,18 @@ vue add electron-builder
// );
// );
sliceMgt.getNSTs = (req, res) => {
sliceMgt.getNSTs = (req, res) => {
console.log('GET: NST List')
console.log("GET: NST List");
// 查询数据
// 查询数据
let data = db.get('NSTs').value()
let data = db.get("NSTs").value();
res.json(data)
res.json(data);
}
}
;
sliceMgt.getPLMNs = (req, res) => {
sliceMgt.getPLMNs = (req, res) => {
console.log('GET: PLMN List')
console.log("GET: PLMN List");
res.send('getPLMNs')
res.send("getPLMNs");
}
}
;
export { sliceMgt as default }
export { sliceMgt as default }
;
```
```
be_sysConfig.js 类似处理
be_sysConfig.js 类似处理
...
@@ -743,7 +788,15 @@ vue add electron-builder
...
@@ -743,7 +788,15 @@ vue add electron-builder
```
json
```
json
[
[
{ "label": "差动保护", "value": "nst_001", "id": "007b1880-f259-4993-b786-a5d93310b306" },
{
{ "label": "龙门吊", "value": "nst_002", "id": "1ff1f498-b308-4649-a42e-77e7293e42b6" }
"label": "差动保护",
"value": "nst_001",
"id": "007b1880-f259-4993-b786-a5d93310b306"
},
{
"label": "龙门吊",
"value": "nst_002",
"id": "1ff1f498-b308-4649-a42e-77e7293e42b6"
}
]
]
```
```
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment