albertgao.common-electron
Version:
个人常用的nodejs/electron模块
393 lines (385 loc) • 14.3 kB
JavaScript
const exportObject = {
...require('./utils/express-wss'),
...require('./utils/run-script'),
...require('./common'),
/**
* 向页面注入script标签
*
* @param {object | string}param0 可以直接为Js代码片段
* @param {string}param0.script 要注入的Javascript内容
* @param {string}param0.src 要注入的src
* @param {string}param0.filePath 要注入的js文件地址
*/
injectScript: function (param0) {
const Fs = require('fs-extra')
const scriptElement = document.createElement('script')
scriptElement.type = 'text/javascript'
if (param0 instanceof String) {
scriptElement.appendChild(document.createTextNode(param0))
} else {
const { script, src, filePath } = param0
if (script) {
scriptElement.appendChild(document.createTextNode(script))
} else if (src) {
scriptElement.src = src
} else if (filePath && Fs.pathExistsSync(filePath)) {
const js = Fs.readFileSync(filePath, { encoding: 'utf-8' })
scriptElement.appendChild(document.createTextNode(js))
}
}
document.body.appendChild(scriptElement)
},
/**
* 向页面注入style
*
* @param {object} param0 选项
* @param {string} param0.css 要注入的css内容
* @param {string} param0.href 要注入的hef
* @param {string} param0.filePath 要注入的css文件地址
*/
injectCss: function ({ css, href, filePath }) {
const Fs = require('fs-extra')
const head = document.getElementsByTagName('head')[0]
if (href) {
const cssElement = document.createElement('link')
cssElement.setAttribute('rel', 'stylesheet')
cssElement.setAttribute('media', 'all')
cssElement.setAttribute('type', 'text/css')
cssElement.setAttribute('href', href)
head.appendChild(cssElement)
} else {
const styleElement = document.createElement('style')
const content = css || Fs.readFileSync(filePath, { encoding: 'utf-8' })
styleElement.appendChild(document.createTextNode(content))
head.appendChild(styleElement)
}
},
/**
* 获得相对于工程或者exe文件路径的地址,该地址会自动创建
*
* @param {string | string[]} relativePath 相对于 工程 或者 exe所在文件夹 的位置
* @param {string} [mode=Dir] - "Dir"(默认) - 这是个文件夹 ; "File"-这是个文件;
* @returns {string} 如果已打包,则在exe所在位置/data目录下。如果未打包,则在工程的/data目录。
*/
dir: function (relativePath = 'data', mode = 'Dir') {
const Path = require('path')
const Fs = require('fs-extra')
// 需要@electron/remote模块
// 需要在background.js中运行以下内容
// const remoteMain = require('@electron/remote/main')
// remoteMain.initialize()
// remoteMain.enable(webContents)
const cb = (app) => {
try {
let dirPath = app.isPackaged
? Path.normalize(Path.dirname(app.getPath('exe'))) // 如果已打包返回exe的目录
: process.cwd() // 否则是Nodejs项目的根目录
dirPath =
Object.prototype.toString.call(relativePath) === '[object String]'
? Path.join(dirPath, relativePath)
: Path.join(dirPath, ...relativePath)
if (/dir/i.test(mode)) {
Fs.ensureDirSync(dirPath)
} else {
Fs.ensureFileSync(dirPath)
}
return dirPath
} catch (error) {
let dirPath = app.isPackaged
? Path.join(process.cwd(), '../../') // 退到asar以外
: process.cwd()
dirPath =
Object.prototype.toString.call(relativePath) === '[object String]'
? Path.join(dirPath, relativePath)
: Path.join(dirPath, ...relativePath)
if (/dir/i.test(mode)) {
Fs.ensureDirSync(dirPath)
} else {
Fs.ensureFileSync(dirPath)
}
return dirPath
}
}
try {
const { app } = require('@electron/remote')
return cb(app)
} catch (error) {
try {
const { app } = require('electron')
return cb(app)
} catch (error) {
// const exeDir = Path.join(
// __dirname
// // '../' /* node_modules */,
// // '../' /* app.asar.unpacked */,
// // '../' /* resources */,
// // '../' /* win-unpacked */
// )
const exeDir = process.cwd()
return Object.prototype.toString.call(relativePath) ===
'[object String]'
? Path.normalize(Path.resolve(exeDir, relativePath)) // 如果是string
: Path.normalize(Path.resolve(exeDir, ...relativePath)) // 如果是[]
}
}
},
/**
*
* @param {string}[mode='save'] 或者Open
* @see {@link https://www.electronjs.org/docs/latest/api/dialog#dialogshowopendialogbrowserwindow-options | 打开文件 / 文件夹}
* @see {@link https://www.electronjs.org/docs/latest/api/dialog#dialogshowsavedialogbrowserwindow-options | 保存文件}
* @param {object}options 选项
* @returns {Promise<object>} .canceled Boolean - whether or not the dialog was canceled.
* .filePaths String[] 只有在open模式下返回数组,如果没有选择任何文件,返回空数组
* .filePath String[] 只有在save模式下返回数组
*/
dialogF: function (mode = 'save', options) {
const { dialog } = require('@electron/remote')
if (/open/i.test(mode)) {
return dialog.showOpenDialog(options)
} else {
return dialog.showSaveDialog(options)
}
},
/**
*
* @param {string}[mode='save'] 或者Open
* @see {@link https://www.electronjs.org/docs/latest/api/dialog#dialogshowopendialogsyncbrowserwindow-options | 打开文件 / 文件夹}
* @see {@link https://www.electronjs.org/docs/latest/api/dialog#dialogshowsavedialogsyncbrowserwindow-options | 保存文件}
* @param {object}options 选项
* @returns {string[] | string | undefined} 如果是是Open模式,返回String[];。如果Save模式,返回String; 如果点击了取消,则
*/
dialogFSync: function (mode = 'save', options) {
const { dialog } = require('@electron/remote')
if (/open/i.test(mode)) {
return dialog.showOpenDialogSync(options)
} else {
return dialog.showSaveDialogSync(options)
}
},
/**
* @function 执行一个command并返回输出内容
* @param {string}[command="pip list"] 要运行的command
* @returns {Promise<string>} 输出的内容
*/
exec(command = 'pip list') {
return new Promise((resolve, reject) => {
const { exec } = require('child_process')
// eslint-disable-next-line node/handle-callback-err
const child = exec(command, (err, stdout) => {
resolve(stdout)
})
child.on('error', (err) => reject(err))
})
},
/**
* 对一个对图片进行ocr识别,需要Python 3.x和easyocr (pip包)
*
* @param {object}root0 选项
* @param {string|HTMLElement}root0.img 文件的路径,或者DOM元素
* @param {string}[root0.atDir="/node_modules/abertgao.common-electron/data/ocr"] 在哪个文件夹运行,如果打包electron asar需要特别注意修改,asar为只读文件
* @returns {Promise<string>} 返回图片识别结果
*/
async easyOcr({ img, atDir = require('path').join(__dirname, 'data/ocr') }) {
let tempImgPath = `${new Date().getTime()}-${~~(Math.random() * 100000)}`
const Fs = require('fs-extra')
const Path = require('path')
const { isHTMLElement, getImgBase64, runScript, exec } = exportObject
const pythonVersion = await exec('python -V')
if (!/Python\s+3\./.test(pythonVersion)) {
throw new Error('Python 3.x not installed')
}
const pipList = await exec('pip list')
if (!/easyocr\s+\d/.test(pipList)) {
throw new Error('easyocr not installed')
}
// eslint-disable-next-line no-unused-expressions
;('------------把文件临时拷过来-----------')
if (Object.prototype.toString.call(img) === '[object String]') {
// 如果是路径
if (Fs.pathExistsSync(img)) {
const ext = Path.extname(img)
tempImgPath = Path.join(atDir, `${tempImgPath}${ext}`) // ext 不用加.
Fs.copySync(img, tempImgPath, { overwrite: true })
} else {
throw new Error("File path does't exist:" + img)
}
} else if (isHTMLElement(img)) {
// 如果是HtmlElement
const { ext, data } = getImgBase64(img)
tempImgPath = Path.join(atDir, `${tempImgPath}.${ext}`)
Fs.writeFileSync(tempImgPath, data, { encoding: 'base64' })
}
// eslint-disable-next-line no-unused-expressions
;('------------对文件进行ocr解析-----------')
await runScript({
filePath: Path.join(__dirname, 'python/ocr.py'),
msgTo: tempImgPath,
atDir
})
Fs.removeSync(tempImgPath)
const txtPath = tempImgPath + '.txt'
if (Fs.pathExistsSync(txtPath)) {
const result = Fs.readJSONSync(txtPath)
Fs.removeSync(txtPath)
return result
} else {
throw new Error('No result')
}
},
/**
* 对一个对图片进行ocr识别,使用tesseract.js
*
* @param {object}root0 选项
* @param {string|HTMLElement}root0.img 文件的路径,或者DOM元素
* @param {string}[root0.atDir="/node_modules/abertgao.common-electron/data/ocr"] 在哪个文件夹运行,如果打包electron asar需要特别注意修改,asar为只读文件
* @returns {Promise<string>} 返回图片识别结果
*/
async ocr({ img, atDir = require('path').join(__dirname, '/data/ocr') }) {
// const tempImgPath = `${new Date().getTime()}-${~~(Math.random() * 100000)}`
const Fs = require('fs-extra')
const Path = require('path')
const { isHTMLElement, getImgBase64, runScript } = exportObject
let newImg
// eslint-disable-next-line no-unused-expressions
;('------------把文件临时拷过来-----------')
if (Object.prototype.toString.call(img) === '[object String]') {
// 如果是路径
if (Fs.pathExistsSync(img)) {
newImg = img
} else {
throw new Error("File path does't exist:" + img)
}
} else if (isHTMLElement(img)) {
// 如果是HtmlElement
newImg = getImgBase64(img).base64
}
// eslint-disable-next-line no-unused-expressions
;('------------对文件进行ocr解析-----------')
return await runScript({
filePath: Path.join(__dirname, 'scripts/tesseract.js'),
msgTo: { img: newImg },
atDir
})
},
/**
* 把Csv格式专为Object[]
*
* @param {string | Array[]} csv 从csv文件读取的数组
* @param {number} [titleStart=0] 哪个index是title(从0开始)
* @param {number} [dataStart=titleStart + 1] 哪个(含)index之后是数据
* @param {string | RegExp} [splitter=","] csv文件的每行中各值之间的分割符
* @returns {object[]} 转换结果
*/
csvToObjArr(
csv = [],
titleStart = 0,
dataStart = titleStart + 1,
splitter = ','
) {
if (/String/i.test(Object.prototype.toString.call(csv))) {
// 如果是string,先转换成Array
csv = [...csv.split(/\n\r*/g)].map((row) => [...row.split(splitter)])
}
const titles = csv[titleStart]
const spliced = csv.slice(dataStart)
return spliced.map((row) => {
const obj = {}
for (const i in row) {
obj[titles[i]] = row[i]
}
return obj
})
},
/**
* 把Object[]转换回Csv格式的数组
*
* @param {object[]} rows 传入一个object[],至少要有一行数据
* @param {number}[titlesFrom=0] 从哪个index读取titles
* @param {Function}[parser = (value, key, i) => value || ''] 传入(值 ,键, 行index),返回该位置的值
* @returns {Array[]}返回csv可用的数组,首行为keys
*/
objArrToCsv(
rows = [],
titlesFrom = 0,
parser = (value, key, i) => value || ''
) {
if (rows[titlesFrom]) {
const titles = [...Object.keys(rows[titlesFrom])]
const rowsEnsure = [...rows] // 确保rows是数组,防止.map不可用
return [
titles, // 第一行先输出titles
...rowsEnsure.map((row, i) => {
return titles.map((key) => parser(row[key], key, i)) // 每行按titles顺序输出数组
})
]
} else {
return []
}
},
/**
* 强制关闭Excel应用
*/
killExcel() {
const childProcess = require('child_process')
childProcess.exec('taskkill /f /im et.exe')
childProcess.exec('taskkill /f /im excel.exe')
},
/**
* 自动将数据写入excel
*
* @param {string} path xlsx文件的路径
* @param {object[] | Array[String[]]} data *
* @param {number}[titlesFrom=0] 从哪个index读取titles
* @param {Function}[parser = (value, key, i) => value || ''] 传入(值 ,键, 行index),返回该位置的值
*/
writeABook(
path,
data,
titlesFrom = 0,
parser = (value, key, i) => value || ''
) {
const { objArrToCsv } = exportObject
const { Workbook } = require('xlsx-rw')
const Fs = require('fs-extra')
try {
Fs.removeSync(path)
} finally {
//
}
Fs.ensureFileSync(path)
const wb1 = new Workbook(path)
if (
data[0] &&
/object\s+(object)/i.test(Object.prototype.toString.call(data[0]))
) {
wb1.writeData(wb1.SheetNames[0], objArrToCsv(data, titlesFrom, parser))
} else {
wb1.writeData(wb1.SheetNames[0], data)
}
wb1.save(path)
},
readJSONSync(fullName, defaultTo = null) {
const Fs = require('fs-extra')
try {
return JSON.parse(Fs.readFileSync(fullName, { encoding: 'utf-8' }))
} catch (error) {
return defaultTo
}
},
getFileSize(path, digits = 1) {
const Fs = require('fs-extra')
let { size } = Fs.statSync(path)
if (size > 1024 ** 3) {
size = `${~~((size * 10 ** digits) / 1024 ** 3) / 10 ** digits}GB`
} else if (size > 1024 ** 2) {
size = `${~~((size * 10 ** digits) / 1024 ** 2) / 10 ** digits}MB`
} else if (size > 1024) {
size = `${~~((size * 10 ** digits) / 1024) / 10 ** digits}KB`
} else {
size = `${size}b`
}
return size
}
}
module.exports = exportObject