UNPKG

app-lib-progress

Version:
211 lines (182 loc) 6.87 kB
/************************************************************************************* * * 支持已知进度和未知进度打印 * **************************************************************************************/ const { log } = require('app-lib-log'); const MODULE_NAME = "app-lib-progress"; const defaultOption = { stream: process.stderr, // 输出 total: -1, // 总进度数量 // 没有总数的时候 表示没有君度的显示 current: 0, // 当前进度 loopText: ["—", "\\", "|", "/"], // 未知进度的旋转文本 currentLoopTextIndex: 0, // 轮询显示位置 loopTime: 1000, // 动画切换时间 cChar: "=", // 进度完成的显示 ucChar: "-", // 未完成的显示 output: ":flag (:current)", // 输出内容格式化 startTime: 0, // 开始时间 clear: () => true, // 结束是否清空控控制台 success: () => true, // 每次完成回调 true 清空控制太 false 不清空 默认不清空 throttle: 10, // 防抖时间 cusField: [], // 自定义需要格式化的字段 data: null, // 每次动态显示的数据 按照对象的key进行output对应即可, isComplete: false, }; const type = any => Object.prototype.toString.call(any).split(' ')[1].split(']')[0].toLowerCase(); class Progress { /** * 初始化参数 * * @param {oobject} option */ constructor(option) { this.lastContent = ""; // 上一次打印内容 this.allContent = []; // 全部打印内容 this.option = { ...defaultOption, ...option } } /** * 设置属性值 * * @param {string} key 属性值 * @param {any} value 值 * @returns {object} 框架对象 */ set(key, value) { this.option[key] = value; return this; } /** * 获取属性 * @param {string} key 属性键 * @returns {any} 属性键对应的值 */ get(key) { return this.option[key]; } /** * 开始统计 * * @param {num} length 长度 相对total * @param {object} data 自定义数据 * @param {boolean} isForce 是否强制执行 * @returns 进度对象 */ count(length, data, isForce) { if (!isForce && this.option.isComplete) return; let len = void 0 === length ? 1 : 1 * length; if (isNaN(len)) { len = 1; log.mwn(MODULE_NAME, `length require number type`); } // 开始时间 if (0 == this.option.current) this.option.startTime = + new Date; this.option.current += len; this.option.data = data; return this.output(); } /** * 格式化输出字段内容 * @returns {string} 格式化的内容 */ formatStr() { let { stream, total, current, startTime, loopText, currentLoopTextIndex, loopTime, cChar, ucChar, output, data, cusField } = this.option; // 消耗时间 let useTime = (((+ new Date) - startTime) / 1000).toFixed(1); // 秒 let percent = 0; let flag = loopText[currentLoopTextIndex % loopText.length]; if (total == -1) { // 没有总数 只是数量 } else { // 进度 // 进度百分比 var ratio = this.curr / this.total; ratio = Math.min(Math.max(ratio, 0), 1); percent = Math.floor(ratio * 100); } let str = output .replace(":total", total) // 总数 .replace(":current", current) // 当前数量 .replace(":flag", flag) // 无总数的旋转标识 .replace(":useTime", useTime) // 消耗时间 .replace(":percent", percent) // 百分比 // TODO 后续需要进度在进行添加 // 格式化用户自定义数据 if (cusField.length) { cusField.forEach(diyFiled => { let value = data?.[diyFiled]; str = str.replace(`:${diyFiled}`, void 0 == value ? '-' : value) // 总数 }); } return str; } // 设置完成 complate(length = 0, data = null) { this.option.isComplete = true; this.count(length, data || this.option.data, true); } // 当前是否处于完成状态 isComplete() { if (this.option.isComplete) return true; if (this.option.total == -1) return this.option.isComplete; return this.option.total <= this.option.current; } /** * 输出内容 * */ output() { if (!this.option.stream.isTTY) return; this.option.currentLoopTextIndex += 1; let outputString = this.formatStr(); this._log(outputString); if (this.isComplete()) { if (this.option?.success()) { this.over(); } } } /** * 结束时是否清空输出 * @returns {object} 进度对象 */ over() { if (this.option?.clear) { if (this.option.clear(log)) { this.option.stream?.clearLine(); this.option.stream?.cursorTo(0); } } // this.option.stream?.write('\n'); return this; } // 输出到控制太 _log(str) { if (this.lastContent === str) return; let stream = this.option.stream; stream.cursorTo(0); stream.write(str); stream.clearLine(1); this.lastContent = str; this.allContent.push(str) // 记录用于获取全部内容 } /** * 向上插入信息打印 * @param {string/function} message function 标识回调函数外部进行打印 */ log(fn, ...message) { let stream = this.option.stream; // clear the current line stream.clearLine(); // move the cursor to the start of the line stream.cursorTo(0); // write the message text if (type(fn) == "function") { fn.apply(log,message); } else { log.i(...[fn,...message]); stream.write('\n'); } stream.write(this.lastContent); } } exports = module.exports = Progress;