UNPKG

albertgao.common-electron

Version:

个人常用的nodejs/electron模块

393 lines (385 loc) 14.3 kB
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