UNPKG

albertgao.common-electron

Version:

个人常用的nodejs/electron模块

539 lines (531 loc) 17.7 kB
const exportObject = { /** * * @returns {string} 获得的uuid */ getUuid: () => `${new Date().getTime()}-${~~(Math.random() * 9999999999)}`, /** * 获取对象的类型 * * @param {*} obj 要检查的对象 * @returns {string} 'String', 'Object', 'Array'.... */ getInstance: function (obj) { const result = Object.prototype.toString.call(obj) const { groups: { type } } = /^\[object\s+(?<type>\w+(\d\w)*)\]$/.exec(result) return type }, /** * 根据最大行数对Array进行分段 * * @param {*[]}[arr=[]] 传入的Array * @param {number}[maxRow=[]] 最大行数 * @returns {Array[]} 返回分段后的Array */ splitArrByRow: function (arr = [], maxRow = 5) { maxRow = ~~(((+maxRow) ** 2) ** 0.5) || 1 // 先转换为正整数 const devider = Math.ceil(arr.length / maxRow) // 例如总公共有6个,每份5个,分成2份 if (arr.length > maxRow) { const arrOut = [] for (let i = 0; i < devider; i++) { const [from, to] = [i * maxRow, (i + 1) * maxRow] if (to < arr.length) { // 如果范围没超,从from截取到to但不含to arrOut.push(arr.slice(from, to)) } else { // 如果是最后一列,截取到结尾 arrOut.push(arr.slice(from)) } } return arrOut } else { return [arr] } }, /** * 根据列数数对Array进行分段 * * @param {*[]}[arr=[]] 传入的Array * @param {number}[cols=[]] 列数 * @returns {Array[]} 返回分段后的Array */ splitArrByCol: function (arr = [], cols = 2) { cols = ~~(((+cols) ** 2) ** 0.5) || 1 // 先转换为正整数 const maxRow = Math.ceil(arr.length / cols) // 例如总公共有5个,每份2列,分成3份 if (arr.length > maxRow) { const arrOut = [] for (let i = 0; i < cols; i++) { const [from, to] = [i * maxRow, (i + 1) * maxRow] if (to < arr.length) { // 如果范围没超,从from截取到to但不含to arrOut.push(arr.slice(from, to)) } else { // 如果是最后一列,截取到结尾 arrOut.push(arr.slice(from)) } } return arrOut } else { return [arr] } }, /** * 将img标签转为base64 * * @description 在electron, 有些网站需要设置webPreferences-webSecurity为false * @description 如果需要导出图片到文件需要设置 {encoding: "base64"} * @see {@link https://www.electronjs.org/zh/docs/latest/api/browser-window#new-browserwindowoptions | BrowserWindow设置} * @param {Element}img <img>标签 * @returns {object} - .ext 格式(jpg等,不含".") - .data Base64内容 .base64 包含data:image/png;base64开头的文件 */ getImgBase64: function (img) { // 创建一个空的canvas元素 const canvas = document.createElement('canvas') canvas.width = img.width canvas.height = img.height // Copy the image contents to the canvas const ctx = canvas.getContext('2d') ctx.drawImage(img, 0, 0) // Get the data-URL formatted image // Firefox supports PNG and JPEG. You could check img.src to // guess the original format, but be aware the using "image/jpg" // will re-encode the image. const dataURL = canvas.toDataURL('image/png') const reg = /^data:image\/(?<ext>\w+);base64,/ const { groups: { ext } } = reg.exec(dataURL) return { data: dataURL.replace(reg, ''), ext, base64: dataURL } }, /** * 在页面中是否存在某个元素 * * @param {string}css css选择器 * @returns {boolean} 该元素是否存在 */ exists: function (css) { const $ = require('jquery') return $(css).length > 0 }, /** * 显示Url窗口,如果需要跳转,则输入回车 */ showUrl: function () { const $ = require('jquery') $(document.body).prepend( `<div style="position:fixed;width: calc(100vw - 20px);bottom:0px;z-index:9999;background-color:#fff;"> <div class="example-showcase"> <div class="el-input el-input--default el-input-group el-input-group--append"> <input id="my-preload-common-href" class="el-input__inner" type="text" autocomplete="off" placeholder="Please input" /> <div id="my-preload-common-href-go" class="el-input-group__append" style="cursor:pointer;">转到</div> </div> </div> </div>` ) let history = '' setInterval(() => { history !== window.location.href && $('#my-preload-common-href').val(window.location.href) history = window.location.href }, 500) const cb = () => { let inputVal = $('#my-preload-common-href').val() ;/https?:\/{2}/i.test(inputVal) || (inputVal = 'http://' + inputVal) window.location.href = inputVal } $('#my-preload-common-href-go').on('click', cb) $('#my-preload-common-href').each((i, n) => { n.addEventListener('keydown', ({ key }) => /enter/i.test(key) && cb()) }) }, /** * 创建一个倒计时窗 * * @param {object}param0 选项 * @param {string}param0.spanId 倒计时内的span的id * @param {number}param0.duration 倒计时的总毫秒数 * @param {number}param0.interval 倒计时所使用的减小间隔 * @param {Function}param0.countDownMessage (timeLeft) => 用于显示timeleft的函数,返回字符串 * @param {Function}param0.beforeStart 在倒计时开始之前运行的内容 * @param {Function}param0.fail 倒计时取消时运行的内容 * @param {Function}param0.success 倒计时结束时运行的内容 * @param {Function}param0.every 每间隔都运行的内容 * @param {boolean}param0.showCancel 是否显示"取消" * @param {boolean}param0.showStart 是否显示"立即" * @param {boolean}param0.showRestart 是否显示"重新" * @returns {object} [msg=>ElMessage对象] [promise=Promise对象,将会在倒计时结束时resolve] */ startCountDown: function ({ spanId = 'my-spider-countdown', duration = 10 * 1000, interval = 100, countDownMessage = (timeLeft) => `剩余${timeLeft}毫秒`, showCancel = true, showStart = true, showRestart = false, beforeStart = () => 0, fail = () => 0, success = () => 0, every = () => 0 }) { beforeStart() const { ElMessage } = require('element-plus') const { h } = require('vue') const $ = require('jquery') const innerMsg = [] const styleConfig = { class: 'el-button el-button--text', style: 'display:inline;' } showRestart && innerMsg.push( h('span', { id: `${spanId}Restart`, ...styleConfig }, '重新') ) showStart && innerMsg.push(h('span', { id: `${spanId}Start`, ...styleConfig }, '立即')) showCancel && innerMsg.push( h('span', { id: `${spanId}Cancel`, ...styleConfig }, '取消') ) const message = h('div', { style: 'line-height:1.5;' }, [ h( 'span', { id: `${spanId}`, style: 'margin-right:15px;' }, countDownMessage(duration) ), ...innerMsg ]) const msg = ElMessage({ duration: duration + 10, message }) return { msg, promise: new Promise((resolve, reject) => { let _timeLeft = duration let _timer = '' /** * */ function _successCallback() { clearInterval(_timer) $(`#${spanId}`).text(countDownMessage(0)) $(`#${spanId}Cancel`).remove() $(`#${spanId}Start`).remove() msg.close() success() resolve(1) } /** * */ function _failCallback() { clearInterval(_timer) $(`#${spanId}Cancel`).remove() $(`#${spanId}Start`).remove() msg.close() fail() // 如果点击了取消就运行fail这个function // eslint-disable-next-line prefer-promise-reject-errors reject('Countdown Cancelled') // 并且返回catch } $(`#${spanId}Restart`).click(() => { _timeLeft = duration $(`#${spanId}`).text(countDownMessage(_timeLeft)) }) $(`#${spanId}Start`).click(_successCallback) $(`#${spanId}Cancel`).click(_failCallback) _timer = setInterval(() => { every(_timeLeft) if (_timeLeft < interval) { _successCallback() } else { _timeLeft -= interval $(`#${spanId}`).text(countDownMessage(_timeLeft)) } }, interval) }) } }, /** * 等待多少ms * * @param {number} interval 单位毫秒 * @returns {Promise} undefined */ sleep: function (interval) { return new Promise((resolve) => { setTimeout(resolve, interval) }) }, /** * 在数字前面补0,如果数字已经够大了,就不补了 * * @param {number|string} num - 要计算的数字 * @param {number} minLength - 补到多少位数 * @returns {string} - 补充完0的字符串 */ keep0s(num, minLength) { const isMinus = +num < 0 let str = (isMinus ? -num : num) + '' const { length: len } = str.split('.')[0] if (len < minLength) { str = Array.from({ length: minLength - len }) .map(() => '0') .join('') + str } return `${isMinus ? '-' : ''}${str}` }, /** * @function 将60进制字符串转化成秒 * @param {object|string}options 第一个参数可以使选项,也可以是时间字符串 * @param {number}[options.base=60] - 多少进制 * @param {string|RegExp}[options.delimeter=/[::]{1}/g] - 分割符 * @param {...string} args 60进制时间字符串 * @returns {number} 总秒数 */ secOf60s(options, ...args) { const times = [options, ...args] // 按值传递 // 第一个参数是否为Object也就是选项 const isFirstArgOptions = Object.prototype.toString.call(times[0]) === '[object Object]' const { base = 60, delimeter = /[::]{1}/g } = isFirstArgOptions ? times.shift() : {} const reg = /^\s*-\s*/g let secs = 0 // 总秒数 for (const str of times) { const sgn = reg.test(str) ? -1 : 1 // 是否为负 if (str) { // eslint-disable-next-line no-unused-vars const arr = [ ...str.replace(reg, '').replace(/\s+/g, '').split(delimeter) ] .reverse() .forEach((ele, i) => (secs += base ** i * sgn * +ele)) } } return secs }, /** * @function 将总秒数转化为60进制字符串 * @param {object|number}options - 第一个参数可以是选项,也可以是秒数 * @param {number}[options.digits=0] - 保留小数点后几位 * @param {number}[options.colons=1] - 用几个冒号分割 * @param {number}[options.base=60] - 多少进制 * @param {string}[options.delimeter=":"] - 分割符 * @param {boolean}[options.keep0=true] - 是否补0,直到和base的位数一致 * @param {...number} args - 数字 * @returns {string}返回计算结果 */ to60sFromSec(options, ...args) { const times = [options, ...args] // 按值传递 // 第一个参数是否为Object也就是选项 const isFirstArgOptions = Object.prototype.toString.call(times[0]) === '[object Object]' const { digits = 0, // colons = 1, base = 60, delimeter = ':', keep0 = true } = isFirstArgOptions ? times.shift() : {} let total = 0 // 总共有多少秒 if (Object.prototype.toString.call(times) === '[object Array]') { for (const time of times) { total += time } } else { total = +times } const isMinus = total < 0 isMinus && (total = -total) // 如果为负数,则变为正数 let arr = Array.from({ length: colons + 1 }) .map((_, i) => i) .reverse() .map((i) => { if (i === 0) { return `${~~(total * 10 ** digits) / 10 ** digits}` } else { const floored = Math.floor(total / base ** i) total %= base ** i return floored + '' } }) if (keep0) { arr = arr.map((e) => exportObject.keep0s(e, `${~~base}`.length)) } return `${isMinus ? '-' : ''}${arr.join(delimeter)}` }, /** * @param {object|string|number}options - 第一个参数可以是选项,也可以是秒数 * @param {number}[options.digits=0] - 保留小数点后几位 * @param {number}[options.colons=1] - 用几个冒号分割 * @param {number}[options.base=60] - 多少进制 * @param {string}[options.delimeter=":"] - 分割符 * @param {boolean}[options.keep0=true] - 是否补0,直到和base的位数一致 * @param {...string|number} args 要加的数字或字符串 * @returns {string}计算结果 */ sumOf60s(options, ...args) { let total = 0 const strs = [...arguments].filter( (e) => Object.prototype.toString.call(e) === '[object String]' ) // 筛选出字符串部分 const nums = [...arguments].filter( (e) => Object.prototype.toString.call(e) === '[object Number]' ) // 筛选出数字部分 nums.forEach((e) => (total += e)) // 计算数字部分相加结果 if (Object.prototype.toString.call(options) === '[object Object]') { // 如果有options total += exportObject.secOf60s(options, ...strs) return exportObject.to60sFromSec(options, total) } else { // 如果不带options total += exportObject.secOf60s(...strs) return exportObject.to60sFromSec(total) } }, /** * 判断是否为HTMl元素 * * @param {*}obj 任何变量 * @returns {boolean} 是否为html元素 */ isHTMLElement(obj) { try { const d = document.createElement('div') d.appendChild(obj.cloneNode(true)) return obj.nodeType === 1 } catch (e) { try { return obj === window || obj === document } catch (error) { return false } } }, getWsAddress() { const { protocol, host } = location const wsProtocol = protocol.replace('http', 'ws') return `${wsProtocol}//${host}` }, /** * 批量处理ws.onmessage * * @param {Function[]}handlers (msg)=>{ ...todo}其中msg已经parse过了 * @param {WebSocket}ws 传入一个weWebSocket实例 */ wsOn(handlers = [], ws = new WebSocket(exportObject.getWsAddress())) { ws.onmessage = ({ data }) => { for (const func of handlers) { try { func(JSON.parse(data)) } catch (error) { func(data) } } } }, /** * 封装 ws.send * * @param {*} msg 向服务器发送的对象,会经过JSON.stringify处理 * @param {WebSocket}ws 传入一个weWebSocket实例 */ wsSend(msg, ws = new WebSocket(exportObject.getWsAddress())) { ws.send(JSON.stringify(msg)) }, /** * 由模拟点击来跳转到某个页面 * * @param {string}[href="/"] 要跳转的link * @param {string}[target="_blank"] target */ clickOpenLink(href = '/', target = '_blank') { const id = 'a' + exportObject.getUuid() const $ = require('jquery') $(document.body).append(`<a id ="${id}" href="${href}"></a>`) $('#' + id).each((i, n) => n.click()) $('#' + id).remove() }, /** * @param {number}percentage 百分比 * @returns {string}根据百分比返回的颜色字符串 */ getColorByPercentage(percentage) { if (percentage > 0) { const { max } = Math const r = 0 + ((255 - 0) * percentage) / max(percentage, 100) const g = 240 + ((150 - 240) * percentage) / max(percentage, 100) const b = 180 + ((101 - 180) * percentage) / max(percentage, 100) return `rgb(${r},${g},${b})` } else { return '#8696a7' } }, /** * 用于element-ui的el-table的filter-method * * @param {*} value No discription * @param {*} row No discription * @param {*} column No discription * @returns {boolean} No discription */ generalFilterHandle(value, row, column) { const property = column.property return row[property] === value }, /** * 将Date对象转为字符串 * * @param {string} [fmt='YY-mm-dd'] <Y,m,d,H,M,S>组成的字符串 * @param {Date} [date=new Date()] Date对象 * @returns {string} 返回日期对应的format之后的字符串 */ dateFormat(fmt = 'YY-mm-dd', date = new Date()) { let ret const opt = { 'Y+': date.getFullYear().toString(), // 年 'm+': (date.getMonth() + 1).toString(), // 月 'd+': date.getDate().toString(), // 日 'H+': date.getHours().toString(), // 时 'M+': date.getMinutes().toString(), // 分 'S+': date.getSeconds().toString() // 秒 // 有其他格式化字符需求可以继续添加,必须转化成字符串 } for (const k in opt) { ret = new RegExp('(' + k + ')').exec(fmt) if (ret) { fmt = fmt.replace( ret[1], ret[1].length === 1 ? opt[k] : opt[k].padStart(ret[1].length, '0') ) } } return fmt }, /** * * @param {string | number | Date} a 被减数 * @param {string | number | Date} b 减数 * @returns {number}计算出两个时间的月份差 */ getMonthDifference(a, b) { if (/(string|number)/i.test(a)) { a = new Date(a) } if (/(string|number)/i.test(b)) { b = new Date(b) } return ( a.getFullYear() * 12 + a.getMonth() - b.getFullYear() * 12 - b.getMonth() ) } } module.exports = exportObject