最近工作需要写一个数据录入的小工具,利用vue2+electron+sqlite的方式来实现。因为浏览器有安全权限问题是不能直接读取sqlite的,所以我们借助electron中 node的能力。他具有操作系统相关权限的接口。so…..
vue项目创建就忽略了。。。
环境:
node v18.20.2
npm 10.5.0
yarn 1.22.22
vue @vue/cli 5.0.8
如果node环境问题可以使用
nvm来进行node的管理,非常方便
nvm install 16(node版本)
nvm use 16 (切换到16这个版本)
1、按sqlite
npm i -S sqlite3
2、安装electron
npm i -D electron@31.0.2 //-D 表示开发依赖 生产不依赖
3、安装electron forge 打包工具,也可用其他方式(electron-builder) 我这里使用这个。
npm install --save-dev @electron-forge/cli
npx electron-forge import //npx 是执行包 npm是安装包管理
上面依赖安装完成后,根目录会出现forget.config.js 文件、main.js、以及package.json文件也会有相应的改动。
forget.config.js配置细则:
const { FusesPlugin } = require('@electron-forge/plugin-fuses');
const { FuseV1Options, FuseVersion } = require('@electron/fuses');
const platform = process.platform;
module.exports = {
packagerConfig: {
///额外资源目录
extraResource: [
'./src/assets',
'./src/components',
],
// 是否覆盖已经生成的文件
overwrite: true,
// 应用名称
name: "一体机字段处理工具app",
executableName: "all-in-one",
// 打包平台,macOS 用 'darwin'
platform: platform,//"darwin", //默认不设置是系统环境
// 架构,对于大多数 macOS 应用来说是 'x64' arch
//arch: "x64", //默认不设置是系统环境
// 应用程序的图标
icon: "app",//platform == 'darwin' ? "app.icns" : "app.ico",自动寻找
// 应用的版权说明
appCopyright: 'Copyright © 2024 lion.com版权所有',
// 应用程序的版本
appVersion: "1.0.0",
// macOS 的 bundle ID
appBundleId: "com.electron.www.test",
// macOS 的帮助文件 bundle ID
helperBundleId: "com.electron.www.test.helper",
// 打包输出目录
out: "./out",
// 是否打包为 ASAR 归档文件
asar: true,
win32metadata: {
"ProductName": "五笔助手 Windows",
"CompanyName": "kylebing.cn",
"FileDescription": "五笔助手 for 小狼毫"
}
},
rebuildConfig: {},
makers: [
{
name: '@electron-forge/maker-squirrel',
config: {
"name": "一体机字段处理工具win",
},
},
{
name: '@electron-forge/maker-zip',
platforms: ['darwin'],
},
{
name: '@electron-forge/maker-dmg',
config: {
format: 'ULFO'
}
},
// {
// name: '@electron-forge/maker-pkg',
// config: {
// // PKG 特定配置 Developer ID Installer: Your Name (Team ID) 打包指令:npx electron-forge make --target pkg
// identity: 'com.electron.www.test',
// format: 'ULFO'
// }
// },
{
name: '@electron-forge/maker-deb',
config: {},
},
{
name: '@electron-forge/maker-rpm',
config: {},
},
],
plugins: [
{
name: '@electron-forge/plugin-auto-unpack-natives',
config: {},
},
// Fuses are used to enable/disable various Electron functionality
// at package time, before code signing the application
new FusesPlugin({
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false,
[FuseV1Options.EnableCookieEncryption]: true,
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
[FuseV1Options.EnableNodeCliInspectArguments]: false,
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
[FuseV1Options.OnlyLoadAppFromAsar]: true,
}),
],
};
package.json文件细则
{
"name": "all-in-one",
"description": "An electron-vue project",
"productName": "一体机字段处理工具",
"version": "0.1.0",
"main": "main.js",
"author": "Lion",
"license": "MIT",
"private": true,
"scripts": {
"serve": "export NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
"build": "vue-cli-service build",
"electron": "export NODE_OPTIONS=--openssl-legacy-provider && electron .",
"start": "electron-forge start",
"package": "electron-forge package",
"make": "electron-forge make",
"make2": "electron-forge make --arch x64 --platform win32"
},
"dependencies": {
"axios": "^1.7.2",
"core-js": "^2.6.5",
"electron-icns": "^3.6.3",
"electron-squirrel-startup": "^1.0.1",
"element-ui": "^2.15.14",
"less": "^4.2.0",
"less-loader": "^7.3.0",
"locate-path": "^7.2.0",
"path-exists": "^5.0.0",
"sqlite3": "^5.1.7",
"vue": "^2.6.10"
},
"devDependencies": {
"@electron-forge/cli": "^7.4.0",
"@electron-forge/maker-deb": "^7.4.0",
"@electron-forge/maker-dmg": "^7.4.0",
"@electron-forge/maker-pkg": "^7.4.0",
"@electron-forge/maker-rpm": "^7.4.0",
"@electron-forge/maker-squirrel": "^7.4.0",
"@electron-forge/maker-zip": "^7.4.0",
"@electron-forge/plugin-auto-unpack-natives": "^7.4.0",
"@electron-forge/plugin-fuses": "^7.4.0",
"@electron/fuses": "^1.8.0",
"@vue/cli-plugin-babel": "^3.8.0",
"@vue/cli-service": "^3.8.0",
"electron": "^31.0.2",
"vue-template-compiler": "^2.6.10"
}
}
main.js 文件
const { app, BrowserWindow,ipcMain} = require('electron')
const { db ,dbClose } = require('./db.js');
const { ipcMainBind } = require('./ipcMainBind.js');
//import('./test.js');
const path = require('path');
const fs = require('fs');
// 获取 userData 目录的路径
const userDataPath = app.getPath('userData');
// 定义要保存的数据文件的名称
const dataFileName = 'userPreferences.json';
// 构建完整的文件路径
const dataFilePath = path.join(userDataPath, dataFileName);
const createWindow = () => {
const win = new BrowserWindow({
width: 1200,
height: 800,
//autoHideMenuBar: true,
webPreferences: {
preload: path.join(__dirname, './preload.js'), // 指定预加载脚本路径
nodeIntegration: false, // 禁用 Node.js 和预加载脚本互斥
contextIsolation: true,//启用上下文隔离
},
//icon: path.join(__dirname, './app.ico'), // 图片
icon: 'app.png', // 指定窗口图片
})
// 全屏
//win.maximize()
win.setMenu(null);
//win.loadFile('index.html') 这里最后 vue 打包后 改为本地文件这样更流畅 这里我就用了localhost连接地址
win.loadURL('http://localhost:8081')
}
app.whenReady().then(() => {
createWindow()
ipcMainBind();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0){
createWindow()
}
})
//生成文件 打印日志
//log();
})
app.on('window-all-closed', () => {
//dbClose();
if (process.platform !== 'darwin') app.quit()
})
///写入日志
function log(){
//写入日志示例数据
const userData = {
preferences: {
theme: 'dark',
language: 'zh-CN'
}
};
//判断文件是否存在
userDataPath && fs.existsSync(userDataPath) || fs.mkdirSync(userDataPath);
// 将数据保存到文件
fs.writeFile(dataFilePath, JSON.stringify(userData), (err) => {
if (err) throw err;
console.log('用户数据已保存到', dataFilePath);
});
}
完成后。查看package.json中,先启动 vue网站
npm run serve
再运行electron 程序可以本地打开一个桌面的调试程序
npm run start
//或者 npx 包执行命令来执行也可行
npx electron-forge start
ok这时候不出意外哈喽我的 应该出现了。。。
再来看看sqlite的代码部分。首先vue中是不能操作sqlite的。。所以我们用到预加载的功能,使用进程通信才能实现。因为我们vue 是渲染进程,electron中sqlite是在主进程中。。
首先新建一个 preload.js,代码有注释 不描述了。。
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
//暴漏了一个electronAPI 给渲染进程调用
contextBridge.exposeInMainWorld('electronAPI', {
///异步返回 在vue中使用的代码
// console.log('window.electron='+window.electronAPI);
// window.electronAPI.send("insert","wo lai le");
// window.electronAPI.receive("insert", (msg) => {
// console.log(`Received ${msg} from main process`);
// });
send: (channel, data) => {
console.log('preload.js send:'+channel+"=="+data);
ipcRenderer.send(channel, data);
},
//返回结果
receive: (channel, func) => {
//渲染进程
console.log('preload.js receive:'+channel+"=="+func);
//判断是否已经监听
if (ipcRenderer.listenerCount(channel) > 0) {
return;
}
ipcRenderer.on(channel, (event, ...args) => func(...args));
// ipcRenderer.on(channel,function(event,...args){
// console.log('preload.js receive:'+channel+"=="+a);
// });
},
//移除监听
removeListener: (channel) => {
ipcRenderer.removeListener(channel);
},
//移除全部监听
removeAllListeners: () => {
ipcRenderer.removeAllListeners();
},
//异步返回结果 和上面相反
//异步返回 在vue中使用的代码
// query2(){
// window.electronAPI.sendSynchronization('query2', 'bai').then((data) => {
// console.log('data='+data);
// console.log('data='+JSON.stringify(data));
// });
// }
sendSynchronization : async (channel,key) =>{
try {
var result = await ipcRenderer.invoke(channel, key);
console.log('Received result:', result);
} catch (error) {
console.error('Error requesting data from main process:', error);
}
return result;
}
});
还要注册一个频道监听icpmain.js
const {ipcMain} = require('electron')
const { db,query,insertAsync,update } = require('./db.js');
exports.ipcMainBind = function(){
ipcMain.on('insert', async (event, arg) => {
console.log('insert='+arg);
//insert( arg );
let res = await insertAsync( arg );
//event.returnValue = insert(arg);
//event.reply('insert', 'insert success');
event.sender.send('insert', res);
});
ipcMain.on('query', async (event, arg) => {
console.log('query='+arg);
let res = await query( arg );
//event.reply('query', 'query success');
event.sender.send('query', res );
// event.returnValue = 'query success';
});
///要在ready 之后 才能使用
//同步等待
ipcMain.handle('query2', async (event, ...args) => {
let res = await query( args[0] );
return res; // 将结果返回给渲染进程
});
ipcMain.on('update', async (event, arg) => {
console.log('update='+arg);
let res = await update( arg );
//event.reply('update', 'update success');
event.sender.send('update', res );
// event.returnValue = 'update success';
});
}
里面使用db的调用函数,db是我自定义的数据库操作函数。。db.js如下
///数据库初始化
let DatabaseInstance = (function() {
let instance;
function createInstance() {
//// 引入 sqlite3 并设置为 verbose 模式,以便能够看到更详细的日志
const sqlite3 = require('sqlite3').verbose();
let db = new sqlite3.Database('/Users/baijinhao/www/testvUE/DD/src/assets/yitiji.db', (err) => {
if (err) {
console.error('Error opening database', err);
} else {
console.log('Database opened successfully');
// 在这里,您可以执行数据库初始化操作,例如创建表
db.run("CREATE TABLE IF NOT EXISTS contacts (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, email TEXT)");
}
});
return db;
}
return {
getInstance: function() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
exports.db = DatabaseInstance.getInstance();
exports.insertAsync = async function (arg) {
await exports.db.run('INSERT INTO data (value,key) VALUES (?,?)',arg.value,arg.key);
// console.log('insert success');
return 'insert success';
}
exports.query = async function (arg){
return new Promise((resolve, reject) => {
exports.db.all("SELECT * FROM data where key='"+arg+"'", [], (err, rows) => {
if (err) {
console.error('Error running query', err);
reject(err);
} else {
//console.log('query success');
//console.log('query success'+JSON.stringify(rows));
resolve(rows);
}
});
});
}
exports.update = async function (arg){
return new Promise((resolve, reject) => {
exports.db.all("update data set value = ? where key = ? ", [arg.value,arg.key], (err, rows) => {
if (err) {
// console.error('Error running update', err);
reject(err);
} else {
// console.log('update success');
resolve("update success");
}
});
});
}
exports.dbClose = function (){
exports.db.close((err) => {
if (err) {
console.error('Error closing database', err);
} else {
console.log('Database closed successfully');
}
});
}
vue 界面的调用
created() {
//这是监听后的结果
window.electronAPI.receive("update", (msg) => {
this.$message({
message: '更新信息成功',
type: 'success'
});
});
window.electronAPI.receive("insert", (msg) => {
this.$message({
message: '插入信息成功',
type: 'success'
});
});
window.electronAPI.receive("query", (data) => {
console.log(JSON.stringify(data));
console.log(data.length)
if (data && data.length > 0) {
this.data = JSON.parse(data[0]['value']);
} else {
this.insert();
}
});
this.query();
},
},
methods:{
async insert() {
//调用主进程的暴漏的函数
window.electronAPI.send('insert', { value: JSON.stringify(this.data), key: this.table });
},
async query() {
//调用主进程的暴漏的函数
window.electronAPI.send('query', this.table);
},
async update() {
//调用主进程的暴漏的函数
console.log(this.data);
window.electronAPI.send('update', { value: JSON.stringify(this.data), key: this.table });
},
query2(){
//这是另外的方式 不需要注册receive函数同步等待
window.electronAPI.sendSynchronization('query2', 'bai').then((data) => {
console.log('data='+data);
console.log('data='+JSON.stringify(data));
});
}
}
OK这里sqlite的操作结束完毕。
最后就是打包生成,dmg,app,exe 操作。在上面forge.config.js中已经配置完成。所以我们最后需要执行
npm run make //就可以之心打包操作
我配置的打包地址是根目录下的out文件,所以打包完成后文件会保存在这里。

最后看看我的效果吧。


本源码已经放置地址:https://gitee.com/codeceo_net/vue2-electron-sqlite.git(release分支)
安装依赖:npm install
有兴趣可以下载来玩玩。。。。