https://github.com/SimulatedGREG/electron-vue
https://simulatedgreg.gitbooks.io/electron-vue/content/cn/
主进程
- 可以看做是package.json中main属性对应得文件
- 一个应用只会有一个进行
- 只有主进程可以进行GUI的API操作(图例中Native apis)
渲染进程:
- windows中展示的界面通过渲染进程表现
- 一个应用可以有多个渲染进程
核心:启动主进程,创建窗口,加载指定界面,之后开启渲染进程,如果说渲染进程需要通信这时候利用ipc完成通信操作,之后通过主进程调用原生api,在进行跟操作系统进行互动从而完成功能的操作。
ipcRenderer
https://www.electronjs.org/zh/docs/latest/api/ipc-renderer
ipcRenderer.on(channel, listener)
// channel: string
// listener: function(event, ...args)
监听channel,当新消息到达,将通过listener(event, args...)调用listener
ipcRenderer.send(channel, ...args)
// channel: string
// ...args: any
通过channel向主进程发送异步信息,可以发送任意参数。
主进程中,通过ipcMain模块下的channel来处理这些消息
在vue中调用接口 - 相互通信
大概流程就是vue中不能直接向接口发送请求,必须通过ipcRenderer.send()来向主进程发送请求,然后再主进程中发送接口请求,把接口请求的返回参数通过event.sender.send()发送给.vue中,.vue中必须时刻监听‘event.sender.send()请求’,监听的方式是ipcRenderer.on()
在****.vue中
<template>
<div @click="handleLogin">接口请求</div>
</template>
<script>
import { ipcRenderer } from 'electron'
export default {
data () {
return {}
},
created () {
ipcRenderer.send('getaccount-login') // 加载的时候接口请求
},
mounted () {
// 监听主进程返回的数据
ipcRenderer.on('account-login', (event, response) => {
console.log(response)
})
},
methods: {
handleLogin () {
ipcRenderer.send('getaccount-login', this.ruleForm)
}
}
}
</script>
在index.js中
import { app, BrowserWindow, ipcMain, Menu, session, dialog } from 'electron'
import elog from 'electron-log'
import {getAccountLogin} from './api'
// 创建窗口
function createWindow () {
elog.info('app on ready')
/**
* Initial window options
*/
if (process.platform === 'darwin') {
const template = [
{
label: 'Application',
submenu: [
{ label: 'Quit',
accelerator: 'Command+Q',
click: () => { app.quit() }
}
]
},
{
label: 'Edit',
submenu: [
{ label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
{ label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' }
]
}
]
// mac下 设置快捷按键
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
} else {
Menu.setApplicationMenu(null)
}
// 设置窗口的参数
mainWindow = new BrowserWindow({
height: 623,
useContentSize: true,
width: 1000
})
mainWindow.loadURL(winURL)
mainWindow.on('closed', () => {
mainWindow = null
})
}
app.on('ready', createWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (mainWindow === null) {
createWindow()
}
})
ipcMain.on('getaccount-login', (event, form) => {
getAccountLogin(form).then(res => {
event.sender.send('account-login', res) // 将接口中的参数返沪
}).catch(e => {
console.log(e)
})
})
api.js
import myAxios from 'axios'
import Util from './util.js'
const service = myAxios.create({
timeout: 10000, // 请求超时时间
withCredentials: true
})
// // respone拦截器
// service.interceptors.response.use(
// response => {
// /**
// * code为非20000是抛错 可结合自己业务进行修改
// */
// const res = response.data
// if (res.code) {
// // 401:Token 过期了;
// const errRes = {code: res.code}
// return errRes
// } else {
// return response.data
// }
// },
// error => {
// let errorMsg = error.response
// // console.log('errorMsg', errorMsg)
// if (!errorMsg) {
// errorMsg = error.message
// }
// return Promise.reject(error)
// }
// )
export const getAccountLogin = (params) => {
return service({
headers: {'Content-Type': 'application/json'},
method: 'post',
url: `https://authentication.com/accsounts/accountLogin`,
data: params
})
}
filePath = ''
ipcRenderer.send('openFile-dialog', this.filePath)
// 打开文件选择框,oldPath上一次打开的位置
ipcMain.on('openFile-dialog', (event, oldPath) => {
if (oldPath === '') {
oldPath = app.getPath('downloads')
}
console.log('oldPath==', oldPath)
let newPath = dialog.showOpenDialog({
title: '保存的位置',
properties: ['openDirectory', 'createDirectory'],
defaultPath: oldPath
})
event.sender.send('file-name', newPath)
})
csv、txt
示例:
上传文档csv或者txt,并将文件中得内容解析
<template>
<el-button @click="jieix">解析csv内容</el-button>
<el-input v-model="form.filePath" :disabled="true" placeholder="请选择上传测试集.csv/.txt">
<el-button slot="append" icon="el-icon-document" @click="onOpenFile"> </el-button>
</el-input>
</template>
import { ipcRenderer } from 'electron'
<script>
export default {
data() {
return {
form: {
filePath: ''
}
}
},
mounted() {
ipcRenderer.on('selected-file', (event, path) => {
this.form.filePath = path ? path[0] : ''
})
},
method: {
// 上传测试集
onOpenFile () {
ipcRenderer.send('open-file')
},
jieix () {
ipcRenderer.send('detail-data', this.form)
}
}
}
</script>
上传csv文件
index.js中
import { ipcMain, dialog } from 'electron'
// 上传csv文件
ipcMain.on('open-file', (event, param) => {
let filters = [{name: 'CSV', extensions: ['csv', 'txt']}]
dialog.showOpenDialog({
properties: ['openFile'],
filters: filters
}, (path) => {
if (path) {
event.sender.send('selected-file', path)
}
})
})
将csv文件读取内容
iconv-lite:解决读取内容乱码
// 将csv文件解析内容
import iconv from 'iconv-lite'
ipcMain.on('detail-data', (event, form) => {
let detailData = fs.readFileSync(path.resolve(form.filePath)) // 读取文件内容
detailData = iconv.decode(detailData, 'GBK') // 处理乱码问题
// detailData = iconv.decode(detailData, 'UTF-8') // 解析txt时,用utf-8
let lineArr = detailData.split('\r\n') // 分割文件的每一行文本
console.log('lineArr=====', lineArr) // 解析到的内容
})
xlsx
上传xlsx文件
// 上传xlsx文件
ipcMain.on('open-file', (event, param) => {
let filters = [{ name: 'XLSX', extensions: ['xlsx'] }]
dialog.showOpenDialog({
properties: ['openFile'],
filters: filters
}, (path) => {
if (path) {
event.sender.send('selected-file', path)
}
})
})
xlsx文件内容读取
上传xlsx文件并读取xlsx文件内容
“node-xlsx”: “^0.15.0”,
“xlsx-style”: “^0.8.13”
nodeExcel.js中
const xlsx = require('node-xlsx').default
export const readExcel = (fileName) => {
return xlsx.parse(fileName)
}
index.js中
import {readExcel} from './nodeExcel.js'
ipcMain.on('xlsx-data', (event, form) => {
const xlsxJson = readExcel(form.filePath) // 读取xlsx内容
for (let i = 0; i < xlsxJson.length; i++) { // 注意这边将每个工作表中单元格为空的返回'',否则对应不上
for (let j = 0; j < xlsxJson[i].data.length; j++) {
for (let x = 0; x < xlsxJson[i].data[0].length; x++) { // 根据标题(第一行)得长度
if (xlsxJson[i].data[j][x] === undefined) {
xlsxJson[i].data[j][x] = ''
}
}
}
}
event.sender.send('getxslx-data', xlsxJson)
})
上传的xlsx
xlsxJson数据格式
下载xslx文件
简单无样式的表格 node-xlsx
import os from 'os'
const xlsx = require('node-xlsx').default
const fs = require('fs')
export const createExcelTest = (options, callback) => {
const worksheets = [
{
name: 'sheet1',
data: [['表头A', '表头B', '表头C', '表头D'], ['数据A', '数据B', '数据C', '数据D']]
},
{
name: 'sheet2',
data: [['表头A', '表头B', '表头C', '表头D'], ['数据A', '数据B', '数据C', '数据D']]
}
]
const buffer = xlsx.build(worksheets)
console.log('buffer=', buffer)
const folderExists = fs.existsSync(`${os.homedir()}/assess-tools`) // os 模块内置的应用编程接口,用于获取当前用户的主目录路径
console.log('folderExists=', folderExists)
if (!folderExists) {
fs.mkdirSync(`${os.homedir()}/assess-tools/`)
}
let fileName = `测试结果明细-${new Date().getTime()}.xlsx`
fs.writeFile(`${os.homedir()}/assess-tools/${fileName}`, buffer, function (err) {
if (err) { throw err }
console.log('Write to xlsx has finished')
const res = {msg: `测试报告已生成`, path: `${os.homedir()}/assess-tools/${fileName}`}
callback(res)
})
}
下载xslx文件 带有格式(一个工作表)xlsx-style
import os from 'os'
const xlsx = require('node-xlsx').default
const styleXlsx = require('xlsx-style')
const fs = require('fs')
export const createExcelTest = (options, callback) => {
let _headers = ['id', 'name', 'age', 'country', 'remark']
let _headersName = ['sid', 'sname', 'asge', 'csountry', 'rsemark']
let _data = [
{
id: 1,
name: 'dsd',
age: 22,
country: 'shagng',
remark: 'hello'
},
{
id: 12,
name: '232dsd',
age: 32,
country: 'shagng2323',
remark: 'hell2323o'
}
]
let headersExport = _headersName
.map((v, i) => Object.assign({}, { v: v, position: String.fromCharCode(65 + i) + 1 }))
.reduce((prev, next) => Object.assign({}, prev, {
[next.position]: {
v: next.v
}
}), {})
let dataExport = _data.map((v, i) => _headers.map((k, j) => Object.assign({}, { v: v[k], position: String.fromCharCode(65 + j) + (i + 2) })))
.reduce((prev, next) => prev.concat(next))
.reduce((prev, next) => Object.assign({}, prev, {
[next.position]: {
v: next.v,
s: {
alignment: {
horizontal: 'center',
vertical: 'center',
wrapText: true // 设置单元格换行
}
}
}
}), {})
console.log('dataExport==', dataExport)
// 合并 headersExport和dataExport
const output = Object.assign({}, headersExport, dataExport)
// 获取所有单元格的位置
const outputPos = Object.keys(output)
// 计算出范围
const ref = outputPos[0] + ':' + outputPos[outputPos.length - 1]
// 构建workbook对象
const wb = {
SheetNames: ['Sheet'],
Sheets: {
'Sheet': Object.assign({}, output, { '!ref': ref }, {'!cols': [ { wch: 20 }, { wpx: 150 }, { wch: 20 }, { wch: 15 }, { wch: 15 } ]})
}
}
const folderExists = fs.existsSync(`${os.homedir()}/assess-tools`)
if (!folderExists) {
fs.mkdirSync(`${os.homedir()}/assess-tools/`)
}
let fileName = `测试结果明细-${new Date().getTime()}.xlsx`
const filePath = `${os.homedir()}/assess-tools/${fileName}`
styleXlsx.writeFile(wb, filePath)
console.log('Write to xlsx has finished')
const res = {msg: `校验报告已生成`, path: filePath}
callback(res)
}
下载xslx文件 多张工作表表,位置数据
import os from 'os'
const xlsx = require('node-xlsx').default
const styleXlsx = require('xlsx-style')
const fs = require('fs')
export const createExcelTest = (options, callback) => {
options.dataInfo = [
{
name: '单轮测试集',
data: [
['测试用例', '技能', '任务', '意图', '语义', 'command', 'nlg', '错误提示', '实际结果'],
['打开主驾车窗', '1', '', '', '', '', '', '技能错误,预期返回结果:无语义错误,预期返回结果:无,实际返回结果:有', '{"contextId":"24ca0af766a931","d…"skillId":"2022101700000012","topic":"dm.output"}'],
['打开主驾车窗', '', '2', '3', '', '', '', '技能错误,预期返回结果:预期返回结果:无,实际返回结果:有', '{"contextId":"2022112","topic":"dm.output"}'],
['打开主驾车窗', '', '', 'd', 'd', '', '', '技能错误,预期返回结果:…,预期返回结果:无,实际返回结果:有', '12","topic":"dm.output"}'],
['打开主驾车窗', '', '', '', '', '', '', '技能错误,预期返回结果有', '{"contextId":"24ca0af760b']
]
},
{
name: '单轮车控测试集ssdsd',
data: [
['测试用sds例', '技能', '任务', '意图', '语义', 'command', 'nlg', '错误提示', '实际结果'],
['sd', '1', '', '', '', '', '', '技能错误,预期返回结果:无,实际返回结果义错误,预期返回结果:无,实际返回结果:有', '{"contextId":"24c8931","d…"skillId":"2022112","topic":"dm.output"}']
]
}
]
const wb = {
SheetNames: [],
Sheets: {}
}
let _header = [] // 每个sheet表中表格标题,第一行(二维数组)
let _data = [] // 每个sheet表中表格内容,第二行开始的内容(三维数组)
options.dataInfo.forEach((item, index) => {
wb.SheetNames.push(item.name)
_header.push(item.data[0])
let data = item.data.slice(1, item.data.length)
_data.push(data)
})
let headersExport = _header.map((item, index) => {
let obj = item.map((v, i) => Object.assign({}, { v: v, position: String.fromCharCode(65 + i) + 1 }))
.reduce((prev, next) => Object.assign({}, prev, {
[next.position]: {
v: next.v
}
}), {})
return obj
})
let dataExport = []
_data.forEach(sitem => {
let dataExportArr = sitem.map((item, index) => {
let obj = item.map((v, i) => Object.assign({}, {v: v, position: String.fromCharCode(65 + i) + (index + 2)}))
.reduce((prev, next) => Object.assign({}, prev, {
[next.position]: {
v: next.v,
s: {
alignment: {
horizontal: 'center',
vertical: 'center',
wrapText: true // 设置单元格换行
}
}
}
}), {})
return obj
})
dataExport.push(dataExportArr)
})
let output = []
for (let i = 0; i < headersExport.length; i++) {
let obj = {}
dataExport[i].forEach(item => {
obj = {...obj, ...item}
})
let outputInfo = {
...headersExport[i],
...obj
}
output.push(outputInfo)
}
let outputPos = output.map(item => {
return Object.keys(item)
})
let ref = []
outputPos.forEach(item => {
console.log(item)
let refText = item[0] + ':' + item[item.length - 1]
ref.push(refText)
})
wb.SheetNames.forEach((item, index) => {
// wb.Sheets[item] = Object.assign({}, output[index], { '!ref': ref[index] }, {'!cols': [ { wch: 20 }, { wpx: 150 }, { wch: 20 }, { wch: 15 }, { wch: 15 } ]})
wb.Sheets[item] = Object.assign({}, output[index], { '!ref': ref[index] })
})
console.log('wb=====', wb)
console.log(wb.Sheets['单轮测试集'])
console.log(wb.Sheets['单轮测试集'])
const folderExists = fs.existsSync(`${os.homedir()}/assess-tools`)
if (!folderExists) {
fs.mkdirSync(`${os.homedir()}/assess-tools/`)
}
let fileName = `测试结果-${new Date().getTime()}.xlsx`
const filePath = `${os.homedir()}/assess-tools/${fileName}`
styleXlsx.writeFile(wb, filePath)
console.log('Write to xlsx has finished')
const res = {msg: `校验报告已生成`, path: filePath}
callback(res)
}
创建csv文件,并异步一条条写入数据
方法一 fs.createReadStream readline.createInterface
注:csv可以创建文件后,一条条写入数据,xlsx不支持此功能
fs :
1、返回一个readStream(文件读取流,输入流)对象。(可读流)
fs.createReadStream(path, [options])
由于该方法属于fs模块,使用前需要引入fs模块(var fs= require(“fs”) )
2、fs.existsSync以同步的方法检测目录是否存在。如果目录存在 返回 true ,如果目录不存在 返回false
readline :
通过这个模块我们可以以逐行的方式读取数据流
readline.createInterface({input: fRead}) 创建readline接口实例
import fs from 'fs'
import readline from 'readline'
format () {
const fRead = fs.createReadStream(form.filePath) // 创建可读流,并读取,注意这边form.filePath不为空如果为空则会报错
const objReadline = readline.createInterface({input: fRead})
objReadline.on('close', function () {
const filePath = `${os.homedir()}/assess-tools`
const folderExists = fs.existsSync(`${filePath}`)
if (!folderExists) {
fs.mkdirSync(`${filePath}/`)
}
let fileName = `assess-${new Date().getTime()}.csv` // 注:最好加上日期不然第二次下载的时候,会报错(第一个文件跟第二个文件同名)
let createFileName = `${os.homedir()}/as/${fileName}`
let data = ['name', 'age', 'sex']
fs.appendFileSync(createFileName, `\uFEFF${data .join(',')}\n`, {
encoding: 'utf8',
mode: 0o666,
flag: 'a'
}) // 有多少数据写多少数据,可以将此段提取出来,跟接口返回操作,通过websocket连接,得到一条数据,写入csv文件中一条
getTestDm(createFileName, res => {
// 判断是否是最后一条数据
console.log(res)
})
}
}
function getTestDm (createFileName, callback) {
let datas = ['lily', '22', 'girl']
fs.appendFileSync(createFilePath[count], `\uFEFF${datas.join(',')}\n`)
// 当一切操作完可以返回一些内容
let res = 'done'
callback(res)
}
对于上述fs.createReadStream(form.filePath)使用局限性问题,这边示例则采用之间创建csv文件并异步存入数据
方法二 fs.writeFile
import os from 'os'
import fs from 'fs'
function createCsvFile () {
const filePath = `${os.homedir()}/assess-tools`
const folderExists = fs.existsSync(`${filePath}`)
if (!folderExists) {
fs.mkdirSync(`${filePath}/`)
}
let fileName = `测试-${new Date().getTime()}.csv`
let createFilePath = `${os.homedir()}/assess-tools/${fileName}`
let title = ['姓名', '年龄']
fs.writeFile(createFilePath, `\uFEFF${title.join(',')}\n`, function (err) {
if (!err) {
// 可以写个函数放上数据然后
// let data = ['哈哈哈', '20']
// fs.appendFileSync(createFilePath[count], `\uFEFF${datas.join(',')}\n`)
}
})
}
`姓名,年龄\n哈哈哈,20\n嘻嘻嘻,89’
生成csv为
姓名 年龄
哈哈哈 20
嘻嘻嘻 89
csv 格式编辑
单元格支持英文逗号
注:
1、英文状态下逗号,csv中会当成另外一列
2、\n在csv中规则换行
3、csv中单元格换个加双引号并且换行的内容处加\n 如dataArr[7] = ""操作=调节\n对象=车窗 \n对象_raw=车窗
3、如果let val = {“a”: “bb”, “c”: “dd”} let strs = JSON.stringify(val).replace(/“/g, ""
) strs = "${strs}"
将原本的"替换为”",然后进行’“a,b”'类似这种写法