UNPKG

seasun-util

Version:

JavaScript 软件开发工具包

317 lines (310 loc) 11.9 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Seasun = {})); })(this, (function (exports) { 'use strict'; /** * 常用工具函数模块 * @module basic * */ /** * 异步 sleep 函数 * @param ms 毫秒数 * @returns Promise<void> * @example * await sleep(1000); // 等待1秒 */ function sleep(ms) { return new Promise(function (resolve) { return setTimeout(resolve, ms); }); } /** * 节流函数:多次触发,只在间隔一段时间后才能再触发,执行目标函数 * @param fn 需要节流的函数 * @param interval 间隔时间(毫秒) * @returns 包装后的函数 */ function throttle(fn, interval) { var timer = null; var lastTime = 0; return function () { var _this = this; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var now = Date.now(); if (lastTime && now < lastTime + interval) { // 如果距离上次执行还没有超过delay时间,则清除定时器并重新设置 if (timer) { window.clearTimeout(timer); } timer = window.setTimeout(function () { lastTime = now; fn.apply(_this, args); }, interval); } else { // 如果距离上次执行已经超过delay时间,则直接执行 lastTime = now; fn.apply(this, args); } }; } /** * 防抖函数:多次触发,只在最后一次触发后的一段时间后才执行目标函数 * @param fn 需要防抖的函数 * @param delay 延迟时间(毫秒) * @returns 包装后的函数 */ function debounce(fn, delay) { var timer = null; return function () { var _this = this; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (timer) { window.clearTimeout(timer); } timer = window.setTimeout(function () { fn.apply(_this, args); }, delay); }; } /** * 检测对象是否含有某个或某组属性 * @param obj {Object} 检测对象 * @param keys {Array|string} 属性列表 * @example * var obj={name:"test",age:18}; * hasKey(obj,["name","age"]) // true * hasKey(obj,["name","age","sex"]); // false */ function hasKeys(obj, keys) { if (obj instanceof Object) { if (typeof keys === 'string') keys = [keys]; for (var i = 0; i < keys.length; i++) { if (!Object.prototype.hasOwnProperty.call(obj, keys[i])) return false; } } return true; } /** * 深度优先搜索遍历树形结构 * * @template T - 树节点类型 * @param {T[]} items - 要遍历的树节点数组 * @param {(item: T, depth: number) => boolean} callback - 处理每个节点的回调函数 * 返回true继续遍历,返回false停止遍历 * @param {number} [depth=1] - 当前遍历深度,内部递归使用 * @param {string} [childrenKey='children'] - 子节点属性名,默认为'children' * @returns {boolean} - 如果遍历成功完成返回true,如果被中途停止则返回false */ function dfs(items, callback, depth, childrenKey) { if (depth === void 0) { depth = 1; } if (childrenKey === void 0) { childrenKey = 'children'; } if (!items || !Array.isArray(items) || items.length === 0) { return true; // 无内容可遍历,视为成功完成 } for (var i = 0; i < items.length; i++) { var item = items[i]; // 执行回调并检查是否应该继续 var shouldContinue = callback(item, depth); if (!shouldContinue) { return false; // 回调返回false,遍历被中止 } var children = item === null || item === void 0 ? void 0 : item[childrenKey]; // 如果有子节点,递归遍历 if (children && Array.isArray(children) && children.length > 0) { var shouldStop = dfs(children, callback, depth + 1, childrenKey); if (!shouldStop) { return false; // 子树遍历被中断,向上传递中断信号 } } } // 遍历完成未中断 return true; } /** * 生成指定长度的随机ID * @param {number} length ID长度,默认为8 * @returns {string} 返回指定长度的随机ID */ function generateRandomId(length) { if (length === void 0) { length = 8; } var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; var result = ''; var charsLength = chars.length; for (var i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * charsLength)); } return result; } /** * 格式化链接,将链接中的占位符替换为 params 中对应的值 * @param url - 链接 * @param params - 参数对象 * @param reg - 可选,自定义正则表达式,用于匹配占位符 * @returns 格式化后的链接 * @example * formatUrlWithParams("https://example.com/product/${product_key}/service/praise",{product_key:'jx3'}) // 默认匹配 ${key} 格式 * formatUrlWithParams("https://example.com/product/{product_key}/service/praise",{product_key:'jx3'}, new RegExp(/\{(\w+)\}/, 'g')) // 匹配 {key} 格式 */ function formatUrlWithParams(url, params, reg) { if (!url) { return ''; } // 默认匹配 ${key} 格式的占位符 var defaultRegex = new RegExp(/\$\{(\w+)\}/, 'g'); var regex = reg || defaultRegex; // 使用正则表达式匹配占位符 return url.replace(regex, function (match, key) { // 如果在 params 中找到对应的 key,则替换,否则保留原始匹配 return params[key] !== undefined ? params[key] : match; }); } // 参考:https://mp.weixin.qq.com/s/Szl-4Q7gvHXBM1kB8Hhenw /** * 类型检测模块 * @module type * */ /** * 获取变量的类型名称。 * @param {any} value - 需要获取类型的值。 * @returns {string} 返回值的类型名称。 */ function getType(value) { return Object.prototype.toString.call(value).slice(8, -1); } /** * 检查变量是否为数组。 * @param {any} value - 需要检查的值。 * @returns {boolean} 如果是数组返回 true,否则返回 false。 */ function isArray(value) { return getType(value) === 'Array'; } /** * 格式化模块 * @module format * */ /** * 格式化时间戳 * @param {number} time 时间戳 * @param {string} fmt 格式 * @return {String} * @example * formatTime(1700152449834) ===> 2023-11-17 00:34:09 */ function formatTime(time, fmt) { if (fmt === void 0) { fmt = 'yyyy-MM-dd hh:mm:ss'; } var ret; var date = new Date(time); var 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 (var 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 startTime {Date} 开始时间 * @return String * @example * formatPassTime(new Date(2022-01-01 00:00:00)) ===> 1年前 * formatPassTime(new Date(2021-21-01 10:00:00)) ===> 2月前 */ function formatTimePass(startTime) { var currentTime = Date.parse(new Date().toString()), time = currentTime - (typeof startTime === 'number' ? startTime : Date.parse(startTime.toString())), day = parseInt((time / (1000 * 60 * 60 * 24)).toString()), hour = parseInt((time / (1000 * 60 * 60)).toString()), min = parseInt((time / (1000 * 60)).toString()), month = parseInt((day / 30).toString()), year = parseInt((month / 12).toString()); if (year) return year + '年前'; if (month) return month + '个月前'; if (day) return day + '天前'; if (hour) return hour + '小时前'; if (min) return min + '分钟前'; else return '刚刚'; } /** * 正则检测模块 * @module regexp * */ /** * 检测是否为有效的时时间戳格式 * @param {String} str 检测的值 * @return {Boolean} true或false * */ function isValidTimestamp(str) { // 1. 检查字符串是否为空 if (!str) return false; // 处理数字类型的输入 var strValue = typeof str === 'number' ? str.toString() : str; // 2. 检查是否为10位或13位数字 if (!/^(-?\d{10}$)|(-?\d{13}$)/.test(strValue)) { return false; } // 3. 将字符串转换为数字 var timestamp = parseInt(strValue); // 4. 检查是否为有效数字 if (isNaN(timestamp)) { return false; } // 5. 检查是否在JavaScript的安全整数范围内 if (timestamp < Number.MIN_SAFE_INTEGER || timestamp > Number.MAX_SAFE_INTEGER) { return false; } // 6. 检查是否为正数 if (timestamp < 0) { return false; } // 7. 验证时间戳是否在合理的时间范围内 var date = new Date(timestamp); var isValidDate = !isNaN(date.getTime()); // 8. 对于10位时间戳,需要乘以1000转换为毫秒 var finalTimestamp = strValue.length === 10 ? timestamp * 1000 : timestamp; // 9. 验证最终时间戳是否在合理范围内 var isValidRange = finalTimestamp >= Date.UTC(1970, 0, 1) && finalTimestamp <= Date.UTC(2038, 0, 19); return isValidDate && isValidRange; } /** * 检测是否为有效的时间字符串格式 * @param {String} str 检测的值 * @return {Boolean} true或false * */ function isValidDateTime(str) { var regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:[+-]\d{2}:\d{2}|Z)$/; return regex.test(str); } exports.debounce = debounce; exports.dfs = dfs; exports.formatTime = formatTime; exports.formatTimePass = formatTimePass; exports.formatUrlWithParams = formatUrlWithParams; exports.generateRandomId = generateRandomId; exports.getType = getType; exports.hasKeys = hasKeys; exports.isArray = isArray; exports.isValidDateTime = isValidDateTime; exports.isValidTimestamp = isValidTimestamp; exports.sleep = sleep; exports.throttle = throttle; }));