seasun-util
Version:
JavaScript 软件开发工具包
317 lines (310 loc) • 11.9 kB
JavaScript
(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;
}));