@pmun/utils 0.3.5 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -588,6 +588,50 @@ declare function formatHumanReadable(date: DateLike): string;
588
588
  * ```
589
589
  */
590
590
  declare function formatChatTime(date: DateLike): string;
591
+ /**
592
+ * 格式化时间戳为时长字符串
593
+ *
594
+ * 支持自定义格式化模板和智能单位显示。如果提供格式化字符串,则按照指定格式返回;
595
+ * 如果不提供,则智能判断显示单位,只显示有意义的时间单位。
596
+ *
597
+ * @param timestamp 时间戳(毫秒)或者持续时间(毫秒)
598
+ * @param format 可选的格式化字符串,支持 YY(年)、MM(月)、DD(天)、HH(小时)、mm(分钟)、ss(秒钟)占位符
599
+ * @returns 格式化后的时长字符串
600
+ * @group Date
601
+ * @example
602
+ * ```ts
603
+ * // 使用自定义格式
604
+ * formatDuration(3661000, 'HH:mm:ss') // "01:01:01"
605
+ * formatDuration(3661000, 'HH时mm分ss秒') // "01时01分01秒"
606
+ * formatDuration(90061000, 'DD天HH时mm分ss秒') // "01天01时01分01秒"
607
+ * formatDuration(2592000000, 'MM月DD天') // "01月00天"
608
+ * formatDuration(31536000000, 'YY年MM月DD天') // "01年00月00天"
609
+ *
610
+ * // 单一格式返回数字(双字母或单字母)
611
+ * formatDuration(604800000, 'HH') // 168(总小时数)
612
+ * formatDuration(604800000, 'H') // 168(总小时数)
613
+ * formatDuration(3600000, 'mm') // 60(总分钟数)
614
+ * formatDuration(3600000, 'm') // 60(总分钟数)
615
+ * formatDuration(604800000, 'DD') // 7(总天数)
616
+ * formatDuration(604800000, 'D') // 7(总天数)
617
+ *
618
+ * // 智能格式(只显示有意义的单位)
619
+ * formatDuration(3600000) // "1小时"(正好1小时)
620
+ * formatDuration(3661000) // "1小时1分钟1秒"(1小时1分钟1秒)
621
+ * formatDuration(90061000) // "1天1小时1分钟1秒"(1天1小时1分钟1秒)
622
+ * formatDuration(2592000000) // "1个月"(30天)
623
+ * formatDuration(31536000000) // "1年"(365天)
624
+ * formatDuration(125000) // "2分钟5秒"(2分钟5秒)
625
+ * formatDuration(30000) // "30秒"(30秒)
626
+ * formatDuration(5000) // "5秒"(5秒)
627
+ * formatDuration(0) // "0秒"(0秒)
628
+ *
629
+ * // 处理超大时间值
630
+ * formatDuration(94608000000) // "3年"(3*365天)
631
+ * formatDuration(94694461000) // "3年1天1小时1分钟1秒"
632
+ * ```
633
+ */
634
+ declare function formatDuration(timestamp: number, format?: string): string | number;
591
635
 
592
636
  /**
593
637
  * @module 类型检查
@@ -1482,4 +1526,4 @@ declare function parseJsonStr<T = any>(str?: string | null, defaultValue?: T): T
1482
1526
  */
1483
1527
  declare function getQueryStringify(params: Record<string, any> | null | undefined): string;
1484
1528
 
1485
- export { type DateLike, add, addDays, addMonths, addYears, appendUniversalOption, arrayReplaceNBSP, arrayToObject, camelToKebab, capitalize, chunk, clamp, convertToDayjsParam, createDate, deepClone, deepMerge, delay, diff, endOf, ensureRpxUnit, escapeHtml, filterObjectByKeys, first, formatChatTime, formatCurrency, formatDate, formatFullTime, formatHumanReadable, formatNumberWithTenThousand, formatThousands, fromNow, get, getDayOfWeek, getDaysInMonth, getQueryStringify, groupBy, hasOwnProp, isArray, isBoolean, isClass, isDate, isDateInRange, isEmpty, isEmptyObject, isEmptyString, isEqual, isEven, isFunction, isMap, isMillisecondTimestamp, isMobilePhone, isNaN, isNull, isNullOrUndefined, isNumber, isObject, isOdd, isPlainObject, isPrimitive, isPromise, isRegExp, isSet, isString, isSymbol, isUndefined, isValidEmail, isValidUrl, kebabToCamel, last, merge, now, objectToQueryString, omit, parseDate, parseJsonStr, percentage, pick, randomInt, randomString, remove, renameTreeNodes, replaceNBSP, round, sample, shuffle, startOf, transformTree, truncate, unique };
1529
+ export { type DateLike, add, addDays, addMonths, addYears, appendUniversalOption, arrayReplaceNBSP, arrayToObject, camelToKebab, capitalize, chunk, clamp, convertToDayjsParam, createDate, deepClone, deepMerge, delay, diff, endOf, ensureRpxUnit, escapeHtml, filterObjectByKeys, first, formatChatTime, formatCurrency, formatDate, formatDuration, formatFullTime, formatHumanReadable, formatNumberWithTenThousand, formatThousands, fromNow, get, getDayOfWeek, getDaysInMonth, getQueryStringify, groupBy, hasOwnProp, isArray, isBoolean, isClass, isDate, isDateInRange, isEmpty, isEmptyObject, isEmptyString, isEqual, isEven, isFunction, isMap, isMillisecondTimestamp, isMobilePhone, isNaN, isNull, isNullOrUndefined, isNumber, isObject, isOdd, isPlainObject, isPrimitive, isPromise, isRegExp, isSet, isString, isSymbol, isUndefined, isValidEmail, isValidUrl, kebabToCamel, last, merge, now, objectToQueryString, omit, parseDate, parseJsonStr, percentage, pick, randomInt, randomString, remove, renameTreeNodes, replaceNBSP, round, sample, shuffle, startOf, transformTree, truncate, unique };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import i from'dayjs';import N from'dayjs/plugin/isBetween';import A from'dayjs/plugin/isSameOrAfter';import P from'dayjs/plugin/isSameOrBefore';import H from'dayjs/plugin/relativeTime';import'dayjs/locale/zh-cn';function a(n){return typeof n=="string"}function c(n){return typeof n=="number"&&!f(n)}function j(n){return typeof n=="boolean"}function d(n){return typeof n=="function"}function K(n){if(!d(n))return false;let t=n,e=t.toString();if(/^class\s/.test(e))return true;let r=t;return !!(r.prototype&&r.prototype.constructor===r&&(!/^[a-z]/.test(r.name||"")||/\[native code\]/.test(e)))}function g(n){return n!==null&&typeof n=="object"}function D(n){return Array.isArray(n)}function C(n){return n instanceof Date}function B(n){return n instanceof RegExp}function I(n){return g(n)&&d(n.then)&&d(n.catch)}function F(n){return n instanceof Map}function W(n){return n instanceof Set}function z(n){return typeof n=="symbol"}function J(n){return n===null||typeof n!="object"&&typeof n!="function"}function k(n){return typeof n>"u"}function w(n){return n===null}function R(n){return w(n)||k(n)}function S(n){return g(n)&&Object.keys(n).length===0}function Q(n){return R(n)?true:a(n)?n.trim().length===0:D(n)?n.length===0:g(n)?S(n):false}function f(n){return Number.isNaN(n)}function p(n){if(typeof n!="object"||n===null)return false;let t=Object.getPrototypeOf(n);return t===Object.prototype||t===null}function q(n){if(!a(n)&&!c(n))return false;let t=String(n);return /^1[3-9]\d{9}$/.test(t)}function y(n,t){return Object.prototype.hasOwnProperty.call(n,t)}function l(n){if(n===null||typeof n!="object")return n;if(n instanceof Date)return new Date(n.getTime());if(n instanceof RegExp)return new RegExp(n.source,n.flags);if(Array.isArray(n))return n.map(e=>l(e));let t={};return Object.keys(n).forEach(e=>{t[e]=l(n[e]);}),t}function _(n,t,e){let r=t.split("."),o=n;for(let u of r){if(o==null)return e;o=o[u];}return o===void 0?e:o}function G(n,t){return t.reduce((e,r)=>(r in n&&(e[r]=n[r]),e),{})}function X(n,t){let e={...n};return t.forEach(r=>{delete e[r];}),e}function T(n){return Object.entries(n).filter(([t,e])=>e!=null).map(([t,e])=>Array.isArray(e)?e.map(r=>`${encodeURIComponent(t)}=${encodeURIComponent(String(r))}`).join("&"):`${encodeURIComponent(t)}=${encodeURIComponent(String(e))}`).join("&")}function v(...n){return Object.assign({},...n)}function M(...n){let t={};return n.forEach(e=>{p(e)&&Object.keys(e).forEach(r=>{let o=e[r],u=t[r];p(o)&&p(u)?t[r]=M(u,o):t[r]=l(o);});}),t}function nn(n,t,e={}){return t.reduce((r,o)=>{if(y(n,o)){let u=e[o]||o;r[u]=n[o];}return r},{})}function rn(n){return n.length===0?n:n.charAt(0).toUpperCase()+n.slice(1)}function on(n){return n.replace(/([A-Z])([A-Z]+)([A-Z])/g,"$1$2-$3").replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}function un(n){return n.replace(/-([a-z])/g,(t,e)=>e.toUpperCase())}function sn(n,t=50,e="..."){if(n.length<=t)return n;let r=t-e.length;return n.substring(0,r)+e}function an(n,t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"){let e="",r=t.length;for(let o=0;o<n;o++)e+=t.charAt(Math.floor(Math.random()*r));return e}function fn(n){let t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};return n.replace(/[&<>"'`=/]/g,e=>t[e]||e)}function cn(n){try{return !!new URL(n)}catch{return false}}function pn(n){return /^[\w.%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i.test(n)}function mn(n){return n.trim().length===0}function dn(n){let t=Number(n);return f(t)?n:`${n}rpx`}function b(n){return a(n)?n.replace(/&nbsp;/g," ").replace(/\u00A0/g," "):n}function gn(n,t){if(!n)return t!==void 0?t:{};if(!a(n))return t!==void 0?t:n;let e=n.trim();if(!(e.startsWith("{")&&e.endsWith("}")||e.startsWith("[")&&e.endsWith("]")||e.startsWith('"')&&e.endsWith('"')||/^-?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i.test(e)||["true","false","null"].includes(e)))return t!==void 0?t:n;try{return JSON.parse(e)}catch(o){return console.error("\u89E3\u6790 JSON \u5931\u8D25: \u8F93\u5165\u5B57\u7B26\u4E32\u4E3A",n,"\u9519\u8BEF\u8BE6\u60C5: ",o),t!==void 0?t:n}}function Tn(n,t){return n.filter(e=>e!==t)}function bn(n){return Array.from(new Set(n))}function hn(n,t){if(t<=0)return [n];let e=[];for(let r=0;r<n.length;r+=t)e.push(n.slice(r,r+t));return e}function Dn(n){return n.length>0?n[n.length-1]:void 0}function kn(n){return n.length>0?n[0]:void 0}function wn(n){let t=[...n];for(let e=t.length-1;e>0;e--){let r=Math.floor(Math.random()*(e+1));[t[e],t[r]]=[t[r],t[e]];}return t}function Rn(n){if(n.length===0)return;let t=Math.floor(Math.random()*n.length);return n[t]}function Sn(n,t){return n.length!==t.length?false:n.every((e,r)=>e===t[r])}function Mn(n,t){return n.reduce((e,r)=>{let o=t(r);return (e[o]=e[o]||[]).push(r),e},{})}function On(n,{name:t="label",value:e="",valueKey:r="value"}={}){return [{[t]:"\u5168\u90E8",[r]:e},...n]}function O(n,t,e="children",r=true){return n.map(o=>{let u={...o};return Object.entries(t).forEach(([m,h])=>{y(u,m)&&(u[h]=u[m],r&&delete u[m]);}),Array.isArray(u[e])&&(u[e]=O(u[e],t,e,r)),u})}function L(n,t,e="children"){return n.map(r=>{let o=r[e]?L(r[e],t,e):[];return t(r,o)})}function Ln(n,t){let e={};for(let r of n){let o=r[t];o&&(e[o]=r);}return e}function Nn(n,t){return n.length?n.map(e=>typeof e[t]=="string"?{...e,[t]:b(e[t])}:e):[]}i.locale("zh-cn");i.extend(H);i.extend(P);i.extend(A);i.extend(N);function Kn(n){return i(n)}function U(n){if(!c(n)||n.toString().length!==13)return false;let t=new Date(n);return !f(t.getTime())}function s(n){return U(n)?n:c(n)&&String(n).length===10?n*1e3:n}function Y(n,t="YYYY-MM-DD"){return i(s(n)).format(t)}function Cn(n){return Y(n,"YYYY-MM-DD HH:mm:ss")}function Bn(){return Date.now()}function In(n){let t=i(n);if(!t.isValid())throw new TypeError("\u65E0\u6548\u7684\u65E5\u671F\u683C\u5F0F");return t.toDate()}function Fn(n,t,e="day"){return i(s(n)).diff(i(s(t)),e)}function x(n,t,e="day"){return i(s(n)).add(t,e).toDate()}function Wn(n,t){return x(s(n),t,"day")}function zn(n,t){return x(s(n),t,"month")}function Jn(n,t){return x(s(n),t,"year")}function Qn(n,t=false){let r=i(s(n)).day();return t?r===0?6:r-1:r}function qn(n,t,e){let r=i(s(n));return r.isAfter(i(s(t)))&&r.isBefore(i(s(e)))}function Vn(n,t){return i(new Date(n,t,1)).daysInMonth()}function Zn(n,t){return n=s(n),t=t&&s(t),t?i(n).from(i(t)):i(n).fromNow()}function _n(n,t){return i(s(n)).startOf(t).toDate()}function Gn(n,t){return i(s(n)).endOf(t).toDate()}function Xn(n){let t=i(s(n)),e=i();return t.isSame(e,"day")?`\u4ECA\u5929 ${t.format("HH:mm")}`:t.isSame(e.subtract(1,"day"),"day")?`\u6628\u5929 ${t.format("HH:mm")}`:t.isSame(e.add(1,"day"),"day")?`\u660E\u5929 ${t.format("HH:mm")}`:t.isSame(e,"year")?t.format("M\u6708D\u65E5 HH:mm"):t.format("YYYY\u5E74M\u6708D\u65E5 HH:mm")}function vn(n){let t=i(s(n)),e=i();if(t.isSame(e,"day"))return t.format("HH:mm");if(t.isSame(e.subtract(1,"day"),"day"))return `\u6628\u5929 ${t.format("HH:mm")}`;let r=e.diff(t,"day");if(r>=2&&r<7){let o=["\u661F\u671F\u65E5","\u661F\u671F\u4E00","\u661F\u671F\u4E8C","\u661F\u671F\u4E09","\u661F\u671F\u56DB","\u661F\u671F\u4E94","\u661F\u671F\u516D"],u=t.day();return o[u]||"\u661F\u671F\u65E5"}return t.isSame(e,"year")?t.format("MM\u6708DD\u65E5"):t.format("YYYY\u5E74MM\u6708DD\u65E5")}function $(n,t=0){let e=10**t;return Math.round(n*e)/e}function tt(n,t){return n.toLocaleString(t)}function et(n,t={}){let{currency:e="CNY",locale:r="zh-CN",minimumFractionDigits:o=2,maximumFractionDigits:u=2}=t;return new Intl.NumberFormat(r,{style:"currency",currency:e,minimumFractionDigits:o,maximumFractionDigits:u}).format(n)}function rt(n,t,e){return Math.min(Math.max(n,t),e)}function ot(n,t){return n=Math.ceil(n),t=Math.floor(t),Math.floor(Math.random()*(t-n+1))+n}function E(n){return n%2===0}function it(n){return !E(n)}function ut(n,t,e=2){return t===0?0:$(n/t*100,e)}function st(n,t=2){return n?n>=1e4?`${(n/1e4).toFixed(t)}\u4E07`:n.toString():"0"}function ft(n){return new Promise(t=>setTimeout(t,n))}function mt(n){if(!n)return "";let t=T(n);return t?`?${t}`:""}export{x as add,Wn as addDays,zn as addMonths,Jn as addYears,On as appendUniversalOption,Nn as arrayReplaceNBSP,Ln as arrayToObject,on as camelToKebab,rn as capitalize,hn as chunk,rt as clamp,s as convertToDayjsParam,Kn as createDate,l as deepClone,M as deepMerge,ft as delay,Fn as diff,Gn as endOf,dn as ensureRpxUnit,fn as escapeHtml,nn as filterObjectByKeys,kn as first,vn as formatChatTime,et as formatCurrency,Y as formatDate,Cn as formatFullTime,Xn as formatHumanReadable,st as formatNumberWithTenThousand,tt as formatThousands,Zn as fromNow,_ as get,Qn as getDayOfWeek,Vn as getDaysInMonth,mt as getQueryStringify,Mn as groupBy,y as hasOwnProp,D as isArray,j as isBoolean,K as isClass,C as isDate,qn as isDateInRange,Q as isEmpty,S as isEmptyObject,mn as isEmptyString,Sn as isEqual,E as isEven,d as isFunction,F as isMap,U as isMillisecondTimestamp,q as isMobilePhone,f as isNaN,w as isNull,R as isNullOrUndefined,c as isNumber,g as isObject,it as isOdd,p as isPlainObject,J as isPrimitive,I as isPromise,B as isRegExp,W as isSet,a as isString,z as isSymbol,k as isUndefined,pn as isValidEmail,cn as isValidUrl,un as kebabToCamel,Dn as last,v as merge,Bn as now,T as objectToQueryString,X as omit,In as parseDate,gn as parseJsonStr,ut as percentage,G as pick,ot as randomInt,an as randomString,Tn as remove,O as renameTreeNodes,b as replaceNBSP,$ as round,Rn as sample,wn as shuffle,_n as startOf,L as transformTree,sn as truncate,bn as unique};//# sourceMappingURL=index.js.map
1
+ import i from'dayjs';import _ from'dayjs/plugin/isBetween';import G from'dayjs/plugin/isSameOrAfter';import X from'dayjs/plugin/isSameOrBefore';import v from'dayjs/plugin/relativeTime';import'dayjs/locale/zh-cn';function m(n){return typeof n=="string"}function M(n){return typeof n=="number"&&!l(n)}function on(n){return typeof n=="boolean"}function R(n){return typeof n=="function"}function un(n){if(!R(n))return false;let t=n,r=t.toString();if(/^class\s/.test(r))return true;let e=t;return !!(e.prototype&&e.prototype.constructor===e&&(!/^[a-z]/.test(e.name||"")||/\[native code\]/.test(r)))}function S(n){return n!==null&&typeof n=="object"}function F(n){return Array.isArray(n)}function sn(n){return n instanceof Date}function an(n){return n instanceof RegExp}function cn(n){return S(n)&&R(n.then)&&R(n.catch)}function fn(n){return n instanceof Map}function pn(n){return n instanceof Set}function mn(n){return typeof n=="symbol"}function ln(n){return n===null||typeof n!="object"&&typeof n!="function"}function W(n){return typeof n>"u"}function z(n){return n===null}function J(n){return z(n)||W(n)}function Q(n){return S(n)&&Object.keys(n).length===0}function dn(n){return J(n)?true:m(n)?n.trim().length===0:F(n)?n.length===0:S(n)?Q(n):false}function l(n){return Number.isNaN(n)}function k(n){if(typeof n!="object"||n===null)return false;let t=Object.getPrototypeOf(n);return t===Object.prototype||t===null}function gn(n){if(!m(n)&&!M(n))return false;let t=String(n);return /^1[3-9]\d{9}$/.test(t)}function N(n,t){return Object.prototype.hasOwnProperty.call(n,t)}function O(n){if(n===null||typeof n!="object")return n;if(n instanceof Date)return new Date(n.getTime());if(n instanceof RegExp)return new RegExp(n.source,n.flags);if(Array.isArray(n))return n.map(r=>O(r));let t={};return Object.keys(n).forEach(r=>{t[r]=O(n[r]);}),t}function Tn(n,t,r){let e=t.split("."),o=n;for(let u of e){if(o==null)return r;o=o[u];}return o===void 0?r:o}function hn(n,t){return t.reduce((r,e)=>(e in n&&(r[e]=n[e]),r),{})}function bn(n,t){let r={...n};return t.forEach(e=>{delete r[e];}),r}function j(n){return Object.entries(n).filter(([t,r])=>r!=null).map(([t,r])=>Array.isArray(r)?r.map(e=>`${encodeURIComponent(t)}=${encodeURIComponent(String(e))}`).join("&"):`${encodeURIComponent(t)}=${encodeURIComponent(String(r))}`).join("&")}function Dn(...n){return Object.assign({},...n)}function V(...n){let t={};return n.forEach(r=>{k(r)&&Object.keys(r).forEach(e=>{let o=r[e],u=t[e];k(o)&&k(u)?t[e]=V(u,o):t[e]=O(o);});}),t}function Mn(n,t,r={}){return t.reduce((e,o)=>{if(N(n,o)){let u=r[o]||o;e[u]=n[o];}return e},{})}function Rn(n){return n.length===0?n:n.charAt(0).toUpperCase()+n.slice(1)}function Sn(n){return n.replace(/([A-Z])([A-Z]+)([A-Z])/g,"$1$2-$3").replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()}function On(n){return n.replace(/-([a-z])/g,(t,r)=>r.toUpperCase())}function Nn(n,t=50,r="..."){if(n.length<=t)return n;let e=t-r.length;return n.substring(0,e)+r}function Ln(n,t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"){let r="",e=t.length;for(let o=0;o<n;o++)r+=t.charAt(Math.floor(Math.random()*e));return r}function Hn(n){let t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};return n.replace(/[&<>"'`=/]/g,r=>t[r]||r)}function Yn(n){try{return !!new URL(n)}catch{return false}}function $n(n){return /^[\w.%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i.test(n)}function An(n){return n.trim().length===0}function En(n){let t=Number(n);return l(t)?n:`${n}rpx`}function C(n){return m(n)?n.replace(/&nbsp;/g," ").replace(/\u00A0/g," "):n}function Un(n,t){if(!n)return t!==void 0?t:{};if(!m(n))return t!==void 0?t:n;let r=n.trim();if(!(r.startsWith("{")&&r.endsWith("}")||r.startsWith("[")&&r.endsWith("]")||r.startsWith('"')&&r.endsWith('"')||/^-?\d+(?:\.\d+)?(?:e[+-]?\d+)?$/i.test(r)||["true","false","null"].includes(r)))return t!==void 0?t:n;try{return JSON.parse(r)}catch(o){return console.error("\u89E3\u6790 JSON \u5931\u8D25: \u8F93\u5165\u5B57\u7B26\u4E32\u4E3A",n,"\u9519\u8BEF\u8BE6\u60C5: ",o),t!==void 0?t:n}}function Kn(n,t){return n.filter(r=>r!==t)}function In(n){return Array.from(new Set(n))}function Bn(n,t){if(t<=0)return [n];let r=[];for(let e=0;e<n.length;e+=t)r.push(n.slice(e,e+t));return r}function Fn(n){return n.length>0?n[n.length-1]:void 0}function Wn(n){return n.length>0?n[0]:void 0}function zn(n){let t=[...n];for(let r=t.length-1;r>0;r--){let e=Math.floor(Math.random()*(r+1));[t[r],t[e]]=[t[e],t[r]];}return t}function Jn(n){if(n.length===0)return;let t=Math.floor(Math.random()*n.length);return n[t]}function Qn(n,t){return n.length!==t.length?false:n.every((r,e)=>r===t[e])}function Vn(n,t){return n.reduce((r,e)=>{let o=t(e);return (r[o]=r[o]||[]).push(e),r},{})}function qn(n,{name:t="label",value:r="",valueKey:e="value"}={}){return [{[t]:"\u5168\u90E8",[e]:r},...n]}function q(n,t,r="children",e=true){return n.map(o=>{let u={...o};return Object.entries(t).forEach(([a,f])=>{N(u,a)&&(u[f]=u[a],e&&delete u[a]);}),Array.isArray(u[r])&&(u[r]=q(u[r],t,r,e)),u})}function Z(n,t,r="children"){return n.map(e=>{let o=e[r]?Z(e[r],t,r):[];return t(e,o)})}function Zn(n,t){let r={};for(let e of n){let o=e[t];o&&(r[o]=e);}return r}function _n(n,t){return n.length?n.map(r=>typeof r[t]=="string"?{...r,[t]:C(r[t])}:r):[]}i.locale("zh-cn");i.extend(v);i.extend(X);i.extend(G);i.extend(_);function it(n){return i(n)}function nn(n){if(!M(n)||n.toString().length!==13)return false;let t=new Date(n);return !l(t.getTime())}function s(n){return nn(n)?n:M(n)&&String(n).length===10?n*1e3:n}function tn(n,t="YYYY-MM-DD"){return i(s(n)).format(t)}function ut(n){return tn(n,"YYYY-MM-DD HH:mm:ss")}function st(){return Date.now()}function at(n){let t=i(n);if(!t.isValid())throw new TypeError("\u65E0\u6548\u7684\u65E5\u671F\u683C\u5F0F");return t.toDate()}function ct(n,t,r="day"){return i(s(n)).diff(i(s(t)),r)}function L(n,t,r="day"){return i(s(n)).add(t,r).toDate()}function ft(n,t){return L(s(n),t,"day")}function pt(n,t){return L(s(n),t,"month")}function mt(n,t){return L(s(n),t,"year")}function lt(n,t=false){let e=i(s(n)).day();return t?e===0?6:e-1:e}function dt(n,t,r){let e=i(s(n));return e.isAfter(i(s(t)))&&e.isBefore(i(s(r)))}function gt(n,t){return i(new Date(n,t,1)).daysInMonth()}function yt(n,t){return n=s(n),t=t&&s(t),t?i(n).from(i(t)):i(n).fromNow()}function xt(n,t){return i(s(n)).startOf(t).toDate()}function Tt(n,t){return i(s(n)).endOf(t).toDate()}function ht(n){let t=i(s(n)),r=i();return t.isSame(r,"day")?`\u4ECA\u5929 ${t.format("HH:mm")}`:t.isSame(r.subtract(1,"day"),"day")?`\u6628\u5929 ${t.format("HH:mm")}`:t.isSame(r.add(1,"day"),"day")?`\u660E\u5929 ${t.format("HH:mm")}`:t.isSame(r,"year")?t.format("M\u6708D\u65E5 HH:mm"):t.format("YYYY\u5E74M\u6708D\u65E5 HH:mm")}function bt(n){let t=i(s(n)),r=i();if(t.isSame(r,"day"))return t.format("HH:mm");if(t.isSame(r.subtract(1,"day"),"day"))return `\u6628\u5929 ${t.format("HH:mm")}`;let e=r.diff(t,"day");if(e>=2&&e<7){let o=["\u661F\u671F\u65E5","\u661F\u671F\u4E00","\u661F\u671F\u4E8C","\u661F\u671F\u4E09","\u661F\u671F\u56DB","\u661F\u671F\u4E94","\u661F\u671F\u516D"],u=t.day();return o[u]||"\u661F\u671F\u65E5"}return t.isSame(r,"year")?t.format("MM\u6708DD\u65E5"):t.format("YYYY\u5E74MM\u6708DD\u65E5")}function Dt(n,t){let r=Math.abs(n),e=1e3,o=60*e,u=60*o,a=24*u,f=30*a,w=365*a,d=Math.floor(r/w),g=Math.floor(r%w/f),y=Math.floor(r%f/a),x=Math.floor(r%a/u),T=Math.floor(r%u/o),h=Math.floor(r%o/e);if(t){let H=t.trim(),Y=Math.floor(r/w),$=Math.floor(r/f),A=Math.floor(r/a),E=Math.floor(r/u),U=Math.floor(r/o),P=Math.floor(r/e),b={YY:{total:Y,current:d},Y:{total:Y,current:d},MM:{total:$,current:g},M:{total:$,current:g},DD:{total:A,current:y},D:{total:A,current:y},HH:{total:E,current:x},H:{total:E,current:x},mm:{total:U,current:T},m:{total:U,current:T},ss:{total:P,current:h},s:{total:P,current:h}};if(b[H])return b[H].total;let D=t,K=["YY","MM","DD","HH","mm","ss","Y","M","D","H","m","s"];for(let p of K)if(D.includes(p)&&b[p]){let{current:I}=b[p],B=I.toString().padStart(p.length,"0");D=D.replace(new RegExp(p,"g"),B);}return D}let c=[];return d>0&&c.push(`${d}\u5E74`),g>0&&c.push(`${g}\u4E2A\u6708`),y>0&&c.push(`${y}\u5929`),x>0&&c.push(`${x}\u5C0F\u65F6`),T>0&&c.push(`${T}\u5206\u949F`),(h>0||c.length===0)&&c.push(`${h}\u79D2`),c.join("")}function rn(n,t=0){let r=10**t;return Math.round(n*r)/r}function kt(n,t){return n.toLocaleString(t)}function wt(n,t={}){let{currency:r="CNY",locale:e="zh-CN",minimumFractionDigits:o=2,maximumFractionDigits:u=2}=t;return new Intl.NumberFormat(e,{style:"currency",currency:r,minimumFractionDigits:o,maximumFractionDigits:u}).format(n)}function Rt(n,t,r){return Math.min(Math.max(n,t),r)}function St(n,t){return n=Math.ceil(n),t=Math.floor(t),Math.floor(Math.random()*(t-n+1))+n}function en(n){return n%2===0}function Ot(n){return !en(n)}function Nt(n,t,r=2){return t===0?0:rn(n/t*100,r)}function Lt(n,t=2){return n?n>=1e4?`${(n/1e4).toFixed(t)}\u4E07`:n.toString():"0"}function Yt(n){return new Promise(t=>setTimeout(t,n))}function Et(n){if(!n)return "";let t=j(n);return t?`?${t}`:""}export{L as add,ft as addDays,pt as addMonths,mt as addYears,qn as appendUniversalOption,_n as arrayReplaceNBSP,Zn as arrayToObject,Sn as camelToKebab,Rn as capitalize,Bn as chunk,Rt as clamp,s as convertToDayjsParam,it as createDate,O as deepClone,V as deepMerge,Yt as delay,ct as diff,Tt as endOf,En as ensureRpxUnit,Hn as escapeHtml,Mn as filterObjectByKeys,Wn as first,bt as formatChatTime,wt as formatCurrency,tn as formatDate,Dt as formatDuration,ut as formatFullTime,ht as formatHumanReadable,Lt as formatNumberWithTenThousand,kt as formatThousands,yt as fromNow,Tn as get,lt as getDayOfWeek,gt as getDaysInMonth,Et as getQueryStringify,Vn as groupBy,N as hasOwnProp,F as isArray,on as isBoolean,un as isClass,sn as isDate,dt as isDateInRange,dn as isEmpty,Q as isEmptyObject,An as isEmptyString,Qn as isEqual,en as isEven,R as isFunction,fn as isMap,nn as isMillisecondTimestamp,gn as isMobilePhone,l as isNaN,z as isNull,J as isNullOrUndefined,M as isNumber,S as isObject,Ot as isOdd,k as isPlainObject,ln as isPrimitive,cn as isPromise,an as isRegExp,pn as isSet,m as isString,mn as isSymbol,W as isUndefined,$n as isValidEmail,Yn as isValidUrl,On as kebabToCamel,Fn as last,Dn as merge,st as now,j as objectToQueryString,bn as omit,at as parseDate,Un as parseJsonStr,Nt as percentage,hn as pick,St as randomInt,Ln as randomString,Kn as remove,q as renameTreeNodes,C as replaceNBSP,rn as round,Jn as sample,zn as shuffle,xt as startOf,Z as transformTree,Nn as truncate,In as unique};//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../packages/is/index.ts","../packages/object/index.ts","../packages/string/index.ts","../packages/array/index.ts","../packages/date/index.ts","../packages/number/index.ts","../packages/promise/index.ts","../packages/url/index.ts"],"names":["isString","val","isNumber","isNaN","isBoolean","isFunction","isClass","func","funcStr","funcWithPrototype","isObject","isArray","isDate","isRegExp","isPromise","isMap","isSet","isSymbol","isPrimitive","isUndefined","isNull","isNullOrUndefined","isEmptyObject","isEmpty","value","isPlainObject","proto","isMobilePhone","phoneStr","hasOwnProp","obj","prop","deepClone","item","cloned","key","get","path","defaultValue","keys","result","pick","omit","objectToQueryString","_","merge","objects","deepMerge","sourceValue","targetValue","filterObjectByKeys","originalObject","keysArray","keyMapping","newObject","newKey","capitalize","str","camelToKebab","kebabToCamel","char","truncate","length","ellipsis","truncatedLength","randomString","chars","charsLength","i","escapeHtml","html","entityMap","s","isValidUrl","url","isValidEmail","email","isEmptyString","ensureRpxUnit","replaceNBSP","parseJsonStr","trimmedStr","error","remove","array","unique","chunk","size","last","first","shuffle","j","sample","index","isEqual","a","b","groupBy","keyFn","appendUniversalOption","options","name","valueKey","renameTreeNodes","tree","renameMap","childKey","deleteOldKeys","node","newNode","oldKey","transformTree","transformer","children","arrayToObject","arr","K","arrayReplaceNBSP","dayjs","relativeTime","isSameOrBefore","isSameOrAfter","isBetween","createDate","date","isMillisecondTimestamp","convertToDayjsParam","formatDate","format","formatFullTime","now","parseDate","dateStr","d","diff","date1","date2","unit","add","amount","addDays","days","addMonths","months","addYears","years","getDayOfWeek","startOnMonday","day","isDateInRange","startDate","endDate","getDaysInMonth","year","month","fromNow","baseDate","startOf","endOf","formatHumanReadable","formatChatTime","daysDiff","weekdays","dayIndex","round","num","precision","factor","formatThousands","locale","formatCurrency","currency","minimumFractionDigits","maximumFractionDigits","clamp","min","max","randomInt","isEven","isOdd","percentage","total","formatNumberWithTenThousand","fractionDigits","delay","ms","resolve","getQueryStringify","params","queryString"],"mappings":"oNAiBO,SAASA,CAASC,CAAAA,CAAAA,CAA6B,CACpD,OAAO,OAAOA,CAAAA,EAAQ,QACxB,CAgBO,SAASC,CAAAA,CAASD,CAA6B,CAAA,CACpD,OAAO,OAAOA,CAAQ,EAAA,QAAA,EAAY,CAACE,CAAAA,CAAMF,CAAG,CAC9C,CAgBO,SAASG,CAAUH,CAAAA,CAAAA,CAA8B,CACtD,OAAO,OAAOA,CAAAA,EAAQ,SACxB,CAgBO,SAASI,CAAWJ,CAAAA,CAAAA,CAAgD,CACzE,OAAO,OAAOA,CAAAA,EAAQ,UACxB,CAmBO,SAASK,CAAAA,CAAQL,CAAkD,CAAA,CACxE,GAAI,CAACI,CAAWJ,CAAAA,CAAG,CACjB,CAAA,OAAO,MAGT,CAAA,IAAMM,CAAON,CAAAA,CAAAA,CAGPO,CAAUD,CAAAA,CAAAA,CAAK,QAAS,EAAA,CAC9B,GAAI,UAAA,CAAW,IAAKC,CAAAA,CAAO,EACzB,OAAO,KAAA,CAKT,IAAMC,CAAAA,CAAoBF,CAC1B,CAAA,OAAI,CAAAE,EAAAA,CAAAA,CAAkB,SAAaA,EAAAA,CAAAA,CAAkB,SAAU,CAAA,WAAA,GAAgBA,CAGzE,GAAA,CAAC,QAAS,CAAA,IAAA,CAAKA,CAAkB,CAAA,IAAA,EAAQ,EAAE,CAAA,EAM3C,iBAAkB,CAAA,IAAA,CAAKD,CAAO,CAAA,CAAA,CAMtC,CAiBO,SAASE,CAAST,CAAAA,CAAAA,CAAuC,CAC9D,OAAOA,CAAQ,GAAA,IAAA,EAAQ,OAAOA,CAAQ,EAAA,QACxC,CAgBO,SAASU,CAAQV,CAAAA,CAAAA,CAA4B,CAClD,OAAO,KAAM,CAAA,OAAA,CAAQA,CAAG,CAC1B,CAcO,SAASW,CAAOX,CAAAA,CAAAA,CAA2B,CAChD,OAAOA,CAAAA,YAAe,IACxB,CAeO,SAASY,CAAAA,CAASZ,CAA6B,CAAA,CACpD,OAAOA,CAAAA,YAAe,MACxB,CAgBO,SAASa,CAAAA,CAAmBb,CAAiC,CAAA,CAClE,OAAOS,CAAST,CAAAA,CAAG,CAAKI,EAAAA,CAAAA,CAAYJ,CAAY,CAAA,IAAI,CAAKI,EAAAA,CAAAA,CAAYJ,CAAY,CAAA,KAAK,CACxF,CAcO,SAASc,CAAAA,CAAwBd,CAAgC,CAAA,CACtE,OAAOA,CAAAA,YAAe,GACxB,CAcO,SAASe,CAAAA,CAAef,CAA6B,CAAA,CAC1D,OAAOA,CAAAA,YAAe,GACxB,CAcO,SAASgB,CAAAA,CAAShB,CAA6B,CAAA,CACpD,OAAO,OAAOA,CAAAA,EAAQ,QACxB,CAqBO,SAASiB,CAAAA,CAAYjB,CAAuB,CAAA,CACjD,OAAOA,CAAAA,GAAQ,IAAS,EAAA,OAAOA,CAAQ,EAAA,QAAA,EAAY,OAAOA,CAAAA,EAAQ,UACpE,CAeO,SAASkB,CAAYlB,CAAAA,CAAAA,CAAgC,CAC1D,OAAO,OAAOA,CAAAA,CAAQ,GACxB,CAeO,SAASmB,CAAAA,CAAOnB,CAA2B,CAAA,CAChD,OAAOA,CAAAA,GAAQ,IACjB,CAgBO,SAASoB,CAAkBpB,CAAAA,CAAAA,CAAuC,CACvE,OAAOmB,CAAOnB,CAAAA,CAAG,CAAKkB,EAAAA,CAAAA,CAAYlB,CAAG,CACvC,CAiBO,SAASqB,CAAcrB,CAAAA,CAAAA,CAAuB,CACnD,OAAOS,CAAST,CAAAA,CAAG,CAAK,EAAA,MAAA,CAAO,IAAKA,CAAAA,CAAG,CAAE,CAAA,MAAA,GAAW,CACtD,CAoBO,SAASsB,CAAAA,CAAQtB,CAAuB,CAAA,CAC7C,OAAIoB,CAAkBpB,CAAAA,CAAG,CAChB,CAAA,IAAA,CACLD,CAASC,CAAAA,CAAG,CACPA,CAAAA,CAAAA,CAAI,IAAK,EAAA,CAAE,MAAW,GAAA,CAAA,CAC3BU,CAAQV,CAAAA,CAAG,CACNA,CAAAA,CAAAA,CAAI,SAAW,CACpBS,CAAAA,CAAAA,CAAST,CAAG,CAAA,CACPqB,CAAcrB,CAAAA,CAAG,CACnB,CAAA,KACT,CAgBO,SAASE,CAAMqB,CAAAA,CAAAA,CAAyB,CAC7C,OAAO,MAAO,CAAA,KAAA,CAAMA,CAAK,CAC3B,CAkBO,SAASC,CAAAA,CAAcD,CAA8C,CAAA,CAC1E,GAAI,OAAOA,CAAU,EAAA,QAAA,EAAYA,CAAU,GAAA,IAAA,CACzC,OAAO,MAAA,CAGT,IAAME,CAAAA,CAAQ,MAAO,CAAA,cAAA,CAAeF,CAAK,CAAA,CACzC,OAAOE,CAAAA,GAAU,MAAO,CAAA,SAAA,EAAaA,CAAU,GAAA,IACjD,CAyBO,SAASC,CAAc1B,CAAAA,CAAAA,CAAuB,CAEnD,GAAI,CAACD,CAASC,CAAAA,CAAG,CAAK,EAAA,CAACC,CAASD,CAAAA,CAAG,CACjC,CAAA,OAAO,MAIT,CAAA,IAAM2B,CAAW,CAAA,MAAA,CAAO3B,CAAG,CAAA,CAM3B,OAFoB,eAAA,CAED,IAAK2B,CAAAA,CAAQ,CAClC,CC5bO,SAASC,CAAAA,CAAWC,CAAaC,CAAAA,CAAAA,CAAgC,CACtE,OAAO,MAAO,CAAA,SAAA,CAAU,cAAe,CAAA,IAAA,CAAKD,CAAKC,CAAAA,CAAI,CACvD,CAoBO,SAASC,CAAaF,CAAAA,CAAAA,CAAW,CACtC,GAAIA,CAAQ,GAAA,IAAA,EAAQ,OAAOA,CAAAA,EAAQ,QACjC,CAAA,OAAOA,CAGT,CAAA,GAAIA,CAAe,YAAA,IAAA,CACjB,OAAO,IAAI,IAAKA,CAAAA,CAAAA,CAAI,OAAQ,EAAC,CAG/B,CAAA,GAAIA,CAAe,YAAA,MAAA,CACjB,OAAO,IAAI,MAAOA,CAAAA,CAAAA,CAAI,MAAQA,CAAAA,CAAAA,CAAI,KAAK,CAGzC,CAAA,GAAI,KAAM,CAAA,OAAA,CAAQA,CAAG,CAAA,CACnB,OAAOA,CAAAA,CAAI,GAAIG,CAAAA,CAAAA,EAAQD,CAAUC,CAAAA,CAAI,CAAC,CAAA,CAGxC,IAAMC,CAAAA,CAA8B,EACpC,CAAA,OAAA,MAAA,CAAO,IAAKJ,CAAAA,CAA0B,CAAE,CAAA,OAAA,CAASK,CAAQ,EAAA,CACvDD,CAAOC,CAAAA,CAAG,CAAIH,CAAAA,CAAAA,CAAWF,CAA4BK,CAAAA,CAAG,CAAC,EAC3D,CAAC,CAEMD,CAAAA,CACT,CAoBO,SAASE,CAAaN,CAAAA,CAAAA,CAA0BO,CAAcC,CAAAA,CAAAA,CAAiC,CACpG,IAAMC,CAAOF,CAAAA,CAAAA,CAAK,KAAM,CAAA,GAAG,CACvBG,CAAAA,CAAAA,CAASV,CAEb,CAAA,IAAA,IAAWK,CAAOI,IAAAA,CAAAA,CAAM,CACtB,GAA4BC,CAAW,EAAA,IAAA,CACrC,OAAOF,CAAAA,CAETE,CAASA,CAAAA,CAAAA,CAAOL,CAAG,EACrB,CAEA,OAAQK,IAAW,MAAaF,CAAAA,CAAAA,CAAeE,CACjD,CAmBO,SAASC,CAAAA,CAAuDX,CAAQS,CAAAA,CAAAA,CAAuB,CACpG,OAAOA,CAAK,CAAA,MAAA,CAAO,CAACC,CAAAA,CAAQL,CACtBA,IAAAA,CAAAA,IAAOL,IACTU,CAAOL,CAAAA,CAAG,CAAIL,CAAAA,CAAAA,CAAIK,CAAG,CAAA,CAAA,CAEhBK,CACN,CAAA,CAAA,EAAgB,CACrB,CAiBO,SAASE,CAAuDZ,CAAAA,CAAAA,CAAQS,CAAuB,CAAA,CACpG,IAAMC,CAAS,CAAA,CAAE,GAAGV,CAAI,CACxB,CAAA,OAAAS,CAAK,CAAA,OAAA,CAASJ,CAAQ,EAAA,CACpB,OAAOK,CAAAA,CAAOL,CAAG,EACnB,CAAC,CAAA,CACMK,CACT,CAeO,SAASG,CAAAA,CAAoBb,CAAkC,CAAA,CACpE,OAAO,MAAA,CAAO,OAAQA,CAAAA,CAAG,CACtB,CAAA,MAAA,CAAO,CAAC,CAACc,CAAGpB,CAAAA,CAAK,IAA6BA,CAAU,EAAA,IAAI,CAC5D,CAAA,GAAA,CAAI,CAAC,CAACW,CAAKX,CAAAA,CAAK,CACX,GAAA,KAAA,CAAM,OAAQA,CAAAA,CAAK,CACdA,CAAAA,CAAAA,CAAM,GAAIS,CAAAA,CAAAA,EAAQ,GAAG,kBAAmBE,CAAAA,CAAG,CAAC,CAAA,CAAA,EAAI,kBAAmB,CAAA,MAAA,CAAOF,CAAI,CAAC,CAAC,CAAA,CAAE,CAAE,CAAA,IAAA,CAAK,GAAG,CAAA,CAE9F,CAAG,EAAA,kBAAA,CAAmBE,CAAG,CAAC,CAAA,CAAA,EAAI,kBAAmB,CAAA,MAAA,CAAOX,CAAK,CAAC,CAAC,CAAA,CACvE,CACA,CAAA,IAAA,CAAK,GAAG,CACb,CAeO,SAASqB,CAAwCC,CAAAA,GAAAA,CAAAA,CAAiB,CACvE,OAAO,MAAO,CAAA,MAAA,CAAO,EAAC,CAAG,GAAGA,CAAO,CACrC,CAeO,SAASC,CAAAA,CAAAA,GAAaD,CAAqD,CAAA,CAChF,IAAMN,CAAAA,CAA8B,EAEpC,CAAA,OAAAM,CAAQ,CAAA,OAAA,CAAShB,CAAQ,EAAA,CAClBL,CAAcK,CAAAA,CAAG,CAGtB,EAAA,MAAA,CAAO,IAAKA,CAAAA,CAAG,CAAE,CAAA,OAAA,CAASK,CAAQ,EAAA,CAChC,IAAMa,CAAAA,CAAclB,CAAIK,CAAAA,CAAG,CACrBc,CAAAA,CAAAA,CAAcT,CAAOL,CAAAA,CAAG,CAG1BV,CAAAA,CAAAA,CAAcuB,CAAW,CAAA,EAAKvB,CAAcwB,CAAAA,CAAW,CACzDT,CAAAA,CAAAA,CAAOL,CAAG,CAAIY,CAAAA,CAAAA,CAAUE,CAAaD,CAAAA,CAAW,CAIhDR,CAAAA,CAAAA,CAAOL,CAAG,CAAA,CAAIH,CAAUgB,CAAAA,CAAW,EAEvC,CAAC,EACH,CAAC,CAEMR,CAAAA,CACT,CAkBO,SAASU,EACdC,CAAAA,CAAAA,CACAC,CACAC,CAAAA,CAAAA,CAAqC,EAAC,CACjB,CACrB,OAAOD,CAAU,CAAA,MAAA,CAAO,CAACE,CAAAA,CAAgCnB,CAAgB,GAAA,CACvE,GAAIN,CAAWsB,CAAAA,CAAAA,CAAgBhB,CAAG,CAAA,CAAG,CACnC,IAAMoB,CAASF,CAAAA,CAAAA,CAAWlB,CAAG,CAAA,EAAKA,CAClCmB,CAAAA,CAAAA,CAAUC,CAAM,CAAA,CAAIJ,CAAehB,CAAAA,CAAG,EACxC,CACA,OAAOmB,CACT,CAAA,CAAG,EAAE,CACP,CC9OO,SAASE,EAAAA,CAAWC,CAAqB,CAAA,CAC9C,OAAIA,CAAAA,CAAI,MAAW,GAAA,CAAA,CACVA,EACFA,CAAI,CAAA,MAAA,CAAO,CAAC,CAAA,CAAE,WAAY,EAAA,CAAIA,CAAI,CAAA,KAAA,CAAM,CAAC,CAClD,CAeO,SAASC,EAAaD,CAAAA,CAAAA,CAAqB,CAEhD,OAAOA,CACJ,CAAA,OAAA,CAAQ,yBAA2B,CAAA,SAAS,CAC5C,CAAA,OAAA,CAAQ,oBAAsB,CAAA,OAAO,CACrC,CAAA,WAAA,EACL,CAeO,SAASE,EAAAA,CAAaF,CAAqB,CAAA,CAChD,OAAOA,CAAI,CAAA,OAAA,CAAQ,WAAa,CAAA,CAACb,CAAGgB,CAAAA,CAAAA,GAASA,CAAK,CAAA,WAAA,EAAa,CACjE,CAgBO,SAASC,EAASJ,CAAAA,CAAAA,CAAaK,CAAiB,CAAA,EAAA,CAAIC,EAAmB,KAAe,CAAA,CAC3F,GAAIN,CAAAA,CAAI,MAAUK,EAAAA,CAAAA,CAChB,OAAOL,CAAAA,CAGT,IAAMO,CAAAA,CAAkBF,CAASC,CAAAA,CAAAA,CAAS,MAC1C,CAAA,OAAON,CAAI,CAAA,SAAA,CAAU,EAAGO,CAAe,CAAA,CAAID,CAC7C,CAeO,SAASE,EAAAA,CAAaH,CAAgBI,CAAAA,CAAAA,CAAQ,gEAA0E,CAAA,CAC7H,IAAI1B,CAAAA,CAAS,EACP2B,CAAAA,CAAAA,CAAcD,CAAM,CAAA,MAAA,CAE1B,IAASE,IAAAA,CAAAA,CAAI,CAAGA,CAAAA,CAAAA,CAAIN,CAAQM,CAAAA,CAAAA,EAAAA,CAC1B5B,CAAU0B,EAAAA,CAAAA,CAAM,MAAO,CAAA,IAAA,CAAK,KAAM,CAAA,IAAA,CAAK,MAAO,EAAA,CAAIC,CAAW,CAAC,EAGhE,OAAO3B,CACT,CAaO,SAAS6B,EAAWC,CAAAA,CAAAA,CAAsB,CAC/C,IAAMC,CAAoC,CAAA,CACxC,GAAK,CAAA,OAAA,CACL,GAAK,CAAA,MAAA,CACL,GAAK,CAAA,MAAA,CACL,IAAK,QACL,CAAA,GAAA,CAAM,OACN,CAAA,GAAA,CAAK,QACL,CAAA,GAAA,CAAK,QACL,CAAA,GAAA,CAAK,QACP,CAAA,CAEA,OAAOD,CAAAA,CAAK,OAAQ,CAAA,aAAA,CAAeE,CAAKD,EAAAA,CAAAA,CAAUC,CAAC,CAAKA,EAAAA,CAAC,CAC3D,CAeO,SAASC,EAAAA,CAAWC,CAAsB,CAAA,CAC/C,GAAI,CACF,OAAO,CAAA,CAAQ,IAAI,GAAA,CAAIA,CAAG,CAC5B,CACM,KAAA,CACJ,OAAO,MACT,CACF,CAiBO,SAASC,EAAAA,CAAaC,CAAwB,CAAA,CAEnD,OADW,qCAAA,CACD,IAAKA,CAAAA,CAAK,CACtB,CAeO,SAASC,EAAcpB,CAAAA,CAAAA,CAAsB,CAClD,OAAOA,CAAI,CAAA,IAAA,EAAO,CAAA,MAAA,GAAW,CAC/B,CAeO,SAASqB,EAAAA,CAAc7E,CAA8B,CAAA,CAC1D,IAAMwD,CAAAA,CAAM,MAAOxD,CAAAA,CAAG,CACtB,CAAA,OAAIE,CAAMsD,CAAAA,CAAG,CACJxD,CAAAA,CAAAA,CAEF,CAAGA,EAAAA,CAAG,CACf,GAAA,CAAA,CAiBO,SAAS8E,CAAAA,CAAYtB,CAAsB,CAAA,CAChD,OAAKzD,CAASyD,CAAAA,CAAG,CAIVA,CAAAA,CAAAA,CAAI,OAAQ,CAAA,SAAA,CAAW,GAAG,CAAA,CAAE,OAAQ,CAAA,SAAA,CAAW,GAAG,CAAA,CAHhDA,CAIX,CAwCO,SAASuB,EAAAA,CAAsBvB,CAAqBnB,CAAAA,CAAAA,CAAoD,CAE7G,GAAI,CAACmB,CAAAA,CACH,OAAOnB,CAAAA,GAAiB,MAAYA,CAAAA,CAAAA,CAAe,EAAC,CAItD,GAAI,CAACtC,CAASyD,CAAAA,CAAG,EACf,OAAOnB,CAAAA,GAAiB,MAAYA,CAAAA,CAAAA,CAAemB,CAGrD,CAAA,IAAMwB,CAAaxB,CAAAA,CAAAA,CAAI,IAAK,EAAA,CAiB5B,GAAI,EAZDwB,CAAW,CAAA,UAAA,CAAW,GAAG,CAAA,EAAKA,EAAW,QAAS,CAAA,GAAG,CAElDA,EAAAA,CAAAA,CAAW,UAAW,CAAA,GAAG,CAAKA,EAAAA,CAAAA,CAAW,QAAS,CAAA,GAAG,CAErDA,EAAAA,CAAAA,CAAW,UAAW,CAAA,GAAG,CAAKA,EAAAA,CAAAA,CAAW,SAAS,GAAG,CAAA,EAEtD,kCAAmC,CAAA,IAAA,CAAKA,CAAU,CAAA,EAElD,CAAC,MAAA,CAAQ,OAAS,CAAA,MAAM,CAAE,CAAA,QAAA,CAASA,CAAU,CAAA,CAAA,CAKhD,OAAO3C,CAAAA,GAAiB,MAAYA,CAAAA,CAAAA,CAAemB,CAGrD,CAAA,GAAI,CACF,OAAO,IAAK,CAAA,KAAA,CAAMwB,CAAU,CAC9B,CACOC,MAAAA,CAAAA,CAAO,CACZ,OAAA,OAAA,CAAQ,KAAM,CAAA,sEAAA,CAAsBzB,EAAK,4BAAUyB,CAAAA,CAAK,CACjD5C,CAAAA,CAAAA,GAAiB,MAAYA,CAAAA,CAAAA,CAAemB,CACrD,CACF,CCnSO,SAAS0B,EAAUC,CAAAA,CAAAA,CAAYnD,CAAc,CAAA,CAClD,OAAOmD,CAAAA,CAAM,OAAOhB,CAAKA,EAAAA,CAAAA,GAAMnC,CAAI,CACrC,CAiBO,SAASoD,EAAUD,CAAAA,CAAAA,CAAiB,CACzC,OAAO,KAAM,CAAA,IAAA,CAAK,IAAI,GAAA,CAAIA,CAAK,CAAC,CAClC,CAkBO,SAASE,EAASF,CAAAA,CAAAA,CAAYG,CAAqB,CAAA,CACxD,GAAIA,CAAAA,EAAQ,CACV,CAAA,OAAO,CAACH,CAAK,CAEf,CAAA,IAAM5C,CAAgB,CAAA,EACtB,CAAA,IAAA,IAAS4B,CAAI,CAAA,CAAA,CAAGA,CAAIgB,CAAAA,CAAAA,CAAM,MAAQhB,CAAAA,CAAAA,EAAKmB,CACrC/C,CAAAA,CAAAA,CAAO,IAAK4C,CAAAA,CAAAA,CAAM,KAAMhB,CAAAA,CAAAA,CAAGA,CAAImB,CAAAA,CAAI,CAAC,CAGtC,CAAA,OAAO/C,CACT,CAgBO,SAASgD,EAAAA,CAAQJ,CAA2B,CAAA,CACjD,OAAOA,CAAAA,CAAM,MAAS,CAAA,CAAA,CAAIA,CAAMA,CAAAA,CAAAA,CAAM,MAAS,CAAA,CAAC,EAAI,MACtD,CAgBO,SAASK,EAAAA,CAASL,CAA2B,CAAA,CAClD,OAAOA,CAAAA,CAAM,MAAS,CAAA,CAAA,CAAIA,CAAM,CAAA,CAAC,CAAI,CAAA,MACvC,CAeO,SAASM,GAAWN,CAAiB,CAAA,CAC1C,IAAM5C,CAAAA,CAAS,CAAC,GAAG4C,CAAK,CAAA,CAExB,IAAShB,IAAAA,CAAAA,CAAI5B,CAAO,CAAA,MAAA,CAAS,CAAG4B,CAAAA,CAAAA,CAAI,CAAGA,CAAAA,CAAAA,EAAAA,CAAK,CAC1C,IAAMuB,CAAI,CAAA,IAAA,CAAK,KAAM,CAAA,IAAA,CAAK,MAAO,EAAA,EAAKvB,CAAI,CAAA,CAAA,CAAE,CAC5C,CAAA,CAAC5B,CAAO4B,CAAAA,CAAC,CAAG5B,CAAAA,CAAAA,CAAOmD,CAAC,CAAC,CAAA,CAAI,CAACnD,CAAAA,CAAOmD,CAAC,CAAA,CAAQnD,CAAO4B,CAAAA,CAAC,CAAM,EAC1D,CAEA,OAAO5B,CACT,CAiBO,SAASoD,EAAAA,CAAUR,CAA2B,CAAA,CACnD,GAAIA,CAAAA,CAAM,MAAW,GAAA,CAAA,CACnB,OACF,IAAMS,CAAQ,CAAA,IAAA,CAAK,KAAM,CAAA,IAAA,CAAK,MAAO,EAAA,CAAIT,CAAM,CAAA,MAAM,EACrD,OAAOA,CAAAA,CAAMS,CAAK,CACpB,CAoBO,SAASC,EAAWC,CAAAA,CAAAA,CAAQC,CAAiB,CAAA,CAClD,OAAID,CAAAA,CAAE,MAAWC,GAAAA,CAAAA,CAAE,MACV,CAAA,KAAA,CACFD,CAAE,CAAA,KAAA,CAAM,CAAC9D,CAAAA,CAAM4D,CAAU5D,GAAAA,CAAAA,GAAS+D,CAAEH,CAAAA,CAAK,CAAC,CACnD,CAmCO,SAASI,EAA+Cb,CAAAA,CAAAA,CAAYc,CAAuC,CAAA,CAChH,OAAOd,CAAM,CAAA,MAAA,CAAO,CAAC5C,CAAAA,CAAQP,CAAS,GAAA,CACpC,IAAME,CAAAA,CAAM+D,CAAMjE,CAAAA,CAAI,CACtB,CAAA,OAAA,CAACO,CAAOL,CAAAA,CAAG,CAAIK,CAAAA,CAAAA,CAAOL,CAAG,CAAK,EAAA,EAAI,EAAA,IAAA,CAAKF,CAAI,CAAA,CACpCO,CACT,CAAA,CAAG,EAAoB,CACzB,CA4CO,SAAS2D,EAAAA,CACdC,CACA,CAAA,CACE,KAAAC,CAAO,CAAA,OAAA,CACP,KAAA7E,CAAAA,CAAAA,CAAQ,EACR,CAAA,QAAA,CAAA8E,CAAW,CAAA,OACb,CAII,CAAA,EACC,CAAA,CACL,OAAO,CACL,CACE,CAACD,CAAI,EAAG,cACR,CAAA,CAACC,CAAQ,EAAG9E,CACd,CAAA,CACA,GAAG4E,CACL,CACF,CAmBO,SAASG,CAAAA,CACdC,CACAC,CAAAA,CAAAA,CACAC,EAAmB,UACnBC,CAAAA,CAAAA,CAAyB,IAClB,CAAA,CACP,OAAOH,CAAAA,CAAK,GAAKI,CAAAA,CAAAA,EAAS,CACxB,IAAMC,CAAU,CAAA,CAAE,GAAGD,CAAK,CAG1B,CAAA,OAAA,MAAA,CAAO,QAAQH,CAAS,CAAA,CAAE,OAAQ,CAAA,CAAC,CAACK,CAAAA,CAAQvD,CAAM,CAAA,GAAM,CAClD1B,CAAAA,CAAWgF,CAASC,CAAAA,CAAM,CAC5BD,GAAAA,CAAAA,CAAQtD,CAAM,CAAA,CAAIsD,EAAQC,CAAM,CAAA,CAC5BH,CACF,EAAA,OAAOE,CAAQC,CAAAA,CAAM,CAG3B,EAAA,CAAC,CAGG,CAAA,KAAA,CAAM,OAAQD,CAAAA,CAAAA,CAAQH,CAAQ,CAAC,CACjCG,GAAAA,CAAAA,CAAQH,CAAQ,CAAA,CAAIH,CAClBM,CAAAA,CAAAA,CAAQH,CAAQ,CAAA,CAChBD,CACAC,CAAAA,CAAAA,CACAC,CACF,CAAA,CAAA,CAGKE,CACT,CAAC,CACH,CAoBO,SAASE,CAAAA,CACdP,EACAQ,CACAN,CAAAA,CAAAA,CAAmB,UACZ,CAAA,CACP,OAAOF,CAAAA,CAAK,GAAKI,CAAAA,CAAAA,EAAS,CACxB,IAAMK,CAAWL,CAAAA,CAAAA,CAAKF,CAAQ,CAAA,CAAIK,CAAcH,CAAAA,CAAAA,CAAKF,CAAQ,CAAGM,CAAAA,CAAAA,CAAaN,CAAQ,CAAA,CAAI,EAAC,CAC1F,OAAOM,CAAAA,CAAYJ,CAAMK,CAAAA,CAAQ,CACnC,CAAC,CACH,CAqBO,SAASC,EAAAA,CACdC,EACAhF,CACmB,CAAA,CACnB,IAAMK,CAAAA,CAA4B,EAAC,CACnC,IAAWP,IAAAA,CAAAA,IAAQkF,CAAK,CAAA,CACtB,IAAMC,CAAAA,CAAInF,CAAKE,CAAAA,CAAG,CACbiF,CAAAA,CAAAA,GAEL5E,CAAO4E,CAAAA,CAAW,CAAInF,CAAAA,CAAAA,EACxB,CACA,OAAOO,CACT,CA6BO,SAAS6E,EAAAA,CACdF,CACAhF,CAAAA,CAAAA,CACK,CACL,OAAIgF,CAAI,CAAA,MAAA,CACCA,EAAI,GAAKlF,CAAAA,CAAAA,EACV,OAAOA,CAAAA,CAAKE,CAAG,CAAA,EAAM,QAEhB,CAAA,CACL,GAAGF,CAAAA,CACH,CAACE,CAAG,EAAG4C,CAAAA,CAAY9C,CAAKE,CAAAA,CAAG,CAAW,CACxC,CAEKF,CAAAA,CACR,CAGI,CAAA,EACT,CC3aAqF,CAAM,CAAA,MAAA,CAAO,OAAO,CAAA,CAQpBA,CAAM,CAAA,MAAA,CAAOC,CAAY,CAAA,CACzBD,CAAM,CAAA,MAAA,CAAOE,CAAc,CAAA,CAC3BF,CAAM,CAAA,MAAA,CAAOG,CAAa,CAAA,CAC1BH,CAAM,CAAA,MAAA,CAAOI,CAAS,CAAA,CAsBf,SAASC,EAAAA,CAAWC,CAAgC,CAAA,CACzD,OAAON,CAAMM,CAAAA,CAAI,CACnB,CAeO,SAASC,CAAAA,CAAuBrG,CAAyB,CAAA,CAC9D,GAAI,CAACtB,CAASsB,CAAAA,CAAK,CAAKA,EAAAA,CAAAA,CAAM,QAAS,EAAA,CAAE,SAAW,EAClD,CAAA,OAAO,MAET,CAAA,IAAMoG,CAAO,CAAA,IAAI,IAAKpG,CAAAA,CAAK,CAC3B,CAAA,OAAO,CAACrB,CAAAA,CAAMyH,CAAK,CAAA,OAAA,EAAS,CAC9B,CAeO,SAASE,CAAAA,CAAoBtG,CAAiB,CAAA,CAEnD,OAAIqG,CAAAA,CAAuBrG,CAAK,CAAA,CACvBA,CAILtB,CAAAA,CAAAA,CAASsB,CAAK,CAAA,EAAK,MAAOA,CAAAA,CAAK,CAAE,CAAA,MAAA,GAAW,EACvCA,CAAAA,CAAAA,CAAQ,GAGVA,CAAAA,CACT,CAeO,SAASuG,CAAWH,CAAAA,CAAAA,CAAgBI,CAAS,CAAA,YAAA,CAAsB,CACxE,OAAOV,CAAMQ,CAAAA,CAAAA,CAAoBF,CAAI,CAAC,EAAE,MAAOI,CAAAA,CAAM,CACvD,CAcO,SAASC,EAAAA,CAAeL,CAAgB,CAAA,CAC7C,OAAOG,CAAAA,CAAWH,CAAM,CAAA,qBAAqB,CAC/C,CAWO,SAASM,EAAAA,EAAc,CAC5B,OAAO,IAAA,CAAK,GAAI,EAClB,CAeO,SAASC,EAAUC,CAAAA,CAAAA,CAAuB,CAC/C,IAAMC,CAAIf,CAAAA,CAAAA,CAAMc,CAAO,CAAA,CACvB,GAAI,CAACC,EAAE,OAAQ,EAAA,CACb,MAAM,IAAI,SAAU,CAAA,4CAAS,CAC/B,CAAA,OAAOA,CAAE,CAAA,MAAA,EACX,CAgBO,SAASC,EAAAA,CAAKC,CAAiBC,CAAAA,CAAAA,CAAiBC,CAA+B,CAAA,KAAA,CAAe,CACnG,OAAOnB,CAAMQ,CAAAA,CAAAA,CAAoBS,CAAK,CAAC,CAAE,CAAA,IAAA,CAAKjB,CAAMQ,CAAAA,CAAAA,CAAoBU,CAAK,CAAC,CAAGC,CAAAA,CAAI,CACvF,CAgBO,SAASC,CAAId,CAAAA,CAAAA,CAAgBe,CAAgBF,CAAAA,CAAAA,CAAuB,KAAa,CAAA,CACtF,OAAOnB,CAAAA,CAAMQ,CAAoBF,CAAAA,CAAI,CAAC,CAAA,CAAE,GAAIe,CAAAA,CAAAA,CAAQF,CAAI,CAAE,CAAA,MAAA,EAC5D,CAcO,SAASG,EAAAA,CAAQhB,CAAgBiB,CAAAA,CAAAA,CAAoB,CAC1D,OAAOH,CAAIZ,CAAAA,CAAAA,CAAoBF,CAAI,CAAA,CAAGiB,CAAM,CAAA,KAAK,CACnD,CAeO,SAASC,EAAUlB,CAAAA,CAAAA,CAAgBmB,CAAsB,CAAA,CAC9D,OAAOL,CAAAA,CAAIZ,CAAoBF,CAAAA,CAAI,CAAGmB,CAAAA,CAAAA,CAAQ,OAAO,CACvD,CAeO,SAASC,EAASpB,CAAAA,CAAAA,CAAgBqB,CAAqB,CAAA,CAC5D,OAAOP,CAAAA,CAAIZ,CAAoBF,CAAAA,CAAI,CAAGqB,CAAAA,CAAAA,CAAO,MAAM,CACrD,CAgBO,SAASC,EAAatB,CAAAA,CAAAA,CAAgBuB,EAAgB,KAAe,CAAA,CAE1E,IAAMC,CAAAA,CADI9B,CAAMQ,CAAAA,CAAAA,CAAoBF,CAAI,CAAC,CAC3B,CAAA,GAAA,EACd,CAAA,OAAIuB,CACKC,CAAAA,CAAAA,GAAQ,CAAI,CAAA,CAAA,CAAIA,CAAM,CAAA,CAAA,CAExBA,CACT,CAgBO,SAASC,EAAAA,CAAczB,CAAgB0B,CAAAA,CAAAA,CAAqBC,CAA4B,CAAA,CAC7F,IAAMlB,CAAAA,CAAIf,CAAMQ,CAAAA,CAAAA,CAAoBF,CAAI,CAAC,EACzC,OAAOS,CAAAA,CAAE,OAAQf,CAAAA,CAAAA,CAAMQ,CAAoBwB,CAAAA,CAAS,CAAC,CAAC,CAAKjB,EAAAA,CAAAA,CAAE,QAASf,CAAAA,CAAAA,CAAMQ,CAAoByB,CAAAA,CAAO,CAAC,CAAC,CAC3G,CAeO,SAASC,EAAAA,CAAeC,CAAcC,CAAAA,CAAAA,CAAuB,CAClE,OAAOpC,CAAM,CAAA,IAAI,IAAKmC,CAAAA,CAAAA,CAAMC,CAAO,CAAA,CAAC,CAAC,CAAA,CAAE,aACzC,CAeO,SAASC,EAAAA,CAAQ/B,CAAgBgC,CAAAA,CAAAA,CAA6B,CAGnE,OAFAhC,CAAOE,CAAAA,CAAAA,CAAoBF,CAAI,CAAA,CAC/BgC,CAAWA,CAAAA,CAAAA,EAAY9B,CAAoB8B,CAAAA,CAAQ,EAC/CA,CACKtC,CAAAA,CAAAA,CAAMM,CAAI,CAAA,CAAE,IAAKN,CAAAA,CAAAA,CAAMsC,CAAQ,CAAC,CAClCtC,CAAAA,CAAAA,CAAMM,CAAI,CAAA,CAAE,OAAQ,EAC7B,CAeO,SAASiC,GAAQjC,CAAgBa,CAAAA,CAAAA,CAAwB,CAC9D,OAAOnB,CAAMQ,CAAAA,CAAAA,CAAoBF,CAAI,CAAC,CAAE,CAAA,OAAA,CAAQa,CAAI,CAAA,CAAE,MAAO,EAC/D,CAeO,SAASqB,EAAMlC,CAAAA,CAAAA,CAAgBa,CAAwB,CAAA,CAC5D,OAAOnB,CAAAA,CAAMQ,CAAoBF,CAAAA,CAAI,CAAC,CAAA,CAAE,KAAMa,CAAAA,CAAI,CAAE,CAAA,MAAA,EACtD,CAeO,SAASsB,EAAoBnC,CAAAA,CAAAA,CAAwB,CAC1D,IAAMS,CAAIf,CAAAA,CAAAA,CAAMQ,CAAoBF,CAAAA,CAAI,CAAC,CAAA,CACnCM,CAAMZ,CAAAA,CAAAA,EAEZ,CAAA,OAAIe,CAAE,CAAA,MAAA,CAAOH,EAAK,KAAK,CAAA,CACd,CAAMG,aAAAA,EAAAA,CAAAA,CAAE,MAAO,CAAA,OAAO,CAAC,CAAA,CAAA,CAC5BA,CAAE,CAAA,MAAA,CAAOH,CAAI,CAAA,QAAA,CAAS,CAAG,CAAA,KAAK,CAAG,CAAA,KAAK,EACjC,CAAMG,aAAAA,EAAAA,CAAAA,CAAE,MAAO,CAAA,OAAO,CAAC,CAAA,CAAA,CAC5BA,CAAE,CAAA,MAAA,CAAOH,CAAI,CAAA,GAAA,CAAI,CAAG,CAAA,KAAK,CAAG,CAAA,KAAK,CAC5B,CAAA,CAAA,aAAA,EAAMG,CAAE,CAAA,MAAA,CAAO,OAAO,CAAC,CAC5BA,CAAAA,CAAAA,CAAAA,CAAE,MAAOH,CAAAA,CAAAA,CAAK,MAAM,CAAA,CACfG,CAAE,CAAA,MAAA,CAAO,sBAAY,CAAA,CACvBA,CAAE,CAAA,MAAA,CAAO,gCAAiB,CACnC,CAqBO,SAAS2B,EAAAA,CAAepC,CAAwB,CAAA,CACrD,IAAMS,CAAAA,CAAIf,CAAMQ,CAAAA,CAAAA,CAAoBF,CAAI,CAAC,CACnCM,CAAAA,CAAAA,CAAMZ,CAAM,EAAA,CAGlB,GAAIe,CAAE,CAAA,MAAA,CAAOH,CAAK,CAAA,KAAK,CACrB,CAAA,OAAOG,CAAE,CAAA,MAAA,CAAO,OAAO,CAAA,CAIzB,GAAIA,CAAAA,CAAE,MAAOH,CAAAA,CAAAA,CAAI,QAAS,CAAA,CAAA,CAAG,KAAK,CAAG,CAAA,KAAK,CACxC,CAAA,OAAO,CAAMG,aAAAA,EAAAA,CAAAA,CAAE,MAAO,CAAA,OAAO,CAAC,CAAA,CAAA,CAIhC,IAAM4B,CAAAA,CAAW/B,CAAI,CAAA,IAAA,CAAKG,CAAG,CAAA,KAAK,CAClC,CAAA,GAAI4B,CAAY,EAAA,CAAA,EAAKA,CAAW,CAAA,CAAA,CAAG,CACjC,IAAMC,CAAW,CAAA,CAAC,oBAAO,CAAA,oBAAA,CAAO,oBAAO,CAAA,oBAAA,CAAO,oBAAO,CAAA,oBAAA,CAAO,oBAAK,CAC3DC,CAAAA,CAAAA,CAAW9B,CAAE,CAAA,GAAA,EACnB,CAAA,OAAO6B,CAASC,CAAAA,CAAQ,CAAK,EAAA,oBAC/B,CAGA,OAAI9B,CAAE,CAAA,MAAA,CAAOH,CAAK,CAAA,MAAM,CACfG,CAAAA,CAAAA,CAAE,MAAO,CAAA,kBAAQ,CAInBA,CAAAA,CAAAA,CAAE,MAAO,CAAA,4BAAa,CAC/B,CCxaO,SAAS+B,CAAAA,CAAMC,CAAaC,CAAAA,CAAAA,CAAY,CAAW,CAAA,CACxD,IAAMC,CAAS,CAAA,EAAA,EAAMD,CACrB,CAAA,OAAO,IAAK,CAAA,KAAA,CAAMD,CAAME,CAAAA,CAAM,CAAIA,CAAAA,CACpC,CAeO,SAASC,EAAgBH,CAAAA,CAAAA,CAAaI,CAAyB,CAAA,CACpE,OAAOJ,CAAAA,CAAI,cAAeI,CAAAA,CAAM,CAClC,CAoBO,SAASC,EAAAA,CACdlJ,CACA4E,CAAAA,CAAAA,CAKI,EAAC,CACG,CACR,GAAM,CACJ,QAAA,CAAAuE,EAAW,KACX,CAAA,MAAA,CAAAF,CAAS,CAAA,OAAA,CACT,qBAAAG,CAAAA,CAAAA,CAAwB,CACxB,CAAA,qBAAA,CAAAC,CAAwB,CAAA,CAC1B,CAAIzE,CAAAA,CAAAA,CAEJ,OAAO,IAAI,IAAK,CAAA,YAAA,CAAaqE,EAAQ,CACnC,KAAA,CAAO,UACP,CAAA,QAAA,CAAAE,CACA,CAAA,qBAAA,CAAAC,CACA,CAAA,qBAAA,CAAAC,CACF,CAAC,CAAE,CAAA,MAAA,CAAOrJ,CAAK,CACjB,CAgBO,SAASsJ,GAAMT,CAAaU,CAAAA,CAAAA,CAAaC,CAAqB,CAAA,CACnE,OAAO,IAAA,CAAK,GAAI,CAAA,IAAA,CAAK,GAAIX,CAAAA,CAAAA,CAAKU,CAAG,CAAA,CAAGC,CAAG,CACzC,CAeO,SAASC,EAAUF,CAAAA,CAAAA,CAAaC,CAAqB,CAAA,CAC1D,OAAAD,CAAAA,CAAM,IAAK,CAAA,IAAA,CAAKA,CAAG,CAAA,CACnBC,CAAM,CAAA,IAAA,CAAK,KAAMA,CAAAA,CAAG,CACb,CAAA,IAAA,CAAK,MAAM,IAAK,CAAA,MAAA,EAAYA,EAAAA,CAAAA,CAAMD,CAAM,CAAA,CAAA,CAAE,CAAIA,CAAAA,CACvD,CAeO,SAASG,CAAOb,CAAAA,CAAAA,CAAsB,CAC3C,OAAOA,CAAM,CAAA,CAAA,GAAM,CACrB,CAeO,SAASc,EAAMd,CAAAA,CAAAA,CAAsB,CAC1C,OAAO,CAACa,CAAAA,CAAOb,CAAG,CACpB,CAiBO,SAASe,EAAW5J,CAAAA,CAAAA,CAAe6J,CAAef,CAAAA,CAAAA,CAAY,EAAW,CAC9E,OAAIe,CAAU,GAAA,CAAA,CACL,CACFjB,CAAAA,CAAAA,CAAO5I,CAAQ6J,CAAAA,CAAAA,CAAS,GAAKf,CAAAA,CAAS,CAC/C,CAgBO,SAASgB,EAAAA,CAA4BjB,CAAakB,CAAAA,CAAAA,CAAiB,CAAW,CAAA,CACnF,OAAKlB,CAAAA,CAGDA,CAAO,EAAA,GAAA,CAEF,CAAIA,EAAAA,CAAAA,CAAAA,CAAM,GAAO,EAAA,OAAA,CAAQkB,CAAc,CAAC,CAGxClB,MAAAA,CAAAA,CAAAA,CAAAA,CAAI,QAAS,EAAA,CAPb,GASX,CCrLO,SAASmB,EAAMC,CAAAA,CAAAA,CAA2B,CAC/C,OAAO,IAAI,OAAA,CAAQC,CAAW,EAAA,UAAA,CAAWA,CAASD,CAAAA,CAAE,CAAC,CACvD,CCOO,SAASE,GAAkBC,CAAwD,CAAA,CACxF,GAAI,CAACA,CACH,CAAA,OAAO,EAET,CAAA,IAAMC,CAAclJ,CAAAA,CAAAA,CAAoBiJ,CAAM,CAAA,CAE9C,OAAIC,CAAAA,CACK,CAAIA,CAAAA,EAAAA,CAAW,GAEjB,EACT","file":"index.js","sourcesContent":["/**\n * @module 类型检查\n * @description 提供各种数据类型检查函数,用于判断值的类型、判断对象特性等\n */\n\n/**\n * 检查值是否为字符串类型\n * @param val 要检查的值\n * @returns 如果是字符串则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isString('hello') // true\n * isString(123) // false\n * isString(new String('hello')) // false(注意:包装对象不是原始字符串类型)\n * ```\n */\nexport function isString(val: unknown): val is string {\n return typeof val === 'string'\n}\n\n/**\n * 检查值是否为数字类型\n * @param val 要检查的值\n * @returns 如果是数字则返回 true,否则返回 false(NaN 会返回 false)\n * @group Is\n * @example\n * ```ts\n * isNumber(123) // true\n * isNumber(-45.67) // true\n * isNumber('123') // false\n * isNumber(NaN) // false\n * isNumber(Infinity) // true\n * ```\n */\nexport function isNumber(val: unknown): val is number {\n return typeof val === 'number' && !isNaN(val)\n}\n\n/**\n * 检查值是否为布尔类型\n * @param val 要检查的值\n * @returns 如果是布尔值则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isBoolean(true) // true\n * isBoolean(false) // true\n * isBoolean(0) // false\n * isBoolean('false') // false\n * isBoolean(new Boolean(true)) // false(包装对象不是原始布尔类型)\n * ```\n */\nexport function isBoolean(val: unknown): val is boolean {\n return typeof val === 'boolean'\n}\n\n/**\n * 检查值是否为函数类型\n * @param val 要检查的值\n * @returns 如果是函数则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isFunction(() => {}) // true\n * isFunction(function() {}) // true\n * isFunction(class {}) // true\n * isFunction(Math.sin) // true\n * isFunction({}) // false\n * ```\n */\nexport function isFunction(val: unknown): val is ((...args: any[]) => any) {\n return typeof val === 'function'\n}\n\n/**\n * 检查值是否为类构造函数\n * @param val 要检查的值\n * @returns 如果是类构造函数则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isClass(class User {}) // true\n * isClass(class extends Array {}) // true\n * isClass(Array) // true\n * isClass(Date) // true\n * isClass(function() {}) // false\n * isClass(() => {}) // false\n * isClass(Math.sin) // false\n * isClass({}) // false\n * ```\n */\nexport function isClass(val: unknown): val is new (...args: any[]) => any {\n if (!isFunction(val)) {\n return false\n }\n\n const func = val as (...args: any[]) => any\n\n // 检查函数的字符串表示是否以 class 开头(ES6 类)\n const funcStr = func.toString()\n if (/^class\\s/.test(funcStr)) {\n return true\n }\n\n // 检查是否为内置构造函数或传统构造函数\n // 构造函数通常有 prototype 属性,且 prototype.constructor 指向自己\n const funcWithPrototype = func as any\n if (funcWithPrototype.prototype && funcWithPrototype.prototype.constructor === funcWithPrototype) {\n // 进一步检查:尝试检测是否为箭头函数(箭头函数没有 prototype)\n // 或者是否为明显的工具函数(通过一些启发式规则)\n if (!/^[a-z]/.test(funcWithPrototype.name || '')) {\n // 如果函数名不是以小写字母开头,更可能是构造函数\n return true\n }\n\n // 检查是否为内置构造函数\n if (/\\[native code\\]/.test(funcStr)) {\n return true\n }\n }\n\n return false\n}\n\n/**\n * 检查值是否为对象类型(不包括 null)\n * @param val 要检查的值\n * @returns 如果是对象则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isObject({}) // true\n * isObject([]) // true(数组也是对象)\n * isObject(new Date()) // true\n * isObject(null) // false\n * isObject(undefined) // false\n * isObject(123) // false\n * ```\n */\nexport function isObject(val: unknown): val is Record<any, any> {\n return val !== null && typeof val === 'object'\n}\n\n/**\n * 检查值是否为数组类型\n * @param val 要检查的值\n * @returns 如果是数组则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isArray([]) // true\n * isArray([1, 2, 3]) // true\n * isArray(new Array()) // true\n * isArray({}) // false\n * isArray('abc') // false\n * ```\n */\nexport function isArray(val: unknown): val is any[] {\n return Array.isArray(val)\n}\n\n/**\n * 检查值是否为日期类型\n * @param val 要检查的值\n * @returns 如果是日期对象则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isDate(new Date()) // true\n * isDate(Date.now()) // false(时间戳是数字,不是日期对象)\n * isDate('2023-05-15') // false(日期字符串不是日期对象)\n * ```\n */\nexport function isDate(val: unknown): val is Date {\n return val instanceof Date\n}\n\n/**\n * 检查值是否为正则表达式\n * @param val 要检查的值\n * @returns 如果是正则表达式则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isRegExp(/abc/) // true\n * isRegExp(new RegExp('abc')) // true\n * isRegExp('/abc/') // false(字符串不是正则表达式)\n * isRegExp({}) // false\n * ```\n */\nexport function isRegExp(val: unknown): val is RegExp {\n return val instanceof RegExp\n}\n\n/**\n * 检查值是否为 Promise\n * @param val 要检查的值\n * @returns 如果是 Promise 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isPromise(Promise.resolve()) // true\n * isPromise(new Promise(() => {})) // true\n * isPromise({ then: () => {}, catch: () => {} }) // true(类 Promise 对象也会返回 true)\n * isPromise({}) // false\n * isPromise(null) // false\n * ```\n */\nexport function isPromise<T = any>(val: unknown): val is Promise<T> {\n return isObject(val) && isFunction((val as any).then) && isFunction((val as any).catch)\n}\n\n/**\n * 检查值是否为 Map\n * @param val 要检查的值\n * @returns 如果是 Map 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isMap(new Map()) // true\n * isMap(new WeakMap()) // false\n * isMap({}) // false\n * ```\n */\nexport function isMap<K = any, V = any>(val: unknown): val is Map<K, V> {\n return val instanceof Map\n}\n\n/**\n * 检查值是否为 Set\n * @param val 要检查的值\n * @returns 如果是 Set 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isSet(new Set()) // true\n * isSet(new WeakSet()) // false\n * isSet([]) // false\n * ```\n */\nexport function isSet<T = any>(val: unknown): val is Set<T> {\n return val instanceof Set\n}\n\n/**\n * 检查值是否为 Symbol\n * @param val 要检查的值\n * @returns 如果是 Symbol 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isSymbol(Symbol('foo')) // true\n * isSymbol(Symbol.for('bar')) // true\n * isSymbol('symbol') // false\n * ```\n */\nexport function isSymbol(val: unknown): val is symbol {\n return typeof val === 'symbol'\n}\n\n/**\n * 检查值是否为原始类型(string、number、boolean、symbol、bigint、null、undefined)\n * @param val 要检查的值\n * @returns 如果是原始类型则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isPrimitive('hello') // true\n * isPrimitive(123) // true\n * isPrimitive(true) // true\n * isPrimitive(Symbol()) // true\n * isPrimitive(null) // true\n * isPrimitive(undefined) // true\n * isPrimitive(BigInt(123)) // true\n * isPrimitive({}) // false\n * isPrimitive([]) // false\n * isPrimitive(() => {}) // false\n * ```\n */\nexport function isPrimitive(val: unknown): boolean {\n return val === null || (typeof val !== 'object' && typeof val !== 'function')\n}\n\n/**\n * 检查值是否为 undefined\n * @param val 要检查的值\n * @returns 如果是 undefined 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isUndefined(undefined) // true\n * isUndefined(void 0) // true\n * isUndefined(null) // false\n * isUndefined('') // false\n * ```\n */\nexport function isUndefined(val: unknown): val is undefined {\n return typeof val === 'undefined'\n}\n\n/**\n * 检查值是否为 null\n * @param val 要检查的值\n * @returns 如果是 null 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isNull(null) // true\n * isNull(undefined) // false\n * isNull(0) // false\n * isNull('') // false\n * ```\n */\nexport function isNull(val: unknown): val is null {\n return val === null\n}\n\n/**\n * 检查值是否为 null 或 undefined\n * @param val 要检查的值\n * @returns 如果是 null 或 undefined 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isNullOrUndefined(null) // true\n * isNullOrUndefined(undefined) // true\n * isNullOrUndefined(void 0) // true\n * isNullOrUndefined('') // false\n * isNullOrUndefined(0) // false\n * ```\n */\nexport function isNullOrUndefined(val: unknown): val is null | undefined {\n return isNull(val) || isUndefined(val)\n}\n\n/**\n * 检查对象是否为空对象(没有自身可枚举属性)\n * @param val 要检查的值\n * @returns 如果是空对象则返回 true,否则返回 false;如果不是对象类型则返回 false\n * @group Is\n * @example\n * ```ts\n * isEmptyObject({}) // true\n * isEmptyObject({ a: 1 }) // false\n * isEmptyObject([]) // true(空数组也会返回 true)\n * isEmptyObject(null) // false(不是对象)\n * isEmptyObject(Object.create(null)) // true\n * isEmptyObject(Object.create({ toString: () => '' })) // true(不包括继承的属性)\n * ```\n */\nexport function isEmptyObject(val: unknown): boolean {\n return isObject(val) && Object.keys(val).length === 0\n}\n\n/**\n * 检查值是否为空(null、undefined、空字符串、空数组或空对象)\n * @param val 要检查的值\n * @returns 如果是空值则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isEmpty(null) // true\n * isEmpty(undefined) // true\n * isEmpty('') // true\n * isEmpty(' ') // true(空白字符串也视为空)\n * isEmpty([]) // true\n * isEmpty({}) // true\n * isEmpty(0) // false\n * isEmpty(false) // false\n * isEmpty(' hello ') // false\n * ```\n */\nexport function isEmpty(val: unknown): boolean {\n if (isNullOrUndefined(val))\n return true\n if (isString(val))\n return val.trim().length === 0\n if (isArray(val))\n return val.length === 0\n if (isObject(val))\n return isEmptyObject(val)\n return false\n}\n\n/**\n * 检查值是否为 NaN\n * @param value 要检查的值\n * @returns 如果值是 NaN 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isNaN(NaN) // true\n * isNaN(Number('abc')) // true\n * isNaN(0 / 0) // true\n * isNaN(123) // false\n * isNaN('123') // false(不同于全局 isNaN,此函数不会先尝试转换为数字)\n * ```\n */\nexport function isNaN(value: unknown): boolean {\n return Number.isNaN(value)\n}\n\n/**\n * 检查值是否为普通对象(由 Object 构造函数创建或对象字面量)\n *\n * @param {unknown} value - 要检查的值\n * @returns {boolean} 如果值是普通对象则返回 true,否则返回 false\n *\n * @group Is\n * @example\n * ```ts\n * isPlainObject({}) // true\n * isPlainObject({ a: 1 }) // true\n * isPlainObject(new Date()) // false\n * isPlainObject([]) // false\n * isPlainObject(null) // false\n * ```\n */\nexport function isPlainObject(value: unknown): value is Record<string, any> {\n if (typeof value !== 'object' || value === null) {\n return false\n }\n\n const proto = Object.getPrototypeOf(value)\n return proto === Object.prototype || proto === null\n}\n\n/**\n * 检查值是否为有效的手机号码\n * @param val 要检查的值\n * @returns 如果是有效的手机号码则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isMobilePhone('13812345678') // true\n * isMobilePhone(13812345678) // true\n * isMobilePhone('15987654321') // true\n * isMobilePhone(15987654321) // true\n * isMobilePhone('18600000000') // true\n * isMobilePhone(18600000000) // true\n * isMobilePhone('12345678901') // false(不是有效号段)\n * isMobilePhone(12345678901) // false(不是有效号段)\n * isMobilePhone('1381234567') // false(长度不对)\n * isMobilePhone(1381234567) // false(长度不对)\n * isMobilePhone('138123456789') // false(长度不对)\n * isMobilePhone(138123456789) // false(长度不对)\n * isMobilePhone('') // false\n * isMobilePhone(null) // false\n * ```\n */\nexport function isMobilePhone(val: unknown): boolean {\n // 如果既不是字符串也不是数字,直接返回 false\n if (!isString(val) && !isNumber(val)) {\n return false\n }\n\n // 转换为字符串进行验证\n const phoneStr = String(val)\n\n // 中国大陆手机号码正则表达式\n // 11位数字,以1开头,第二位是3-9的数字\n const mobileRegex = /^1[3-9]\\d{9}$/\n\n return mobileRegex.test(phoneStr)\n}\n","/**\n * @module 对象操作\n * @description 提供各种对象处理函数,包括对象深拷贝、合并、转换等实用功能\n */\n\nimport { isPlainObject } from '../is'\n\n/**\n * 检查对象是否具有指定的自有属性。\n *\n * @param {object} obj - 需要检查的对象\n * @param {string | symbol} prop - 需要检查的属性键\n * @returns {boolean} 如果对象具有该自有属性,则返回 true,否则返回 false\n * @group Object\n * @example\n * ```ts\n * const obj = { name: 'Tom', age: 25 }\n * hasOwnProp(obj, 'name') // true\n * hasOwnProp(obj, 'toString') // false,toString 是继承的属性\n * ```\n */\nexport function hasOwnProp(obj: object, prop: string | symbol): boolean {\n return Object.prototype.hasOwnProperty.call(obj, prop)\n}\n\n/**\n * 深拷贝对象,支持基本类型、数组、对象、日期和正则表达式\n * @param obj 要拷贝的对象\n * @returns 深拷贝后的对象,与原对象完全独立\n * @group Object\n * @example\n * ```ts\n * const original = { a: 1, b: { c: 2 }, d: [1, 2, 3], e: new Date() }\n * const copy = deepClone(original)\n * copy.b.c = 100\n * console.log(original.b.c) // 2,原对象不受影响\n *\n * // 支持日期对象\n * const date = new Date()\n * const clonedDate = deepClone(date)\n * console.log(date === clonedDate) // false,不是同一引用\n * ```\n */\nexport function deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== 'object') {\n return obj\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime()) as unknown as T\n }\n\n if (obj instanceof RegExp) {\n return new RegExp(obj.source, obj.flags) as unknown as T\n }\n\n if (Array.isArray(obj)) {\n return obj.map(item => deepClone(item)) as unknown as T\n }\n\n const cloned: Record<string, any> = {}\n Object.keys(obj as Record<string, any>).forEach((key) => {\n cloned[key] = deepClone((obj as Record<string, any>)[key])\n })\n\n return cloned as T\n}\n\n/**\n * 从对象中获取指定路径的值,支持点分隔的嵌套路径\n * @param obj 源对象\n * @param path 属性路径,如 'user.address.street'\n * @param defaultValue 默认值,当路径不存在时返回\n * @returns 路径对应的值,如果路径不存在则返回默认值\n * @group Object\n * @example\n * ```ts\n * const obj = { user: { profile: { name: 'Tom', age: 25 }, roles: ['admin'] } }\n *\n * get(obj, 'user.profile.name') // 'Tom'\n * get(obj, 'user.roles.0') // 'admin'\n * get(obj, 'user.settings', { theme: 'dark' }) // { theme: 'dark' }(路径不存在,返回默认值)\n * get(obj, 'user.profile.gender') // undefined(路径不存在且没提供默认值)\n * get(obj, 'user.profile.gender', 'unknown') // 'unknown'(使用默认值)\n * ```\n */\nexport function get<T = any>(obj: Record<string, any>, path: string, defaultValue?: T): T | undefined {\n const keys = path.split('.')\n let result = obj\n\n for (const key of keys) {\n if (result === undefined || result === null) {\n return defaultValue\n }\n result = result[key]\n }\n\n return (result === undefined) ? defaultValue : result as T\n}\n\n/**\n * 从对象中选择指定的属性,创建一个新对象\n * @param obj 原始对象\n * @param keys 要选择的键数组\n * @returns 包含指定键的新对象\n * @group Object\n * @example\n * ```ts\n * @group Object\n * const user = { id: 1, name: 'Tom', age: 25, email: 'tom@example.com' }\n *\n * @group Object\n * pick(user, ['name', 'email']) // { name: 'Tom', email: 'tom@example.com' }\n * pick(user, ['name', 'gender']) // { name: 'Tom' }(不存在的键会被忽略)\n * pick(user, []) // {}(空数组返回空对象)\n * ```\n */\nexport function pick<T extends Record<string, any>, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {\n return keys.reduce((result, key) => {\n if (key in obj) {\n result[key] = obj[key]\n }\n return result\n }, {} as Pick<T, K>)\n}\n\n/**\n * 从对象中排除指定的属性,创建一个新对象\n * @param obj 原始对象\n * @param keys 要排除的键数组\n * @returns 不包含指定键的新对象\n * @group Object\n * @example\n * ```ts\n * const user = { id: 1, name: 'Tom', age: 25, password: '123456' }\n *\n * omit(user, ['password']) // { id: 1, name: 'Tom', age: 25 }\n * omit(user, ['id', 'age']) // { name: 'Tom', password: '123456' }\n * omit(user, ['nonExistent']) // 原对象的拷贝,不存在的键会被忽略\n * ```\n */\nexport function omit<T extends Record<string, any>, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {\n const result = { ...obj }\n keys.forEach((key) => {\n delete result[key]\n })\n return result\n}\n\n/**\n * 将对象转换为 URL 查询字符串\n * @param obj 要转换的对象\n * @returns 格式化后的查询字符串(不包含前导?)\n * @group Object\n * @example\n * ```ts\n * objectToQueryString({ name: 'Tom', age: 25 }) // 'name=Tom&age=25'\n * objectToQueryString({ search: 'hello world', page: 1 }) // 'search=hello%20world&page=1'\n * objectToQueryString({ tags: ['js', 'ts'] }) // 'tags=js&tags=ts'\n * objectToQueryString({ name: 'Tom', empty: null }) // 'name=Tom'(null 和 undefined 会被过滤)\n * ```\n */\nexport function objectToQueryString(obj: Record<string, any>): string {\n return Object.entries(obj)\n .filter(([_, value]) => value !== undefined && value !== null)\n .map(([key, value]) => {\n if (Array.isArray(value)) {\n return value.map(item => `${encodeURIComponent(key)}=${encodeURIComponent(String(item))}`).join('&')\n }\n return `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`\n })\n .join('&')\n}\n\n/**\n * 合并多个对象,后面的对象的属性会覆盖前面的\n * @param objects 要合并的对象数组\n * @returns 合并后的新对象\n * @group Object\n * @example\n * ```ts\n * merge({ a: 1 }, { b: 2 }) // { a: 1, b: 2 }\n * merge({ a: 1 }, { a: 2, b: 2 }) // { a: 2, b: 2 }(后面对象的属性会覆盖前面的)\n * merge({ a: { x: 1 } }, { a: { y: 2 } }) // { a: { y: 2 } }(不是深度合并)\n * merge({}, { a: 1 }, { b: 2 }, { c: 3 }) // { a: 1, b: 2, c: 3 }\n * ```\n */\nexport function merge<T extends Record<string, any>>(...objects: T[]): T {\n return Object.assign({}, ...objects)\n}\n\n/**\n * 深度合并多个对象,会递归合并嵌套对象\n * @param objects 要合并的对象数组\n * @returns 深度合并后的新对象\n * @group Object\n * @example\n * ```ts\n * deepMerge({ a: { x: 1 } }, { a: { y: 2 } }) // { a: { x: 1, y: 2 } }\n * deepMerge({ a: 1 }, { b: 2 }) // { a: 1, b: 2 }\n * deepMerge({ a: [1, 2] }, { a: [3, 4] }) // { a: [3, 4] }(数组会被替换,不会合并)\n * deepMerge({}, { a: null }, { a: { b: 2 } }) // { a: { b: 2 } }(null 会被后面的对象覆盖)\n * ```\n */\nexport function deepMerge(...objects: Record<string, any>[]): Record<string, any> {\n const result: Record<string, any> = {}\n\n objects.forEach((obj) => {\n if (!isPlainObject(obj))\n return\n\n Object.keys(obj).forEach((key) => {\n const sourceValue = obj[key]\n const targetValue = result[key]\n\n // 如果两个值都是对象,就递归合并\n if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {\n result[key] = deepMerge(targetValue, sourceValue)\n }\n else {\n // 否则直接覆盖\n result[key] = deepClone(sourceValue)\n }\n })\n })\n\n return result\n}\n\n/**\n * 根据传入的键数组和键名映射对象过滤并重命名对象,返回一个新对象\n * @param originalObject 要过滤的原始对象\n * @param keysArray 要保留的键名数组\n * @param keyMapping 可选的键名映射对象,格式为 { 原键名: 新键名 }\n * @returns 返回一个新对象,其中只包含原对象中匹配的键值对,并根据映射重命名键\n * @group Object\n * @example\n * ```ts\n * const originalObject = { name: \"John\", age: 30, gender: \"male\", country: \"USA\" }\n * const keysToFilter = [\"name\", \"country\"]\n * const keyMapping = { name: \"fullName\", country: \"location\" }\n * const result = filterObjectByKeys(originalObject, keysToFilter, keyMapping)\n * // 结果: { fullName: 'John', location: 'USA' }\n * ```\n */\nexport function filterObjectByKeys(\n originalObject: Record<string, any>,\n keysArray: string[],\n keyMapping: Record<string, string> = {},\n): Record<string, any> {\n return keysArray.reduce((newObject: Record<string, any>, key: string) => {\n if (hasOwnProp(originalObject, key)) {\n const newKey = keyMapping[key] || key // 如果有映射,就用新键名,否则用原键名\n newObject[newKey] = originalObject[key]\n }\n return newObject\n }, {})\n}\n","/**\n * @module 字符串处理\n * @description 提供各种字符串处理函数,包括字符串转换、格式化、验证等实用功能\n */\n\nimport { isNaN, isString } from '../is'\n\n/**\n * 将字符串首字母转为大写\n * @param str 输入字符串\n * @returns 首字母大写后的字符串\n * @group String\n * @example\n * ```ts\n * capitalize('hello') // 'Hello'\n * capitalize('') // ''\n * capitalize('a') // 'A'\n * ```\n */\nexport function capitalize(str: string): string {\n if (str.length === 0)\n return str\n return str.charAt(0).toUpperCase() + str.slice(1)\n}\n\n/**\n * 将驼峰命名转换为短横线命名(kebab-case)\n * @param str 驼峰命名的字符串\n * @returns 短横线命名的字符串\n * @group String\n * @example\n * ```ts\n * camelToKebab('helloWorld') // 'hello-world'\n * camelToKebab('HelloWorld') // 'hello-world'\n * camelToKebab('APIVersion') // 'api-version'\n * camelToKebab('iOS9App') // 'i-os9-app'\n * ```\n */\nexport function camelToKebab(str: string): string {\n // 处理首字母大写的情况,如 APIVersion -> api-version\n return str\n .replace(/([A-Z])([A-Z]+)([A-Z])/g, '$1$2-$3') // 处理连续大写字母\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .toLowerCase()\n}\n\n/**\n * 将短横线命名(kebab-case)转换为驼峰命名(camelCase)\n * @param str 短横线命名的字符串\n * @returns 驼峰命名的字符串\n * @group String\n * @example\n * ```ts\n * kebabToCamel('hello-world') // 'helloWorld'\n * kebabToCamel('api-version') // 'apiVersion'\n * kebabToCamel('i-os9-app') // 'iOs9App'\n * kebabToCamel('--hello') // 'hello'\n * ```\n */\nexport function kebabToCamel(str: string): string {\n return str.replace(/-([a-z])/g, (_, char) => char.toUpperCase())\n}\n\n/**\n * 截取字符串并添加省略号\n * @param str 原始字符串\n * @param length 截取的最大长度,默认为 50\n * @param ellipsis 省略号字符,默认为'...'\n * @returns 截取后的字符串,如果原字符串长度小于等于截取长度,则返回原字符串\n * @group String\n * @example\n * ```ts\n * truncate('这是一个很长的字符串', 5) // '这是...'\n * truncate('这是一个很长的字符串', 5, '…') // '这是一…'\n * truncate('短字符', 10) // '短字符'\n * ```\n */\nexport function truncate(str: string, length: number = 50, ellipsis: string = '...'): string {\n if (str.length <= length)\n return str\n\n // 计算需要保留的字符数 = 总长度 - 省略号长度\n const truncatedLength = length - ellipsis.length\n return str.substring(0, truncatedLength) + ellipsis\n}\n\n/**\n * 生成指定长度的随机字符串\n * @param length 字符串长度\n * @param chars 可选的字符集,默认包含大小写字母和数字\n * @returns 生成的随机字符串\n * @group String\n * @example\n * ```ts\n * randomString(5) // 例如: 'aB9cD'\n * randomString(10) // 例如: 'aBcD1eF2gH'\n * randomString(3, '123456') // 例如: '426'\n * ```\n */\nexport function randomString(length: number, chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'): string {\n let result = ''\n const charsLength = chars.length\n\n for (let i = 0; i < length; i++) {\n result += chars.charAt(Math.floor(Math.random() * charsLength))\n }\n\n return result\n}\n\n/**\n * 将字符串中的 HTML 特殊字符转义,防止 XSS 攻击\n * @param html 包含 HTML 的字符串\n * @returns 转义后的安全字符串\n * @group String\n * @example\n * ```ts\n * escapeHtml('<div>Hello & World</div>') // '&lt;div&gt;Hello &amp; World&lt;/div&gt;'\n * escapeHtml('<script>alert(\"XSS\")</script>') // '&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;'\n * ```\n */\nexport function escapeHtml(html: string): string {\n const entityMap: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n '\\'': '&#39;',\n '/': '&#x2F;',\n '`': '&#x60;',\n '=': '&#x3D;',\n }\n\n return html.replace(/[&<>\"'`=/]/g, s => entityMap[s] || s)\n}\n\n/**\n * 检查字符串是否为有效的 URL\n * @param url 要检查的 URL 字符串\n * @returns 如果是有效的 URL 则返回 true,否则返回 false\n * @group String\n * @example\n * ```ts\n * isValidUrl('https://example.com') // true\n * isValidUrl('http://localhost:3000') // true\n * isValidUrl('example.com') // false,缺少协议\n * isValidUrl('not a url') // false\n * ```\n */\nexport function isValidUrl(url: string): boolean {\n try {\n return Boolean(new URL(url))\n }\n catch {\n return false\n }\n}\n\n/**\n * 检查字符串是否为有效的电子邮件地址\n * @param email 要检查的电子邮件地址\n * @returns 如果是有效的电子邮件地址则返回 true,否则返回 false\n * @group String\n * @example\n * ```ts\n * @group String\n * isValidEmail('user@example.com') // true\n * @group String\n * isValidEmail('user.name+tag@example.co.uk') // true\n * isValidEmail('invalid@email') // false,缺少顶级域名\n * isValidEmail('not an email') // false\n * ```\n */\nexport function isValidEmail(email: string): boolean {\n const re = /^[\\w.%+-]+@[a-z0-9.-]+\\.[a-z]{2,}$/i\n return re.test(email)\n}\n\n/**\n * 检查字符串是否为空或只包含空白字符\n * @param str 要检查的字符串\n * @returns 如果字符串为空或只包含空白字符,则返回 true\n * @group String\n * @example\n * ```ts\n * isEmptyString('') // true\n * isEmptyString(' \\t\\n ') // true\n * isEmptyString('hello') // false\n * isEmptyString(' hello ') // false\n * ```\n */\nexport function isEmptyString(str: string): boolean {\n return str.trim().length === 0\n}\n\n/**\n * 确保值具有 rpx 单位,主要用于小程序/uni-app 样式处理\n * @param val 需要转化的值,可以是数字或字符串\n * @returns 转化后带有 rpx 单位的字符串,如果输入不是数字则原样返回\n * @group String\n * @example\n * ```ts\n * ensureRpxUnit(100) // '100rpx'\n * ensureRpxUnit('100') // '100rpx'\n * ensureRpxUnit('100px') // '100px',不是数字,保持原样\n * ensureRpxUnit('auto') // 'auto',不是数字,保持原样\n * ```\n */\nexport function ensureRpxUnit(val: string | number): string {\n const str = Number(val)\n if (isNaN(str)) {\n return val as string\n }\n return `${val}rpx`\n}\n\n/**\n * 替换字符串中的 `&nbsp;` 为不换行空格\n *\n * @param {string} str - 字符串\n * @returns {string} 处理后的字符串\n *\n * @group String\n * @example\n * ```ts\n * // 基本用法\n * replaceNBSP('John&nbsp;Doe') // 'John Doe'\n * replaceNBSP('Jane&nbsp;&nbsp;Smith') // 'Jane Smith'\n * replaceNBSP('Smith') // 'Smith'\n * ```\n */\nexport function replaceNBSP(str?: string): string {\n if (!isString(str)) {\n return str as any // 应该忽略非字符串字段\n }\n\n return str.replace(/&nbsp;/g, ' ').replace(/\\u00A0/g, ' ')\n}\n\n/**\n * 解析JSON字符串,处理异常情况\n *\n * 支持解析各种有效的JSON格式,包括对象、数组、字符串、数字、布尔值等。\n * 如果解析失败,会在控制台输出错误信息并返回原始字符串。\n *\n * @param str 待解析的字符串\n * @param defaultValue 解析失败时的默认返回值,默认为原始字符串\n * @returns 解析后的对象、数组或其他JSON值,解析失败时返回默认值\n * @group String\n * @example\n * ```ts\n * // 解析对象\n * parseJsonStr('{\"name\": \"Tom\", \"age\": 25}') // { name: 'Tom', age: 25 }\n *\n * // 解析数组\n * parseJsonStr('[1, 2, 3]') // [1, 2, 3]\n *\n * // 解析基础类型\n * parseJsonStr('\"hello\"') // 'hello'\n * parseJsonStr('123') // 123\n * parseJsonStr('true') // true\n *\n * // 处理无效JSON\n * parseJsonStr('{invalid json}') // '{invalid json}'(返回原字符串)\n *\n * // 处理空值\n * parseJsonStr('') // {}\n * parseJsonStr(null) // {}\n * parseJsonStr(undefined) // {}\n *\n * // 自定义默认值\n * parseJsonStr('invalid', null) // null\n *\n * // 非JSON格式的字符串\n * parseJsonStr('hello world') // 'hello world'\n * ```\n */\nexport function parseJsonStr<T = any>(str?: string | null, defaultValue?: T): T | string | Record<string, any> {\n // 处理空值情况\n if (!str) {\n return defaultValue !== undefined ? defaultValue : {}\n }\n\n // 确保输入是字符串\n if (!isString(str)) {\n return defaultValue !== undefined ? defaultValue : str as any\n }\n\n const trimmedStr = str.trim()\n\n // 检查是否可能是JSON格式\n const isLikelyJson = (\n // 对象格式\n (trimmedStr.startsWith('{') && trimmedStr.endsWith('}'))\n // 数组格式\n || (trimmedStr.startsWith('[') && trimmedStr.endsWith(']'))\n // 字符串格式\n || (trimmedStr.startsWith('\"') && trimmedStr.endsWith('\"'))\n // 数字格式(包括负数、小数、科学计数法)\n || /^-?\\d+(?:\\.\\d+)?(?:e[+-]?\\d+)?$/i.test(trimmedStr)\n // 布尔值和null\n || ['true', 'false', 'null'].includes(trimmedStr)\n )\n\n // 如果不像JSON格式,直接返回原字符串\n if (!isLikelyJson) {\n return defaultValue !== undefined ? defaultValue : str\n }\n\n try {\n return JSON.parse(trimmedStr) as T\n }\n catch (error) {\n console.error('解析 JSON 失败: 输入字符串为', str, '错误详情: ', error)\n return defaultValue !== undefined ? defaultValue : str\n }\n}\n","/**\n * @module 数组操作\n * @description 提供各种数组处理函数,包括数组转换、查询、过滤等实用功能。这些函数都不会修改原始数组,而是返回新的数组或值。\n */\n\nimport { hasOwnProp } from '../object'\nimport { replaceNBSP } from '../string'\n\n/**\n * 从数组中移除指定的项\n * @param array 原始数组\n * @param item 要移除的项\n * @returns 移除指定项后的新数组,不会修改原始数组\n * @group Array\n * @example\n * ```ts\n * const numbers = [1, 2, 3, 4, 5]\n * const newArray = remove(numbers, 3) // [1, 2, 4, 5]\n * console.log(numbers) // 原数组不变: [1, 2, 3, 4, 5]\n *\n * // 移除字符串\n * const fruits = ['苹果', '香蕉', '橙子']\n * const newFruits = remove(fruits, '香蕉') // ['苹果', '橙子']\n * ```\n */\nexport function remove<T>(array: T[], item: T): T[] {\n return array.filter(i => i !== item)\n}\n\n/**\n * 数组去重,移除数组中的重复元素\n * @param array 原始数组\n * @returns 去重后的新数组,保持原始顺序\n * @group Array\n * @example\n * ```ts\n * const numbers = [1, 2, 2, 3, 3, 4, 5, 5]\n * const uniqueArray = unique(numbers) // [1, 2, 3, 4, 5]\n *\n * // 对象数组会基于引用去重,内容相同但引用不同的对象会被视为不同元素\n * const objArray = [{id: 1}, {id: 2}, {id: 1}]\n * const uniqueObjArray = unique(objArray) // [{id: 1}, {id: 2}, {id: 1}]\n * ```\n */\nexport function unique<T>(array: T[]): T[] {\n return Array.from(new Set(array))\n}\n\n/**\n * 将数组分成指定大小的块\n * @param array 原始数组\n * @param size 每个块的大小,必须为正整数\n * @returns 二维数组,每个子数组最多包含 size 个元素\n * @group Array\n * @example\n * ```ts\n * const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n * const chunks = chunk(numbers, 3)\n * // 结果: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]\n *\n * // 如果 size 小于等于 0,则返回包含原始数组的数组\n * chunk(numbers, 0) // [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]\n * ```\n */\nexport function chunk<T>(array: T[], size: number): T[][] {\n if (size <= 0)\n return [array]\n\n const result: T[][] = []\n for (let i = 0; i < array.length; i += size) {\n result.push(array.slice(i, i + size))\n }\n\n return result\n}\n\n/**\n * 获取数组中的最后一个元素\n * @param array 数组\n * @returns 最后一个元素,如果数组为空则返回 undefined\n * @group Array\n * @example\n * ```ts\n * const numbers = [1, 2, 3, 4, 5]\n * const lastNumber = last(numbers) // 5\n *\n * const empty = []\n * const lastEmpty = last(empty) // undefined\n * ```\n */\nexport function last<T>(array: T[]): T | undefined {\n return array.length > 0 ? array[array.length - 1] : undefined\n}\n\n/**\n * 获取数组中的第一个元素\n * @param array 数组\n * @returns 第一个元素,如果数组为空则返回 undefined\n * @group Array\n * @example\n * ```ts\n * const numbers = [1, 2, 3, 4, 5]\n * const firstNumber = first(numbers) // 1\n *\n * const empty = []\n * const firstEmpty = first(empty) // undefined\n * ```\n */\nexport function first<T>(array: T[]): T | undefined {\n return array.length > 0 ? array[0] : undefined\n}\n\n/**\n * 随机打乱数组元素顺序\n * @param array 原始数组\n * @returns 打乱后的新数组,不会修改原始数组\n * @group Array\n * @example\n * ```ts\n * const numbers = [1, 2, 3, 4, 5]\n * const shuffled = shuffle(numbers)\n * // 可能的结果: [3, 1, 5, 2, 4]\n * console.log(numbers) // 原数组不变: [1, 2, 3, 4, 5]\n * ```\n */\nexport function shuffle<T>(array: T[]): T[] {\n const result = [...array]\n\n for (let i = result.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [result[i], result[j]] = [result[j] as T, result[i] as T]\n }\n\n return result\n}\n\n/**\n * 从数组中随机选择一个元素\n * @param array 数组\n * @returns 随机选择的元素,如果数组为空则返回 undefined\n * @group Array\n * @example\n * ```ts\n * const fruits = ['苹果', '香蕉', '橙子', '梨', '葡萄']\n * const randomFruit = sample(fruits)\n * // 可能返回: '香蕉'\n *\n * const empty = []\n * const emptyResult = sample(empty) // undefined\n * ```\n */\nexport function sample<T>(array: T[]): T | undefined {\n if (array.length === 0)\n return undefined\n const index = Math.floor(Math.random() * array.length)\n return array[index]\n}\n\n/**\n * 比较两个数组是否相等(元素顺序和值都相同)\n * @param a 第一个数组\n * @param b 第二个数组\n * @returns 如果两个数组长度相同且对应位置的元素全部相等则返回 true,否则返回 false\n * @group Array\n * @example\n * ```ts\n * isEqual([1, 2, 3], [1, 2, 3]) // true\n * isEqual([1, 2, 3], [1, 3, 2]) // false,顺序不同\n * isEqual([1, 2, 3], [1, 2]) // false,长度不同\n *\n * // 对象比较是基于引用的\n * const obj = { id: 1 }\n * isEqual([obj], [obj]) // true,同一个对象引用\n * isEqual([{ id: 1 }], [{ id: 1 }]) // false,不同对象引用\n * ```\n */\nexport function isEqual<T>(a: T[], b: T[]): boolean {\n if (a.length !== b.length)\n return false\n return a.every((item, index) => item === b[index])\n}\n\n/**\n * 根据条件对数组中的元素进行分组\n * @param array 原始数组\n * @param keyFn 用于生成分组键的函数,接收数组元素并返回用作分组键的值\n * @returns 分组后的对象,键为分组键,值为对应的元素数组\n * @group Array\n * @example\n * ```ts\n * const people = [\n * { name: '张三', age: 25 },\n * { name: '李四', age: 30 },\n * { name: '王五', age: 25 },\n * { name: '赵六', age: 30 }\n * ]\n *\n * // 按年龄分组\n * const groupedByAge = groupBy(people, person => person.age)\n * // 结果:\n * // {\n * // 25: [{ name: '张三', age: 25 }, { name: '王五', age: 25 }],\n * // 30: [{ name: '李四', age: 30 }, { name: '赵六', age: 30 }]\n * // }\n *\n * // 使用字符串键\n * const groupedByAgeRange = groupBy(people, person =>\n * person.age < 30 ? '青年' : '成年')\n * // 结果:\n * // {\n * // '青年': [{ name: '张三', age: 25 }, { name: '王五', age: 25 }],\n * // '成年': [{ name: '李四', age: 30 }, { name: '赵六', age: 30 }]\n * // }\n * ```\n */\nexport function groupBy<T, K extends string | number | symbol>(array: T[], keyFn: (item: T) => K): Record<K, T[]> {\n return array.reduce((result, item) => {\n const key = keyFn(item);\n (result[key] = result[key] || []).push(item)\n return result\n }, {} as Record<K, T[]>)\n}\n\n/**\n * 在选项数组前添加一个\"全部\"选项\n *\n * @param options - 原始选项数组\n * @param config - 配置选项\n * @param config.name - 选项标签的字段名 (默认为 'label')\n * @param config.value - \"全部\"选项的值 (默认为 '')\n * @param config.valueKey - 选项值的字段名 (默认为 'value')\n * @returns 添加了\"全部\"选项的新数组\n *\n * @group Array\n * @example\n * ```ts\n * // 基本用法\n * const options = [\n * { label: '选项1', value: 1 },\n * { label: '选项2', value: 2 }\n * ]\n * const result = appendUniversalOption(options)\n * // 结果: [\n * // { label: '全部', value: '' },\n * // { label: '选项1', value: 1 },\n * // { label: '选项2', value: 2 }\n * // ]\n *\n * // 自定义字段名和值\n * const customOptions = [\n * { text: '选项1', id: 1 },\n * { text: '选项2', id: 2 }\n * ]\n * const customResult = appendUniversalOption(customOptions, {\n * name: 'text',\n * valueKey: 'id',\n * value: 0\n * })\n * // 结果: [\n * // { text: '全部', id: 0 },\n * // { text: '选项1', id: 1 },\n * // { text: '选项2', id: 2 }\n * // ]\n * ```\n */\nexport function appendUniversalOption<T extends Record<string, any>, V = any>(\n options: T[],\n {\n name = 'label',\n value = '' as V,\n valueKey = 'value',\n }: {\n name?: string\n value?: V\n valueKey?: string\n } = {},\n): T[] {\n return [\n {\n [name]: '全部',\n [valueKey]: value,\n } as unknown as T,\n ...options,\n ]\n}\n\n/**\n * 递归重命名树形结构节点的属性。\n *\n * @param tree - 需要处理的树形数据(根节点数组)\n * @param renameMap - 属性重命名映射对象,格式为 `{ oldKey: newKey }`\n * @param childKey - 子节点的键名(默认为 `'children'`)\n * @param deleteOldKeys - 是否删除旧属性(默认 `true`)\n * @returns 处理后的树形结构数组\n *\n * @group Array\n * @example\n * const tree = [\n * { id: 1, name: 'Node 1', children: [...] }\n * ];\n * const renamedTree = renameTreeNodes(tree, { name: 'label' });\n * // 输出:节点中的 `name` 被替换为 `label`,且旧属性被删除\n */\nexport function renameTreeNodes(\n tree: any[],\n renameMap: Record<string, string>,\n childKey: string = 'children',\n deleteOldKeys: boolean = true,\n): any[] {\n return tree.map((node) => {\n const newNode = { ...node }\n\n // 重命名属性\n Object.entries(renameMap).forEach(([oldKey, newKey]) => {\n if (hasOwnProp(newNode, oldKey)) {\n newNode[newKey] = newNode[oldKey]\n if (deleteOldKeys) {\n delete newNode[oldKey]\n }\n }\n })\n\n // 递归处理子节点\n if (Array.isArray(newNode[childKey])) {\n newNode[childKey] = renameTreeNodes(\n newNode[childKey],\n renameMap,\n childKey,\n deleteOldKeys,\n )\n }\n\n return newNode\n })\n}\n\n/**\n * 递归转换树形结构的节点,支持重命名、新增、过滤属性。\n *\n * @param tree - 需要处理的树形数据(根节点数组)\n * @param transformer - 转换函数,接收当前节点和子节点,返回处理后的节点\n * @param childKey - 子节点的键名(默认为 `'children'`)\n * @returns 转换后的树形结构数组\n *\n * @group Array\n * @example\n * // 重命名 `name` 为 `label`,并添加 `isLeaf` 属性\n * const transformedTree = transformTree(tree, (node, children) => ({\n * id: node.id,\n * label: node.name,\n * isLeaf: children.length === 0,\n * children,\n * }));\n */\nexport function transformTree(\n tree: any[],\n transformer: (node: any, children: any[]) => any,\n childKey: string = 'children',\n): any[] {\n return tree.map((node) => {\n const children = node[childKey] ? transformTree(node[childKey], transformer, childKey) : []\n return transformer(node, children)\n })\n}\n\ntype KeysMatching<T, V> = {\n [K in keyof T]-?: T[K] extends V ? K : never\n}[keyof T]\n/**\n * 数组转为对象\n * @param arr - 原始数组\n * @param key - 数组对象键\n * @returns 对应键的对象\n * @group Array\n * @example\n * ```ts\n * const arr = [{ key: 'tom', name: '汤姆' }, { key: 'jack', name: '杰克' }]\n * arrayToObject(arr) // { tom: { key: 'tom', name: '汤姆' }, jack: { key: 'jack', name: '杰克' } }\n *\n * // key 值为空\n * const arr = [{ key: 'tom', name: '汤姆' }, { key: '', name: '杰克' }]\n * arrayToObject(arr) // { tom: { key: 'tom', name: '汤姆' } }\n * ```\n */\nexport function arrayToObject<T>(\n arr: T[],\n key: KeysMatching<T, string | number>,\n): Record<string, T> {\n const result: Record<string, T> = {}\n for (const item of arr) {\n const K = item[key] // K 在这里被 TS 推断为 string | number\n if (!K)\n continue\n result[K as string] = item\n }\n return result\n}\n\n/**\n * 替换数组数据中指定字段的 `&nbsp;` 为不换行空格\n *\n * @param {Array<Record<string, any>>} arr - 数组\n * @param {string} key - 需要处理的字段名\n * @returns {Array<Record<string, any>>} 处理后的数据数组\n *\n * @group Array\n * @example\n * ```ts\n * // 基本用法\n * const data = [\n * { name: 'John&nbsp;Doe', age: 30 },\n * { name: 'Jane&nbsp;&nbsp;Smith', age: 25 }\n * ]\n * const result = arrayReplaceNBSP(data, 'name')\n * // 结果: [\n * // { name: 'John Doe', age: 30 },\n * // { name: 'Jane Smith', age: 25 }\n * // ]\n *\n * // 空数组情况\n * const emptyData = []\n * const result = arrayReplaceNBSP(emptyData, 'name')\n * // 结果: []\n * ```\n */\nexport function arrayReplaceNBSP<T extends Record<string, string | number | boolean | object>>(\n arr: T[],\n key: string,\n): T[] {\n if (arr.length) {\n return arr.map((item) => {\n if (typeof item[key] === 'string') {\n // 创建一个新对象,不修改原对象\n return {\n ...item,\n [key]: replaceNBSP(item[key] as string),\n }\n }\n return item\n })\n }\n\n return []\n}\n","import type { Dayjs, ManipulateType, OpUnitType, QUnitType } from 'dayjs'\n/**\n * @module 日期时间\n * @description 提供各种日期时间处理函数,包括日期格式化、日期计算、比较等实用功能。基于 dayjs 库实现。\n */\nimport dayjs from 'dayjs'\nimport isBetween from 'dayjs/plugin/isBetween'\nimport isSameOrAfter from 'dayjs/plugin/isSameOrAfter'\nimport isSameOrBefore from 'dayjs/plugin/isSameOrBefore'\nimport relativeTime from 'dayjs/plugin/relativeTime'\nimport { isNaN, isNumber } from '../is'\n\n// 导入本地化语言\nimport 'dayjs/locale/zh-cn'\n\n// 设置全局语言为中文\ndayjs.locale('zh-cn')\n\n/**\n * 表示日期的各种类型,可以是日期对象、日期字符串或时间戳\n */\nexport type DateLike = Date | string | number\n\n// 注册插件\ndayjs.extend(relativeTime)\ndayjs.extend(isSameOrBefore)\ndayjs.extend(isSameOrAfter)\ndayjs.extend(isBetween)\n\n// 可以按需导入更多插件\n// import weekOfYear from 'dayjs/plugin/weekOfYear'\n// import isLeapYear from 'dayjs/plugin/isLeapYear'\n// import customParseFormat from 'dayjs/plugin/customParseFormat'\n// dayjs.extend(weekOfYear)\n// dayjs.extend(isLeapYear)\n// dayjs.extend(customParseFormat)\n\n/**\n * 创建 dayjs 对象\n * @param date 日期参数,可以是日期对象、时间戳或日期字符串\n * @returns dayjs 对象\n * @group Date\n * @example\n * ```ts\n * createDate() // 当前日期时间\n * createDate('2023-05-15') // 指定日期\n * createDate(1684123456000) // 时间戳\n * ```\n */\nexport function createDate(date?: DateLike | Dayjs): Dayjs {\n return dayjs(date)\n}\n\n/**\n * 判断时间戳是否为毫秒级时间戳\n * @param value 要检查的值\n * @returns 如果是毫秒级时间戳则返回 true,否则返回 false\n * @group Date\n * @example\n * ```ts\n * isMillisecondTimestamp(1673740800000) // true,13位数字\n * isMillisecondTimestamp(1673740800) // false,10位数字,秒级时间戳\n * isMillisecondTimestamp('1673740800000') // false, 字符串不是时间戳\n * isMillisecondTimestamp(new Date()) // false,日期对象不是时间戳\n * ```\n */\nexport function isMillisecondTimestamp(value: unknown): boolean {\n if (!isNumber(value) || value.toString().length !== 13)\n return false\n\n const date = new Date(value)\n return !isNaN(date.getTime())\n}\n\n/**\n * 转换为 dayjs 可接收的参数格式\n * @param value 日期参数,可以是日期对象、时间戳(秒或毫秒)或日期字符串\n * @returns 转换后的参数,如果是秒级时间戳则转为毫秒级,其他类型保持不变\n * @group Date\n * @example\n * ```ts\n * convertToDayjsParam(1673740800) // 1673740800000,秒转为毫秒\n * convertToDayjsParam(1673740800000) // 1673740800000,毫秒保持不变\n * convertToDayjsParam(new Date()) // Date对象,保持不变\n * convertToDayjsParam('2023-01-15') // '2023-01-15',字符串保持不变\n * ```\n */\nexport function convertToDayjsParam(value: DateLike) {\n // 如果已经是毫秒级时间戳,直接返回\n if (isMillisecondTimestamp(value)) {\n return value\n }\n\n // 如果是数字且是10位数字,则可能是秒级时间戳,需要转为毫秒\n if (isNumber(value) && String(value).length === 10) {\n return value * 1000\n }\n\n return value\n}\n\n/**\n * 格式化日期为指定格式的字符串\n * @param date 日期对象、时间戳或日期字符串\n * @param format 格式字符串,支持的占位符请参考 [dayjs 文档](https://day.js.org/docs/zh-CN/durations/format#%E6%94%AF%E6%8C%81%E7%9A%84%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%8D%A0%E4%BD%8D%E7%AC%A6%E5%88%97%E8%A1%A8)\n * @returns 格式化后的日期字符串\n * @group Date\n * @example\n * ```ts\n * formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss') // \"2023-05-16 14:30:45\"\n * formatDate('2023-05-15', 'YYYY年MM月DD日') // \"2023年05月15日\"\n * formatDate(1684123456000, 'MM/DD/YYYY') // \"05/15/2023\"\n * ```\n */\nexport function formatDate(date: DateLike, format = 'YYYY-MM-DD'): string {\n return dayjs(convertToDayjsParam(date)).format(format)\n}\n\n/**\n * 将日期格式化为完整的时间字符串(YYYY-MM-DD HH:mm:ss)\n * @param date 日期对象、时间戳或日期字符串\n * @returns 格式为 YYYY-MM-DD HH:mm:ss 的日期时间字符串\n * @group Date\n * @example\n * ```ts\n * formatFullTime(new Date()) // \"2023-05-16 14:30:45\"\n * formatFullTime('2023-05-15') // \"2023-05-15 00:00:00\"\n * formatFullTime(1684123456) // \"2023-05-15 10:30:56\"(秒级时间戳会被自动转换)\n * ```\n */\nexport function formatFullTime(date: DateLike) {\n return formatDate(date, 'YYYY-MM-DD HH:mm:ss')\n}\n\n/**\n * 获取当前日期时间的时间戳\n * @returns 当前时间戳(毫秒)\n * @group Date\n * @example\n * ```ts\n * now() // 例如: 1684123456789\n * ```\n */\nexport function now(): number {\n return Date.now()\n}\n\n/**\n * 解析日期字符串为日期对象\n * @param dateStr 日期字符串\n * @returns 日期对象\n * @throws 如果日期格式无效,抛出 TypeError\n * @group Date\n * @example\n * ```ts\n * parseDate('2023-05-15') // Date 对象: Mon May 15 2023 00:00:00\n * parseDate('2023/05/15 14:30:45') // Date 对象: Mon May 15 2023 14:30:45\n * parseDate('invalid date') // 抛出 TypeError: 无效的日期格式\n * ```\n */\nexport function parseDate(dateStr: string): Date {\n const d = dayjs(dateStr)\n if (!d.isValid())\n throw new TypeError('无效的日期格式')\n return d.toDate()\n}\n\n/**\n * 计算两个日期之间的差异\n * @param date1 第一个日期\n * @param date2 第二个日期\n * @param unit 计量单位,默认为 'day',可以是 'year', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond'\n * @returns 两个日期之间的差值,正数表示 date1 晚于 date2,负数表示 date1 早于 date2\n * @group Date\n * @example\n * ```ts\n * diff('2023-05-15', '2023-05-10') // 5(相差5天)\n * diff('2023-05-15', '2023-06-15', 'month') // -1(相差1个月,且第一个日期早于第二个)\n * diff('2023-05-15 08:00', '2023-05-15 06:00', 'hour') // 2(相差2小时)\n * ```\n */\nexport function diff(date1: DateLike, date2: DateLike, unit: QUnitType | OpUnitType = 'day'): number {\n return dayjs(convertToDayjsParam(date1)).diff(dayjs(convertToDayjsParam(date2)), unit)\n}\n\n/**\n * 向日期添加指定时间\n * @param date 原始日期\n * @param amount 要添加的数量,可以为负数\n * @param unit 时间单位,默认为 'day',可以是 'year', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond'\n * @returns 添加后的新日期对象\n * @group Date\n * @example\n * ```ts\n * add(new Date('2023-05-15'), 2, 'day') // Date 对象: Wed May 17 2023\n * add('2023-05-15', -1, 'month') // Date 对象: Sat Apr 15 2023\n * add('2023-05-15 12:00', 30, 'minute') // Date 对象: Mon May 15 2023 12:30:00\n * ```\n */\nexport function add(date: DateLike, amount: number, unit: ManipulateType = 'day'): Date {\n return dayjs(convertToDayjsParam(date)).add(amount, unit).toDate()\n}\n\n/**\n * 向日期添加指定天数\n * @param date 原始日期\n * @param days 要添加的天数(可以为负数)\n * @returns 添加天数后的新日期对象\n * @group Date\n * @example\n * ```ts\n * addDays(new Date('2023-05-15'), 5) // Date 对象: Sat May 20 2023\n * addDays('2023-05-15', -3) // Date 对象: Fri May 12 2023\n * ```\n */\nexport function addDays(date: DateLike, days: number): Date {\n return add(convertToDayjsParam(date), days, 'day')\n}\n\n/**\n * 向日期添加指定月数\n * @param date 原始日期\n * @param months 要添加的月数(可以为负数)\n * @returns 添加月数后的新日期对象\n * @group Date\n * @example\n * ```ts\n * addMonths(new Date('2023-05-15'), 2) // Date 对象: Sat Jul 15 2023\n * addMonths('2023-05-31', 1) // Date 对象: Fri Jun 30 2023(注意月份天数自动调整)\n * addMonths('2023-01-15', -3) // Date 对象: Wed Oct 15 2022\n * ```\n */\nexport function addMonths(date: DateLike, months: number): Date {\n return add(convertToDayjsParam(date), months, 'month')\n}\n\n/**\n * 向日期添加指定年数\n * @param date 原始日期\n * @param years 要添加的年数(可以为负数)\n * @returns 添加年数后的新日期对象\n * @group Date\n * @example\n * ```ts\n * addYears(new Date('2023-05-15'), 1) // Date 对象: Wed May 15 2024\n * addYears('2023-05-15', -3) // Date 对象: Fri May 15 2020\n * addYears('2024-02-29', 1) // Date 对象: Fri Feb 28 2025(注意闰年自动调整)\n * ```\n */\nexport function addYears(date: DateLike, years: number): Date {\n return add(convertToDayjsParam(date), years, 'year')\n}\n\n/**\n * 获取指定日期是一周中的第几天\n * @param date 日期\n * @param startOnMonday 是否从周一开始计算(默认为 false,即从周日开始)\n * @returns 一周中的第几天(0-6),周日为 0,周六为 6;如果 startOnMonday 为 true,则周一为 0,周日为 6\n * @group Date\n * @example\n * ```ts\n * getDayOfWeek(new Date('2023-05-15')) // 1(周一,从周日开始算是第1天)\n * getDayOfWeek('2023-05-15', true) // 0(周一,从周一开始算是第0天)\n * getDayOfWeek('2023-05-14') // 0(周日,从周日开始算是第0天)\n * getDayOfWeek('2023-05-14', true) // 6(周日,从周一开始算是第6天)\n * ```\n */\nexport function getDayOfWeek(date: DateLike, startOnMonday = false): number {\n const d = dayjs(convertToDayjsParam(date))\n const day = d.day()\n if (startOnMonday) {\n return day === 0 ? 6 : day - 1\n }\n return day\n}\n\n/**\n * 检查日期是否在指定范围内\n * @param date 要检查的日期\n * @param startDate 范围起始日期\n * @param endDate 范围结束日期\n * @returns 如果日期在指定范围内(不包括边界)则返回 true,否则返回 false\n * @group Date\n * @example\n * ```ts\n * isDateInRange('2023-05-15', '2023-05-10', '2023-05-20') // true\n * isDateInRange('2023-05-15', '2023-05-15', '2023-05-20') // false(不包括开始日期)\n * isDateInRange('2023-05-15', '2023-05-10', '2023-05-15') // false(不包括结束日期)\n * ```\n */\nexport function isDateInRange(date: DateLike, startDate: DateLike, endDate: DateLike): boolean {\n const d = dayjs(convertToDayjsParam(date))\n return d.isAfter(dayjs(convertToDayjsParam(startDate))) && d.isBefore(dayjs(convertToDayjsParam(endDate)))\n}\n\n/**\n * 获取指定月份的天数\n * @param year 年份\n * @param month 月份(0-11),0 表示一月,11 表示十二月\n * @returns 该月的天数\n * @group Date\n * @example\n * ```ts\n * getDaysInMonth(2023, 1) // 28(2023年2月有28天)\n * getDaysInMonth(2024, 1) // 29(2024年2月有29天,闰年)\n * getDaysInMonth(2023, 0) // 31(2023年1月有31天)\n * ```\n */\nexport function getDaysInMonth(year: number, month: number): number {\n return dayjs(new Date(year, month, 1)).daysInMonth()\n}\n\n/**\n * 获取相对时间描述\n * @param date 日期\n * @param baseDate 基准日期,默认为当前时间\n * @returns 相对时间描述,如\"几分钟前\"、\"几天后\"等\n * @group Date\n * @example\n * ```ts\n * fromNow(new Date(Date.now() - 5 * 60 * 1000)) // \"5分钟前\"\n * fromNow(new Date(Date.now() + 24 * 60 * 60 * 1000)) // \"1天内\"\n * fromNow('2023-01-01', '2023-01-05') // \"4天前\"\n * ```\n */\nexport function fromNow(date: DateLike, baseDate?: DateLike): string {\n date = convertToDayjsParam(date)\n baseDate = baseDate && convertToDayjsParam(baseDate)\n if (baseDate)\n return dayjs(date).from(dayjs(baseDate))\n return dayjs(date).fromNow()\n}\n\n/**\n * 获取日期的开始时间\n * @param date 日期\n * @param unit 单位,可以是 'year', 'month', 'week', 'day', 'hour', 'minute', 'second'\n * @returns 单位开始时间的日期对象\n * @group Date\n * @example\n * ```ts\n * startOf(new Date('2023-05-15 15:30:45'), 'day') // Date 对象: Mon May 15 2023 00:00:00\n * startOf('2023-05-15 15:30:45', 'month') // Date 对象: Mon May 01 2023 00:00:00\n * startOf('2023-05-15 15:30:45', 'hour') // Date 对象: Mon May 15 2023 15:00:00\n * ```\n */\nexport function startOf(date: DateLike, unit: OpUnitType): Date {\n return dayjs(convertToDayjsParam(date)).startOf(unit).toDate()\n}\n\n/**\n * 获取日期的结束时间\n * @param date 日期\n * @param unit 单位,可以是 'year', 'month', 'week', 'day', 'hour', 'minute', 'second'\n * @returns 单位结束时间的日期对象\n * @group Date\n * @example\n * ```ts\n * endOf(new Date('2023-05-15 15:30:45'), 'day') // Date 对象: Mon May 15 2023 23:59:59.999\n * endOf('2023-05-15 15:30:45', 'month') // Date 对象: Wed May 31 2023 23:59:59.999\n * endOf('2023-05-15 15:30:45', 'hour') // Date 对象: Mon May 15 2023 15:59:59.999\n * ```\n */\nexport function endOf(date: DateLike, unit: OpUnitType): Date {\n return dayjs(convertToDayjsParam(date)).endOf(unit).toDate()\n}\n\n/**\n * 格式化日期为人类友好的格式\n * @param date 日期\n * @returns 人类友好的日期描述,如\"今天 HH:mm\"、\"昨天 HH:mm\"、\"明天 HH:mm\"或\"YYYY-MM-DD HH:mm\"\n * @group Date\n * @example\n * ```ts\n * formatHumanReadable(new Date()) // \"今天 15:30\"\n * formatHumanReadable(new Date(Date.now() - 24 * 60 * 60 * 1000)) // \"昨天 15:30\"\n * formatHumanReadable(new Date(Date.now() + 24 * 60 * 60 * 1000)) // \"明天 15:30\"\n * formatHumanReadable('2023-01-01') // \"2023-01-01 00:00\"\n * ```\n */\nexport function formatHumanReadable(date: DateLike): string {\n const d = dayjs(convertToDayjsParam(date))\n const now = dayjs()\n\n if (d.isSame(now, 'day'))\n return `今天 ${d.format('HH:mm')}`\n if (d.isSame(now.subtract(1, 'day'), 'day'))\n return `昨天 ${d.format('HH:mm')}`\n if (d.isSame(now.add(1, 'day'), 'day'))\n return `明天 ${d.format('HH:mm')}`\n if (d.isSame(now, 'year'))\n return d.format('M月D日 HH:mm')\n return d.format('YYYY年M月D日 HH:mm')\n}\n\n/**\n * 格式化日期为聊天列表时间格式(类似微信聊天列表)\n * @param date 日期\n * @returns 格式化后的时间字符串\n * - 当天:HH:mm\n * - 昨天:昨天 HH:mm\n * - 7天内(不包括今天和昨天):星期几\n * - 今年其他日期:MM月DD日\n * - 往年日期:YYYY年MM月DD日\n * @group Date\n * @example\n * ```ts\n * formatChatTime(new Date()) // \"14:30\"\n * formatChatTime(new Date(Date.now() - 24 * 60 * 60 * 1000)) // \"昨天 09:15\"\n * formatChatTime(new Date(Date.now() - 3 * 24 * 60 * 60 * 1000)) // \"星期三\"\n * formatChatTime('2023-05-15') // \"05月15日\"\n * formatChatTime('2022-12-25') // \"2022年12月25日\"\n * ```\n */\nexport function formatChatTime(date: DateLike): string {\n const d = dayjs(convertToDayjsParam(date))\n const now = dayjs()\n\n // 当天:显示时间\n if (d.isSame(now, 'day')) {\n return d.format('HH:mm')\n }\n\n // 昨天:显示\"昨天 HH:mm\"\n if (d.isSame(now.subtract(1, 'day'), 'day')) {\n return `昨天 ${d.format('HH:mm')}`\n }\n\n // 7天内(不包括今天和昨天):显示星期几\n const daysDiff = now.diff(d, 'day')\n if (daysDiff >= 2 && daysDiff < 7) {\n const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']\n const dayIndex = d.day()\n return weekdays[dayIndex] || '星期日'\n }\n\n // 今年其他日期:显示MM月DD日\n if (d.isSame(now, 'year')) {\n return d.format('MM月DD日')\n }\n\n // 往年日期:显示YYYY年MM月DD日\n return d.format('YYYY年MM月DD日')\n}\n","/**\n * @module 数字处理\n * @description 提供各种数字处理函数,包括数字格式化、计算、范围控制等实用功能\n */\n\n/**\n * 将数字四舍五入到指定小数位\n * @param num 要处理的数字\n * @param precision 小数位数,默认为0(整数)\n * @returns 四舍五入后的数字\n * @group Number\n * @example\n * ```ts\n * round(3.1415) // 3\n * round(3.1415, 2) // 3.14\n * round(3.1415, 3) // 3.142\n * round(3.9999) // 4\n * ```\n */\nexport function round(num: number, precision = 0): number {\n const factor = 10 ** precision\n return Math.round(num * factor) / factor\n}\n\n/**\n * 将数字格式化为带千位分隔符的字符串\n * @param num 要格式化的数字\n * @param locale 区域设置,默认为浏览器默认区域\n * @returns 格式化后的字符串\n * @group Number\n * @example\n * ```ts\n * formatThousands(1234567) // '1,234,567'(根据浏览器默认区域可能有所不同)\n * formatThousands(1234567.89, 'en-US') // '1,234,567.89'\n * formatThousands(1234567.89, 'de-DE') // '1.234.567,89'\n * ```\n */\nexport function formatThousands(num: number, locale?: string): string {\n return num.toLocaleString(locale)\n}\n\n/**\n * 将数字格式化为货币字符串\n * @param value 要格式化的数字\n * @param options 格式化选项\n * @param options.currency 货币代码(如 'CNY', 'USD'),默认为 'CNY'\n * @param options.locale 地区设置(如 'zh-CN', 'en-US'),默认为 'zh-CN'\n * @param options.minimumFractionDigits 最小小数位数,默认为 2\n * @param options.maximumFractionDigits 最大小数位数,默认为 2\n * @returns 格式化后的货币字符串\n * @group Number\n * @example\n * ```ts\n * formatCurrency(1234.56) // '¥1,234.56'\n * formatCurrency(1234.56, { currency: 'USD', locale: 'en-US' }) // '$1,234.56'\n * formatCurrency(1234.56789, { maximumFractionDigits: 4 }) // '¥1,234.5679'\n * formatCurrency(1234, { minimumFractionDigits: 0 }) // '¥1,234'\n * ```\n */\nexport function formatCurrency(\n value: number,\n options: {\n currency?: string\n locale?: string\n minimumFractionDigits?: number\n maximumFractionDigits?: number\n } = {},\n): string {\n const {\n currency = 'CNY',\n locale = 'zh-CN',\n minimumFractionDigits = 2,\n maximumFractionDigits = 2,\n } = options\n\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency,\n minimumFractionDigits,\n maximumFractionDigits,\n }).format(value)\n}\n\n/**\n * 确保数字在指定范围内\n * @param num 要限制范围的数字\n * @param min 最小值\n * @param max 最大值\n * @returns 在指定范围内的数字:如果小于最小值返回最小值,如果大于最大值返回最大值\n * @group Number\n * @example\n * ```ts\n * clamp(5, 0, 10) // 5 - 在范围内,保持不变\n * clamp(-5, 0, 10) // 0 - 小于最小值,返回最小值\n * clamp(15, 0, 10) // 10 - 大于最大值,返回最大值\n * ```\n */\nexport function clamp(num: number, min: number, max: number): number {\n return Math.min(Math.max(num, min), max)\n}\n\n/**\n * 生成指定范围内的随机整数(包含边界值)\n * @param min 最小值(包含)\n * @param max 最大值(包含)\n * @returns 指定范围内的随机整数\n * @group Number\n * @example\n * ```ts\n * randomInt(1, 10) // 返回 1 到 10 之间的随机整数,包括 1 和 10\n * randomInt(0, 1) // 返回 0 或 1\n * randomInt(5, 5) // 总是返回 5\n * ```\n */\nexport function randomInt(min: number, max: number): number {\n min = Math.ceil(min)\n max = Math.floor(max)\n return Math.floor(Math.random() * (max - min + 1)) + min\n}\n\n/**\n * 检查一个数字是否为偶数\n * @param num 要检查的数字\n * @returns 如果是偶数则返回 true,否则返回 false\n * @group Number\n * @example\n * ```ts\n * isEven(2) // true\n * isEven(3) // false\n * isEven(0) // true\n * isEven(-4) // true\n * ```\n */\nexport function isEven(num: number): boolean {\n return num % 2 === 0\n}\n\n/**\n * 检查一个数字是否为奇数\n * @param num 要检查的数字\n * @returns 如果是奇数则返回 true,否则返回 false\n * @group Number\n * @example\n * ```ts\n * isOdd(3) // true\n * isOdd(2) // false\n * isOdd(0) // false\n * isOdd(-3) // true\n * ```\n */\nexport function isOdd(num: number): boolean {\n return !isEven(num)\n}\n\n/**\n * 计算百分比值\n * @param value 当前值\n * @param total 总值\n * @param precision 结果保留的小数位数,默认为2\n * @returns 百分比值,如果总值为零则返回 0\n * @group Number\n * @example\n * ```ts\n * percentage(25, 100) // 25\n * percentage(1, 3) // 33.33\n * percentage(1, 3, 0) // 33\n * percentage(5, 0) // 0,避免除以零错误\n * ```\n */\nexport function percentage(value: number, total: number, precision = 2): number {\n if (total === 0)\n return 0\n return round((value / total) * 100, precision)\n}\n\n/**\n * 将数值转换为带\"万\"单位的字符串\n * @param num 要转换的数值\n * @param fractionDigits 小数位数,默认为2\n * @returns 转换后的字符串,如果值大于等于10000则转为\"万\"单位\n * @group Number\n * @example\n * ```ts\n * formatNumberWithTenThousand(1234) // '1234'\n * formatNumberWithTenThousand(12345) // '1.23万'\n * formatNumberWithTenThousand(12345, 1) // '1.2万'\n * formatNumberWithTenThousand(0) // '0'\n * ```\n */\nexport function formatNumberWithTenThousand(num: number, fractionDigits = 2): string {\n if (!num) {\n return '0'\n }\n if (num >= 10000) {\n // 保留两位小数\n return `${(num / 10000).toFixed(fractionDigits)}万`\n }\n else {\n return num.toString()\n }\n}\n","/**\n * 延迟指定的毫秒数\n *\n * @param {number} ms - 需要延迟的毫秒数\n * @returns {Promise<void>} 返回一个在指定时间后resolve的Promise\n * @group Promise\n * @example\n * ```ts\n * // 延迟2秒\n * await delay(2000)\n * console.log('2秒后执行')\n *\n * // 在异步函数中使用\n * async function fetchWithDelay() {\n * await delay(1000)\n * return fetch('/api/data')\n * }\n * ```\n */\nexport function delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n","import { objectToQueryString } from '../object'\n\n/**\n * 将对象转换为URL查询字符串\n *\n * @deprecated 请使用 objectToQueryString 函数代替,它在 object 模块中。该函数将在下一个主要版本中移除。\n * @param {Record<string, any>} params - 需要转换的对象\n * @returns {string} 转换后的查询字符串,如果有参数则以?开头\n * @group Url\n * @example\n * ```ts\n * // 基本用法\n * getQueryStringify({ name: 'John', age: 30 })\n * // 返回: ?name=John&age=30\n *\n * // 包含对象的情况\n * getQueryStringify({ user: { name: 'John', age: 30 }, active: true })\n * // 返回: ?user={\"name\":\"John\",\"age\":30}&active=true\n *\n * // 空对象\n * getQueryStringify({})\n * // 返回: ''\n *\n * // null 或 undefined\n * getQueryStringify(null)\n * // 返回: ''\n * ```\n */\nexport function getQueryStringify(params: Record<string, any> | null | undefined): string {\n if (!params)\n return ''\n\n const queryString = objectToQueryString(params)\n\n if (queryString)\n return `?${queryString}`\n\n return ''\n}\n"]}
1
+ {"version":3,"sources":["../packages/is/index.ts","../packages/object/index.ts","../packages/string/index.ts","../packages/array/index.ts","../packages/date/index.ts","../packages/number/index.ts","../packages/promise/index.ts","../packages/url/index.ts"],"names":["isString","val","isNumber","isNaN","isBoolean","isFunction","isClass","func","funcStr","funcWithPrototype","isObject","isArray","isDate","isRegExp","isPromise","isMap","isSet","isSymbol","isPrimitive","isUndefined","isNull","isNullOrUndefined","isEmptyObject","isEmpty","value","isPlainObject","proto","isMobilePhone","phoneStr","hasOwnProp","obj","prop","deepClone","item","cloned","key","get","path","defaultValue","keys","result","pick","omit","objectToQueryString","_","merge","objects","deepMerge","sourceValue","targetValue","filterObjectByKeys","originalObject","keysArray","keyMapping","newObject","newKey","capitalize","str","camelToKebab","kebabToCamel","char","truncate","length","ellipsis","truncatedLength","randomString","chars","charsLength","i","escapeHtml","html","entityMap","s","isValidUrl","url","isValidEmail","email","isEmptyString","ensureRpxUnit","replaceNBSP","parseJsonStr","trimmedStr","error","remove","array","unique","chunk","size","last","first","shuffle","j","sample","index","isEqual","a","b","groupBy","keyFn","appendUniversalOption","options","name","valueKey","renameTreeNodes","tree","renameMap","childKey","deleteOldKeys","node","newNode","oldKey","transformTree","transformer","children","arrayToObject","arr","K","arrayReplaceNBSP","dayjs","relativeTime","isSameOrBefore","isSameOrAfter","isBetween","createDate","date","isMillisecondTimestamp","convertToDayjsParam","formatDate","format","formatFullTime","now","parseDate","dateStr","d","diff","date1","date2","unit","add","amount","addDays","days","addMonths","months","addYears","years","getDayOfWeek","startOnMonday","day","isDateInRange","startDate","endDate","getDaysInMonth","year","month","fromNow","baseDate","startOf","endOf","formatHumanReadable","formatChatTime","daysDiff","weekdays","dayIndex","formatDuration","timestamp","duration","SECOND","MINUTE","HOUR","DAY","MONTH","YEAR","hours","minutes","seconds","trimmedFormat","totalYears","totalMonths","totalDays","totalHours","totalMinutes","totalSeconds","unitMap","formatTokens","token","current","paddedValue","parts","round","num","precision","factor","formatThousands","locale","formatCurrency","currency","minimumFractionDigits","maximumFractionDigits","clamp","min","max","randomInt","isEven","isOdd","percentage","total","formatNumberWithTenThousand","fractionDigits","delay","ms","resolve","getQueryStringify","params","queryString"],"mappings":"oNAiBO,SAASA,CAASC,CAAAA,CAAAA,CAA6B,CACpD,OAAO,OAAOA,CAAQ,EAAA,QACxB,CAgBO,SAASC,EAASD,CAA6B,CAAA,CACpD,OAAO,OAAOA,GAAQ,QAAY,EAAA,CAACE,CAAMF,CAAAA,CAAG,CAC9C,CAgBO,SAASG,EAAAA,CAAUH,EAA8B,CACtD,OAAO,OAAOA,CAAAA,EAAQ,SACxB,CAgBO,SAASI,CAAWJ,CAAAA,CAAAA,CAAgD,CACzE,OAAO,OAAOA,CAAQ,EAAA,UACxB,CAmBO,SAASK,EAAQL,CAAAA,CAAAA,CAAkD,CACxE,GAAI,CAACI,CAAWJ,CAAAA,CAAG,EACjB,OAAO,MAAA,CAGT,IAAMM,CAAAA,CAAON,EAGPO,CAAUD,CAAAA,CAAAA,CAAK,QAAS,EAAA,CAC9B,GAAI,UAAW,CAAA,IAAA,CAAKC,CAAO,CAAA,CACzB,OAAO,KAKT,CAAA,IAAMC,CAAoBF,CAAAA,CAAAA,CAC1B,OAAI,CAAAE,EAAAA,CAAAA,CAAkB,SAAaA,EAAAA,CAAAA,CAAkB,UAAU,WAAgBA,GAAAA,CAAAA,GAGzE,CAAC,QAAA,CAAS,IAAKA,CAAAA,CAAAA,CAAkB,IAAQ,EAAA,EAAE,GAM3C,iBAAkB,CAAA,IAAA,CAAKD,CAAO,CAAA,CAAA,CAMtC,CAiBO,SAASE,CAAAA,CAAST,CAAuC,CAAA,CAC9D,OAAOA,CAAQ,GAAA,IAAA,EAAQ,OAAOA,CAAAA,EAAQ,QACxC,CAgBO,SAASU,CAAAA,CAAQV,EAA4B,CAClD,OAAO,KAAM,CAAA,OAAA,CAAQA,CAAG,CAC1B,CAcO,SAASW,EAAAA,CAAOX,EAA2B,CAChD,OAAOA,CAAe,YAAA,IACxB,CAeO,SAASY,EAASZ,CAAAA,CAAAA,CAA6B,CACpD,OAAOA,CAAAA,YAAe,MACxB,CAgBO,SAASa,EAAmBb,CAAAA,CAAAA,CAAiC,CAClE,OAAOS,EAAST,CAAG,CAAA,EAAKI,CAAYJ,CAAAA,CAAAA,CAAY,IAAI,CAAKI,EAAAA,CAAAA,CAAYJ,CAAY,CAAA,KAAK,CACxF,CAcO,SAASc,EAAwBd,CAAAA,CAAAA,CAAgC,CACtE,OAAOA,CAAAA,YAAe,GACxB,CAcO,SAASe,EAAef,CAAAA,CAAAA,CAA6B,CAC1D,OAAOA,CAAe,YAAA,GACxB,CAcO,SAASgB,GAAShB,CAA6B,CAAA,CACpD,OAAO,OAAOA,GAAQ,QACxB,CAqBO,SAASiB,EAAAA,CAAYjB,EAAuB,CACjD,OAAOA,CAAQ,GAAA,IAAA,EAAS,OAAOA,CAAAA,EAAQ,QAAY,EAAA,OAAOA,GAAQ,UACpE,CAeO,SAASkB,CAAAA,CAAYlB,EAAgC,CAC1D,OAAO,OAAOA,CAAAA,CAAQ,GACxB,CAeO,SAASmB,CAAOnB,CAAAA,CAAAA,CAA2B,CAChD,OAAOA,CAAQ,GAAA,IACjB,CAgBO,SAASoB,CAAAA,CAAkBpB,CAAuC,CAAA,CACvE,OAAOmB,CAAOnB,CAAAA,CAAG,CAAKkB,EAAAA,CAAAA,CAAYlB,CAAG,CACvC,CAiBO,SAASqB,CAAAA,CAAcrB,EAAuB,CACnD,OAAOS,CAAST,CAAAA,CAAG,GAAK,MAAO,CAAA,IAAA,CAAKA,CAAG,CAAA,CAAE,SAAW,CACtD,CAoBO,SAASsB,EAAAA,CAAQtB,EAAuB,CAC7C,OAAIoB,CAAkBpB,CAAAA,CAAG,CAChB,CAAA,IAAA,CACLD,CAASC,CAAAA,CAAG,EACPA,CAAI,CAAA,IAAA,EAAO,CAAA,MAAA,GAAW,EAC3BU,CAAQV,CAAAA,CAAG,CACNA,CAAAA,CAAAA,CAAI,SAAW,CACpBS,CAAAA,CAAAA,CAAST,CAAG,CAAA,CACPqB,CAAcrB,CAAAA,CAAG,CACnB,CAAA,KACT,CAgBO,SAASE,CAAAA,CAAMqB,CAAyB,CAAA,CAC7C,OAAO,MAAO,CAAA,KAAA,CAAMA,CAAK,CAC3B,CAkBO,SAASC,CAAAA,CAAcD,CAA8C,CAAA,CAC1E,GAAI,OAAOA,CAAU,EAAA,QAAA,EAAYA,IAAU,IACzC,CAAA,OAAO,MAGT,CAAA,IAAME,EAAQ,MAAO,CAAA,cAAA,CAAeF,CAAK,CAAA,CACzC,OAAOE,CAAU,GAAA,MAAA,CAAO,SAAaA,EAAAA,CAAAA,GAAU,IACjD,CAyBO,SAASC,EAAc1B,CAAAA,CAAAA,CAAuB,CAEnD,GAAI,CAACD,CAASC,CAAAA,CAAG,GAAK,CAACC,CAAAA,CAASD,CAAG,CAAA,CACjC,OAAO,MAIT,CAAA,IAAM2B,CAAW,CAAA,MAAA,CAAO3B,CAAG,CAAA,CAM3B,OAFoB,eAAA,CAED,KAAK2B,CAAQ,CAClC,CC5bO,SAASC,EAAWC,CAAaC,CAAAA,CAAAA,CAAgC,CACtE,OAAO,OAAO,SAAU,CAAA,cAAA,CAAe,IAAKD,CAAAA,CAAAA,CAAKC,CAAI,CACvD,CAoBO,SAASC,EAAaF,CAAW,CAAA,CACtC,GAAIA,CAAAA,GAAQ,MAAQ,OAAOA,CAAAA,EAAQ,QACjC,CAAA,OAAOA,EAGT,GAAIA,CAAAA,YAAe,IACjB,CAAA,OAAO,IAAI,IAAA,CAAKA,CAAI,CAAA,OAAA,EAAS,CAG/B,CAAA,GAAIA,CAAe,YAAA,MAAA,CACjB,OAAO,IAAI,MAAA,CAAOA,CAAI,CAAA,MAAA,CAAQA,EAAI,KAAK,CAAA,CAGzC,GAAI,KAAA,CAAM,QAAQA,CAAG,CAAA,CACnB,OAAOA,CAAAA,CAAI,IAAIG,CAAQD,EAAAA,CAAAA,CAAUC,CAAI,CAAC,EAGxC,IAAMC,CAAAA,CAA8B,EAAC,CACrC,cAAO,IAAKJ,CAAAA,CAA0B,CAAE,CAAA,OAAA,CAASK,CAAQ,EAAA,CACvDD,CAAOC,CAAAA,CAAG,EAAIH,CAAWF,CAAAA,CAAAA,CAA4BK,CAAG,CAAC,EAC3D,CAAC,CAAA,CAEMD,CACT,CAoBO,SAASE,EAAaN,CAAAA,CAAAA,CAA0BO,CAAcC,CAAAA,CAAAA,CAAiC,CACpG,IAAMC,CAAOF,CAAAA,CAAAA,CAAK,MAAM,GAAG,CAAA,CACvBG,CAASV,CAAAA,CAAAA,CAEb,QAAWK,CAAOI,IAAAA,CAAAA,CAAM,CACtB,GAA4BC,GAAW,IACrC,CAAA,OAAOF,CAETE,CAAAA,CAAAA,CAASA,CAAOL,CAAAA,CAAG,EACrB,CAEA,OAAQK,CAAW,GAAA,MAAA,CAAaF,CAAeE,CAAAA,CACjD,CAmBO,SAASC,EAAAA,CAAuDX,CAAQS,CAAAA,CAAAA,CAAuB,CACpG,OAAOA,CAAAA,CAAK,MAAO,CAAA,CAACC,EAAQL,CACtBA,IAAAA,CAAAA,IAAOL,CACTU,GAAAA,CAAAA,CAAOL,CAAG,CAAIL,CAAAA,CAAAA,CAAIK,CAAG,CAAA,CAAA,CAEhBK,GACN,EAAgB,CACrB,CAiBO,SAASE,EAAuDZ,CAAAA,CAAAA,CAAQS,CAAuB,CAAA,CACpG,IAAMC,CAAAA,CAAS,CAAE,GAAGV,CAAI,CACxB,CAAA,OAAAS,CAAK,CAAA,OAAA,CAASJ,GAAQ,CACpB,OAAOK,CAAOL,CAAAA,CAAG,EACnB,CAAC,CAAA,CACMK,CACT,CAeO,SAASG,CAAAA,CAAoBb,CAAkC,CAAA,CACpE,OAAO,MAAO,CAAA,OAAA,CAAQA,CAAG,CAAA,CACtB,OAAO,CAAC,CAACc,CAAGpB,CAAAA,CAAK,IAA6BA,CAAU,EAAA,IAAI,CAC5D,CAAA,GAAA,CAAI,CAAC,CAACW,CAAKX,CAAAA,CAAK,IACX,KAAM,CAAA,OAAA,CAAQA,CAAK,CAAA,CACdA,EAAM,GAAIS,CAAAA,CAAAA,EAAQ,CAAG,EAAA,kBAAA,CAAmBE,CAAG,CAAC,CAAA,CAAA,EAAI,kBAAmB,CAAA,MAAA,CAAOF,CAAI,CAAC,CAAC,CAAE,CAAA,CAAA,CAAE,KAAK,GAAG,CAAA,CAE9F,CAAG,EAAA,kBAAA,CAAmBE,CAAG,CAAC,CAAA,CAAA,EAAI,kBAAmB,CAAA,MAAA,CAAOX,CAAK,CAAC,CAAC,CACvE,CAAA,CAAA,CACA,IAAK,CAAA,GAAG,CACb,CAeO,SAASqB,EAAwCC,CAAAA,GAAAA,CAAAA,CAAiB,CACvE,OAAO,OAAO,MAAO,CAAA,EAAI,CAAA,GAAGA,CAAO,CACrC,CAeO,SAASC,CAAAA,CAAAA,GAAaD,CAAqD,CAAA,CAChF,IAAMN,CAAAA,CAA8B,EAEpC,CAAA,OAAAM,CAAQ,CAAA,OAAA,CAAShB,GAAQ,CAClBL,CAAAA,CAAcK,CAAG,CAAA,EAGtB,OAAO,IAAKA,CAAAA,CAAG,CAAE,CAAA,OAAA,CAASK,CAAQ,EAAA,CAChC,IAAMa,CAAAA,CAAclB,EAAIK,CAAG,CAAA,CACrBc,CAAcT,CAAAA,CAAAA,CAAOL,CAAG,CAG1BV,CAAAA,CAAAA,CAAcuB,CAAW,CAAA,EAAKvB,EAAcwB,CAAW,CAAA,CACzDT,CAAOL,CAAAA,CAAG,EAAIY,CAAUE,CAAAA,CAAAA,CAAaD,CAAW,CAAA,CAIhDR,EAAOL,CAAG,CAAA,CAAIH,CAAUgB,CAAAA,CAAW,EAEvC,CAAC,EACH,CAAC,CAAA,CAEMR,CACT,CAkBO,SAASU,EACdC,CAAAA,CAAAA,CACAC,CACAC,CAAAA,CAAAA,CAAqC,EAAC,CACjB,CACrB,OAAOD,CAAAA,CAAU,MAAO,CAAA,CAACE,EAAgCnB,CAAgB,GAAA,CACvE,GAAIN,CAAAA,CAAWsB,EAAgBhB,CAAG,CAAA,CAAG,CACnC,IAAMoB,CAASF,CAAAA,CAAAA,CAAWlB,CAAG,CAAA,EAAKA,EAClCmB,CAAUC,CAAAA,CAAM,CAAIJ,CAAAA,CAAAA,CAAehB,CAAG,EACxC,CACA,OAAOmB,CACT,EAAG,EAAE,CACP,CC9OO,SAASE,EAAAA,CAAWC,CAAqB,CAAA,CAC9C,OAAIA,CAAI,CAAA,MAAA,GAAW,CACVA,CAAAA,CAAAA,CACFA,EAAI,MAAO,CAAA,CAAC,CAAE,CAAA,WAAA,GAAgBA,CAAI,CAAA,KAAA,CAAM,CAAC,CAClD,CAeO,SAASC,EAAAA,CAAaD,CAAqB,CAAA,CAEhD,OAAOA,CACJ,CAAA,OAAA,CAAQ,yBAA2B,CAAA,SAAS,EAC5C,OAAQ,CAAA,oBAAA,CAAsB,OAAO,CAAA,CACrC,aACL,CAeO,SAASE,EAAAA,CAAaF,CAAqB,CAAA,CAChD,OAAOA,CAAAA,CAAI,QAAQ,WAAa,CAAA,CAACb,CAAGgB,CAAAA,CAAAA,GAASA,EAAK,WAAY,EAAC,CACjE,CAgBO,SAASC,EAASJ,CAAAA,CAAAA,CAAaK,CAAiB,CAAA,EAAA,CAAIC,CAAmB,CAAA,KAAA,CAAe,CAC3F,GAAIN,EAAI,MAAUK,EAAAA,CAAAA,CAChB,OAAOL,CAAAA,CAGT,IAAMO,CAAkBF,CAAAA,CAAAA,CAASC,CAAS,CAAA,MAAA,CAC1C,OAAON,CAAI,CAAA,SAAA,CAAU,CAAGO,CAAAA,CAAe,CAAID,CAAAA,CAC7C,CAeO,SAASE,GAAaH,CAAgBI,CAAAA,CAAAA,CAAQ,gEAA0E,CAAA,CAC7H,IAAI1B,CAAS,CAAA,EAAA,CACP2B,CAAcD,CAAAA,CAAAA,CAAM,OAE1B,IAASE,IAAAA,CAAAA,CAAI,CAAGA,CAAAA,CAAAA,CAAIN,EAAQM,CAC1B5B,EAAAA,CAAAA,CAAAA,EAAU0B,CAAM,CAAA,MAAA,CAAO,KAAK,KAAM,CAAA,IAAA,CAAK,MAAO,EAAA,CAAIC,CAAW,CAAC,CAAA,CAGhE,OAAO3B,CACT,CAaO,SAAS6B,EAAAA,CAAWC,CAAsB,CAAA,CAC/C,IAAMC,CAAAA,CAAoC,CACxC,GAAA,CAAK,QACL,GAAK,CAAA,MAAA,CACL,GAAK,CAAA,MAAA,CACL,IAAK,QACL,CAAA,GAAA,CAAM,OACN,CAAA,GAAA,CAAK,SACL,GAAK,CAAA,QAAA,CACL,GAAK,CAAA,QACP,CAEA,CAAA,OAAOD,CAAK,CAAA,OAAA,CAAQ,cAAeE,CAAKD,EAAAA,CAAAA,CAAUC,CAAC,CAAA,EAAKA,CAAC,CAC3D,CAeO,SAASC,EAAAA,CAAWC,EAAsB,CAC/C,GAAI,CACF,OAAO,CAAQ,CAAA,IAAI,GAAIA,CAAAA,CAAG,CAC5B,CACM,KAAA,CACJ,OAAO,MACT,CACF,CAiBO,SAASC,EAAaC,CAAAA,CAAAA,CAAwB,CAEnD,OADW,qCAAA,CACD,IAAKA,CAAAA,CAAK,CACtB,CAeO,SAASC,EAAcpB,CAAAA,CAAAA,CAAsB,CAClD,OAAOA,CAAAA,CAAI,IAAK,EAAA,CAAE,SAAW,CAC/B,CAeO,SAASqB,EAAAA,CAAc7E,EAA8B,CAC1D,IAAMwD,CAAM,CAAA,MAAA,CAAOxD,CAAG,CAAA,CACtB,OAAIE,CAAAA,CAAMsD,CAAG,CACJxD,CAAAA,CAAAA,CAEF,CAAGA,EAAAA,CAAG,KACf,CAiBO,SAAS8E,CAAYtB,CAAAA,CAAAA,CAAsB,CAChD,OAAKzD,CAAAA,CAASyD,CAAG,CAAA,CAIVA,CAAI,CAAA,OAAA,CAAQ,SAAW,CAAA,GAAG,EAAE,OAAQ,CAAA,SAAA,CAAW,GAAG,CAAA,CAHhDA,CAIX,CAwCO,SAASuB,EAAsBvB,CAAAA,CAAAA,CAAqBnB,EAAoD,CAE7G,GAAI,CAACmB,CAAAA,CACH,OAAOnB,CAAAA,GAAiB,MAAYA,CAAAA,CAAAA,CAAe,EAIrD,CAAA,GAAI,CAACtC,CAAAA,CAASyD,CAAG,CACf,CAAA,OAAOnB,CAAiB,GAAA,MAAA,CAAYA,EAAemB,CAGrD,CAAA,IAAMwB,CAAaxB,CAAAA,CAAAA,CAAI,MAiBvB,CAAA,GAAI,EAZDwB,CAAAA,CAAW,WAAW,GAAG,CAAA,EAAKA,CAAW,CAAA,QAAA,CAAS,GAAG,CAElDA,EAAAA,CAAAA,CAAW,UAAW,CAAA,GAAG,GAAKA,CAAW,CAAA,QAAA,CAAS,GAAG,CAAA,EAErDA,CAAW,CAAA,UAAA,CAAW,GAAG,CAAA,EAAKA,EAAW,QAAS,CAAA,GAAG,CAEtD,EAAA,kCAAA,CAAmC,KAAKA,CAAU,CAAA,EAElD,CAAC,MAAA,CAAQ,QAAS,MAAM,CAAA,CAAE,QAASA,CAAAA,CAAU,CAKhD,CAAA,CAAA,OAAO3C,CAAiB,GAAA,MAAA,CAAYA,EAAemB,CAGrD,CAAA,GAAI,CACF,OAAO,KAAK,KAAMwB,CAAAA,CAAU,CAC9B,CAAA,MACOC,EAAO,CACZ,OAAA,OAAA,CAAQ,KAAM,CAAA,sEAAA,CAAsBzB,CAAK,CAAA,4BAAA,CAAUyB,CAAK,CAAA,CACjD5C,IAAiB,MAAYA,CAAAA,CAAAA,CAAemB,CACrD,CACF,CCnSO,SAAS0B,EAAAA,CAAUC,CAAYnD,CAAAA,CAAAA,CAAc,CAClD,OAAOmD,CAAAA,CAAM,MAAOhB,CAAAA,CAAAA,EAAKA,IAAMnC,CAAI,CACrC,CAiBO,SAASoD,GAAUD,CAAiB,CAAA,CACzC,OAAO,KAAA,CAAM,KAAK,IAAI,GAAA,CAAIA,CAAK,CAAC,CAClC,CAkBO,SAASE,EAASF,CAAAA,CAAAA,CAAYG,CAAqB,CAAA,CACxD,GAAIA,CAAAA,EAAQ,EACV,OAAO,CAACH,CAAK,CAAA,CAEf,IAAM5C,CAAgB,CAAA,EACtB,CAAA,IAAA,IAAS4B,EAAI,CAAGA,CAAAA,CAAAA,CAAIgB,CAAM,CAAA,MAAA,CAAQhB,CAAKmB,EAAAA,CAAAA,CACrC/C,CAAO,CAAA,IAAA,CAAK4C,EAAM,KAAMhB,CAAAA,CAAAA,CAAGA,CAAImB,CAAAA,CAAI,CAAC,CAGtC,CAAA,OAAO/C,CACT,CAgBO,SAASgD,EAAQJ,CAAAA,CAAAA,CAA2B,CACjD,OAAOA,CAAM,CAAA,MAAA,CAAS,CAAIA,CAAAA,CAAAA,CAAMA,EAAM,MAAS,CAAA,CAAC,CAAI,CAAA,MACtD,CAgBO,SAASK,EAAAA,CAASL,CAA2B,CAAA,CAClD,OAAOA,CAAM,CAAA,MAAA,CAAS,CAAIA,CAAAA,CAAAA,CAAM,CAAC,CAAI,CAAA,MACvC,CAeO,SAASM,GAAWN,CAAiB,CAAA,CAC1C,IAAM5C,CAAAA,CAAS,CAAC,GAAG4C,CAAK,CAExB,CAAA,IAAA,IAAShB,EAAI5B,CAAO,CAAA,MAAA,CAAS,CAAG4B,CAAAA,CAAAA,CAAI,CAAGA,CAAAA,CAAAA,EAAAA,CAAK,CAC1C,IAAMuB,EAAI,IAAK,CAAA,KAAA,CAAM,IAAK,CAAA,MAAA,IAAYvB,CAAI,CAAA,CAAA,CAAE,CAC5C,CAAA,CAAC5B,EAAO4B,CAAC,CAAA,CAAG5B,CAAOmD,CAAAA,CAAC,CAAC,CAAA,CAAI,CAACnD,CAAAA,CAAOmD,CAAC,CAAQnD,CAAAA,CAAAA,CAAO4B,CAAC,CAAM,EAC1D,CAEA,OAAO5B,CACT,CAiBO,SAASoD,EAAUR,CAAAA,CAAAA,CAA2B,CACnD,GAAIA,CAAM,CAAA,MAAA,GAAW,CACnB,CAAA,OACF,IAAMS,CAAQ,CAAA,IAAA,CAAK,KAAM,CAAA,IAAA,CAAK,QAAWT,CAAAA,CAAAA,CAAM,MAAM,CAAA,CACrD,OAAOA,CAAMS,CAAAA,CAAK,CACpB,CAoBO,SAASC,EAAWC,CAAAA,CAAAA,CAAQC,CAAiB,CAAA,CAClD,OAAID,CAAE,CAAA,MAAA,GAAWC,CAAE,CAAA,MAAA,CACV,MACFD,CAAE,CAAA,KAAA,CAAM,CAAC9D,CAAAA,CAAM4D,IAAU5D,CAAS+D,GAAAA,CAAAA,CAAEH,CAAK,CAAC,CACnD,CAmCO,SAASI,EAAAA,CAA+Cb,EAAYc,CAAuC,CAAA,CAChH,OAAOd,CAAAA,CAAM,OAAO,CAAC5C,CAAAA,CAAQP,CAAS,GAAA,CACpC,IAAME,CAAM+D,CAAAA,CAAAA,CAAMjE,CAAI,CAAA,CACtB,QAACO,CAAOL,CAAAA,CAAG,CAAIK,CAAAA,CAAAA,CAAOL,CAAG,CAAK,EAAA,EAAI,EAAA,IAAA,CAAKF,CAAI,CACpCO,CAAAA,CACT,CAAG,CAAA,EAAoB,CACzB,CA4CO,SAAS2D,EAAAA,CACdC,CACA,CAAA,CACE,IAAAC,CAAAA,CAAAA,CAAO,QACP,KAAA7E,CAAAA,CAAAA,CAAQ,EACR,CAAA,QAAA,CAAA8E,EAAW,OACb,CAAA,CAII,EAAC,CACA,CACL,OAAO,CACL,CACE,CAACD,CAAI,EAAG,cAAA,CACR,CAACC,CAAQ,EAAG9E,CACd,CAAA,CACA,GAAG4E,CACL,CACF,CAmBO,SAASG,CACdC,CAAAA,CAAAA,CACAC,EACAC,CAAmB,CAAA,UAAA,CACnBC,CAAyB,CAAA,IAAA,CAClB,CACP,OAAOH,CAAK,CAAA,GAAA,CAAKI,GAAS,CACxB,IAAMC,CAAU,CAAA,CAAE,GAAGD,CAAK,CAAA,CAG1B,OAAO,MAAA,CAAA,OAAA,CAAQH,CAAS,CAAE,CAAA,OAAA,CAAQ,CAAC,CAACK,CAAQvD,CAAAA,CAAM,CAAM,GAAA,CAClD1B,EAAWgF,CAASC,CAAAA,CAAM,CAC5BD,GAAAA,CAAAA,CAAQtD,CAAM,CAAIsD,CAAAA,CAAAA,CAAQC,CAAM,CAAA,CAC5BH,GACF,OAAOE,CAAAA,CAAQC,CAAM,CAAA,EAG3B,CAAC,CAAA,CAGG,KAAM,CAAA,OAAA,CAAQD,EAAQH,CAAQ,CAAC,CACjCG,GAAAA,CAAAA,CAAQH,CAAQ,CAAIH,CAAAA,CAAAA,CAClBM,CAAQH,CAAAA,CAAQ,EAChBD,CACAC,CAAAA,CAAAA,CACAC,CACF,CAAA,CAAA,CAGKE,CACT,CAAC,CACH,CAoBO,SAASE,EACdP,CACAQ,CAAAA,CAAAA,CACAN,CAAmB,CAAA,UAAA,CACZ,CACP,OAAOF,CAAAA,CAAK,GAAKI,CAAAA,CAAAA,EAAS,CACxB,IAAMK,CAAAA,CAAWL,CAAKF,CAAAA,CAAQ,CAAIK,CAAAA,CAAAA,CAAcH,CAAKF,CAAAA,CAAQ,EAAGM,CAAaN,CAAAA,CAAQ,CAAI,CAAA,GACzF,OAAOM,CAAAA,CAAYJ,CAAMK,CAAAA,CAAQ,CACnC,CAAC,CACH,CAqBO,SAASC,EACdC,CAAAA,CAAAA,CACAhF,CACmB,CAAA,CACnB,IAAMK,CAA4B,CAAA,EAClC,CAAA,IAAA,IAAWP,KAAQkF,CAAK,CAAA,CACtB,IAAMC,CAAAA,CAAInF,EAAKE,CAAG,CAAA,CACbiF,CAEL5E,GAAAA,CAAAA,CAAO4E,CAAW,CAAA,CAAInF,CACxB,EAAA,CACA,OAAOO,CACT,CA6BO,SAAS6E,EAAAA,CACdF,EACAhF,CACK,CAAA,CACL,OAAIgF,CAAAA,CAAI,OACCA,CAAI,CAAA,GAAA,CAAKlF,CACV,EAAA,OAAOA,EAAKE,CAAG,CAAA,EAAM,QAEhB,CAAA,CACL,GAAGF,CACH,CAAA,CAACE,CAAG,EAAG4C,EAAY9C,CAAKE,CAAAA,CAAG,CAAW,CACxC,EAEKF,CACR,CAAA,CAGI,EACT,CC3aAqF,CAAAA,CAAM,MAAO,CAAA,OAAO,CAQpBA,CAAAA,CAAAA,CAAM,OAAOC,CAAY,CAAA,CACzBD,CAAM,CAAA,MAAA,CAAOE,CAAc,CAC3BF,CAAAA,CAAAA,CAAM,MAAOG,CAAAA,CAAa,EAC1BH,CAAM,CAAA,MAAA,CAAOI,CAAS,CAAA,CAsBf,SAASC,EAAAA,CAAWC,CAAgC,CAAA,CACzD,OAAON,CAAMM,CAAAA,CAAI,CACnB,CAeO,SAASC,EAAuBrG,CAAAA,CAAAA,CAAyB,CAC9D,GAAI,CAACtB,CAASsB,CAAAA,CAAK,CAAKA,EAAAA,CAAAA,CAAM,UAAW,CAAA,MAAA,GAAW,EAClD,CAAA,OAAO,OAET,IAAMoG,CAAAA,CAAO,IAAI,IAAA,CAAKpG,CAAK,CAC3B,CAAA,OAAO,CAACrB,CAAAA,CAAMyH,EAAK,OAAQ,EAAC,CAC9B,CAeO,SAASE,CAAAA,CAAoBtG,CAAiB,CAAA,CAEnD,OAAIqG,EAAuBrG,CAAAA,CAAK,CACvBA,CAAAA,CAAAA,CAILtB,EAASsB,CAAK,CAAA,EAAK,MAAOA,CAAAA,CAAK,EAAE,MAAW,GAAA,EAAA,CACvCA,CAAQ,CAAA,GAAA,CAGVA,CACT,CAeO,SAASuG,EAAAA,CAAWH,EAAgBI,CAAS,CAAA,YAAA,CAAsB,CACxE,OAAOV,EAAMQ,CAAoBF,CAAAA,CAAI,CAAC,CAAA,CAAE,OAAOI,CAAM,CACvD,CAcO,SAASC,EAAeL,CAAAA,CAAAA,CAAgB,CAC7C,OAAOG,GAAWH,CAAM,CAAA,qBAAqB,CAC/C,CAWO,SAASM,EAAc,EAAA,CAC5B,OAAO,IAAA,CAAK,KACd,CAeO,SAASC,EAAAA,CAAUC,EAAuB,CAC/C,IAAMC,CAAIf,CAAAA,CAAAA,CAAMc,CAAO,CACvB,CAAA,GAAI,CAACC,CAAAA,CAAE,SACL,CAAA,MAAM,IAAI,SAAA,CAAU,4CAAS,CAC/B,CAAA,OAAOA,CAAE,CAAA,MAAA,EACX,CAgBO,SAASC,EAAAA,CAAKC,EAAiBC,CAAiBC,CAAAA,CAAAA,CAA+B,KAAe,CAAA,CACnG,OAAOnB,CAAMQ,CAAAA,CAAAA,CAAoBS,CAAK,CAAC,EAAE,IAAKjB,CAAAA,CAAAA,CAAMQ,CAAoBU,CAAAA,CAAK,CAAC,CAAA,CAAGC,CAAI,CACvF,CAgBO,SAASC,CAAAA,CAAId,CAAgBe,CAAAA,CAAAA,CAAgBF,EAAuB,KAAa,CAAA,CACtF,OAAOnB,CAAAA,CAAMQ,EAAoBF,CAAI,CAAC,CAAE,CAAA,GAAA,CAAIe,CAAQF,CAAAA,CAAI,CAAE,CAAA,MAAA,EAC5D,CAcO,SAASG,EAAQhB,CAAAA,CAAAA,CAAgBiB,EAAoB,CAC1D,OAAOH,CAAIZ,CAAAA,CAAAA,CAAoBF,CAAI,CAAGiB,CAAAA,CAAAA,CAAM,KAAK,CACnD,CAeO,SAASC,EAAAA,CAAUlB,CAAgBmB,CAAAA,CAAAA,CAAsB,CAC9D,OAAOL,CAAAA,CAAIZ,CAAoBF,CAAAA,CAAI,EAAGmB,CAAQ,CAAA,OAAO,CACvD,CAeO,SAASC,EAASpB,CAAAA,CAAAA,CAAgBqB,CAAqB,CAAA,CAC5D,OAAOP,CAAAA,CAAIZ,CAAoBF,CAAAA,CAAI,EAAGqB,CAAO,CAAA,MAAM,CACrD,CAgBO,SAASC,EAAatB,CAAAA,CAAAA,CAAgBuB,CAAgB,CAAA,KAAA,CAAe,CAE1E,IAAMC,CAAAA,CADI9B,CAAMQ,CAAAA,CAAAA,CAAoBF,CAAI,CAAC,CAC3B,CAAA,GAAA,GACd,OAAIuB,CAAAA,CACKC,CAAQ,GAAA,CAAA,CAAI,EAAIA,CAAM,CAAA,CAAA,CAExBA,CACT,CAgBO,SAASC,EAAczB,CAAAA,CAAAA,CAAgB0B,CAAqBC,CAAAA,CAAAA,CAA4B,CAC7F,IAAMlB,CAAIf,CAAAA,CAAAA,CAAMQ,EAAoBF,CAAI,CAAC,CACzC,CAAA,OAAOS,EAAE,OAAQf,CAAAA,CAAAA,CAAMQ,CAAoBwB,CAAAA,CAAS,CAAC,CAAC,CAAA,EAAKjB,CAAE,CAAA,QAAA,CAASf,EAAMQ,CAAoByB,CAAAA,CAAO,CAAC,CAAC,CAC3G,CAeO,SAASC,EAAeC,CAAAA,CAAAA,CAAcC,EAAuB,CAClE,OAAOpC,CAAM,CAAA,IAAI,KAAKmC,CAAMC,CAAAA,CAAAA,CAAO,CAAC,CAAC,CAAE,CAAA,WAAA,EACzC,CAeO,SAASC,EAAQ/B,CAAAA,CAAAA,CAAgBgC,CAA6B,CAAA,CAGnE,OAFAhC,CAAOE,CAAAA,CAAAA,CAAoBF,CAAI,CAAA,CAC/BgC,EAAWA,CAAY9B,EAAAA,CAAAA,CAAoB8B,CAAQ,CAAA,CAC/CA,CACKtC,CAAAA,CAAAA,CAAMM,CAAI,CAAA,CAAE,KAAKN,CAAMsC,CAAAA,CAAQ,CAAC,CAAA,CAClCtC,EAAMM,CAAI,CAAA,CAAE,OAAQ,EAC7B,CAeO,SAASiC,EAAAA,CAAQjC,CAAgBa,CAAAA,CAAAA,CAAwB,CAC9D,OAAOnB,CAAMQ,CAAAA,CAAAA,CAAoBF,CAAI,CAAC,CAAA,CAAE,OAAQa,CAAAA,CAAI,EAAE,MAAO,EAC/D,CAeO,SAASqB,GAAMlC,CAAgBa,CAAAA,CAAAA,CAAwB,CAC5D,OAAOnB,EAAMQ,CAAoBF,CAAAA,CAAI,CAAC,CAAA,CAAE,MAAMa,CAAI,CAAA,CAAE,MAAO,EAC7D,CAeO,SAASsB,EAAAA,CAAoBnC,CAAwB,CAAA,CAC1D,IAAMS,CAAIf,CAAAA,CAAAA,CAAMQ,CAAoBF,CAAAA,CAAI,CAAC,CAAA,CACnCM,CAAMZ,CAAAA,CAAAA,GAEZ,OAAIe,CAAAA,CAAE,MAAOH,CAAAA,CAAAA,CAAK,KAAK,CACd,CAAA,CAAA,aAAA,EAAMG,CAAE,CAAA,MAAA,CAAO,OAAO,CAAC,CAAA,CAAA,CAC5BA,CAAE,CAAA,MAAA,CAAOH,EAAI,QAAS,CAAA,CAAA,CAAG,KAAK,CAAA,CAAG,KAAK,CACjC,CAAA,CAAA,aAAA,EAAMG,CAAE,CAAA,MAAA,CAAO,OAAO,CAAC,CAAA,CAAA,CAC5BA,CAAE,CAAA,MAAA,CAAOH,EAAI,GAAI,CAAA,CAAA,CAAG,KAAK,CAAA,CAAG,KAAK,CAAA,CAC5B,CAAMG,aAAAA,EAAAA,CAAAA,CAAE,OAAO,OAAO,CAAC,CAC5BA,CAAAA,CAAAA,CAAAA,CAAE,OAAOH,CAAK,CAAA,MAAM,CACfG,CAAAA,CAAAA,CAAE,OAAO,sBAAY,CAAA,CACvBA,CAAE,CAAA,MAAA,CAAO,gCAAiB,CACnC,CAqBO,SAAS2B,EAAAA,CAAepC,EAAwB,CACrD,IAAMS,CAAIf,CAAAA,CAAAA,CAAMQ,EAAoBF,CAAI,CAAC,CACnCM,CAAAA,CAAAA,CAAMZ,GAGZ,CAAA,GAAIe,CAAE,CAAA,MAAA,CAAOH,CAAK,CAAA,KAAK,CACrB,CAAA,OAAOG,EAAE,MAAO,CAAA,OAAO,CAIzB,CAAA,GAAIA,EAAE,MAAOH,CAAAA,CAAAA,CAAI,QAAS,CAAA,CAAA,CAAG,KAAK,CAAG,CAAA,KAAK,CACxC,CAAA,OAAO,CAAMG,aAAAA,EAAAA,CAAAA,CAAE,MAAO,CAAA,OAAO,CAAC,CAIhC,CAAA,CAAA,IAAM4B,CAAW/B,CAAAA,CAAAA,CAAI,KAAKG,CAAG,CAAA,KAAK,CAClC,CAAA,GAAI4B,GAAY,CAAKA,EAAAA,CAAAA,CAAW,CAAG,CAAA,CACjC,IAAMC,CAAAA,CAAW,CAAC,oBAAA,CAAO,qBAAO,oBAAO,CAAA,oBAAA,CAAO,oBAAO,CAAA,oBAAA,CAAO,oBAAK,CAC3DC,CAAAA,CAAAA,CAAW9B,CAAE,CAAA,GAAA,GACnB,OAAO6B,CAAAA,CAASC,CAAQ,CAAA,EAAK,oBAC/B,CAGA,OAAI9B,CAAE,CAAA,MAAA,CAAOH,EAAK,MAAM,CAAA,CACfG,CAAE,CAAA,MAAA,CAAO,kBAAQ,CAInBA,CAAAA,CAAAA,CAAE,MAAO,CAAA,4BAAa,CAC/B,CA6CO,SAAS+B,EAAeC,CAAAA,CAAAA,CAAmBrC,CAAkC,CAAA,CAElF,IAAMsC,CAAAA,CAAW,KAAK,GAAID,CAAAA,CAAS,CAG7BE,CAAAA,CAAAA,CAAS,IACTC,CAAS,CAAA,EAAA,CAAKD,CACdE,CAAAA,CAAAA,CAAO,GAAKD,CACZE,CAAAA,CAAAA,CAAM,EAAKD,CAAAA,CAAAA,CACXE,CAAQ,CAAA,EAAA,CAAKD,CACbE,CAAAA,CAAAA,CAAO,IAAMF,CAIbzB,CAAAA,CAAAA,CAAQ,IAAK,CAAA,KAAA,CAAMqB,EAAWM,CAAI,CAAA,CAClC7B,CAAS,CAAA,IAAA,CAAK,MAAOuB,CAAWM,CAAAA,CAAAA,CAAQD,CAAK,CAAA,CAC7C9B,CAAO,CAAA,IAAA,CAAK,KAAOyB,CAAAA,CAAAA,CAAWK,EAASD,CAAG,CAAA,CAC1CG,CAAQ,CAAA,IAAA,CAAK,MAAOP,CAAWI,CAAAA,CAAAA,CAAOD,CAAI,CAAA,CAC1CK,EAAU,IAAK,CAAA,KAAA,CAAOR,CAAWG,CAAAA,CAAAA,CAAQD,CAAM,CAC/CO,CAAAA,CAAAA,CAAU,IAAK,CAAA,KAAA,CAAOT,EAAWE,CAAUD,CAAAA,CAAM,CAGvD,CAAA,GAAIvC,EAAQ,CACV,IAAMgD,CAAgBhD,CAAAA,CAAAA,CAAO,MAGvBiD,CAAAA,CAAAA,CAAa,IAAK,CAAA,KAAA,CAAMX,CAAWM,CAAAA,CAAI,CACvCM,CAAAA,CAAAA,CAAc,KAAK,KAAMZ,CAAAA,CAAAA,CAAWK,CAAK,CAAA,CACzCQ,EAAY,IAAK,CAAA,KAAA,CAAMb,CAAWI,CAAAA,CAAG,EACrCU,CAAa,CAAA,IAAA,CAAK,KAAMd,CAAAA,CAAAA,CAAWG,CAAI,CAAA,CACvCY,CAAe,CAAA,IAAA,CAAK,MAAMf,CAAWE,CAAAA,CAAM,CAC3Cc,CAAAA,CAAAA,CAAe,KAAK,KAAMhB,CAAAA,CAAAA,CAAWC,CAAM,CAAA,CAG3CgB,EAA8D,CAClE,EAAA,CAAI,CAAE,KAAA,CAAON,CAAY,CAAA,OAAA,CAAShC,CAAM,CAAA,CACxC,EAAG,CAAE,KAAA,CAAOgC,CAAY,CAAA,OAAA,CAAShC,CAAM,CACvC,CAAA,EAAA,CAAI,CAAE,KAAA,CAAOiC,EAAa,OAASnC,CAAAA,CAAO,CAC1C,CAAA,CAAA,CAAG,CAAE,KAAOmC,CAAAA,CAAAA,CAAa,OAASnC,CAAAA,CAAO,EACzC,EAAI,CAAA,CAAE,KAAOoC,CAAAA,CAAAA,CAAW,QAAStC,CAAK,CAAA,CACtC,CAAG,CAAA,CAAE,MAAOsC,CAAW,CAAA,OAAA,CAAStC,CAAK,CAAA,CACrC,EAAI,CAAA,CAAE,KAAOuC,CAAAA,CAAAA,CAAY,QAASP,CAAM,CAAA,CACxC,CAAG,CAAA,CAAE,MAAOO,CAAY,CAAA,OAAA,CAASP,CAAM,CAAA,CACvC,GAAI,CAAE,KAAA,CAAOQ,CAAc,CAAA,OAAA,CAASP,CAAQ,CAAA,CAC5C,CAAG,CAAA,CAAE,MAAOO,CAAc,CAAA,OAAA,CAASP,CAAQ,CAAA,CAC3C,GAAI,CAAE,KAAA,CAAOQ,CAAc,CAAA,OAAA,CAASP,CAAQ,CAC5C,CAAA,CAAA,CAAG,CAAE,KAAA,CAAOO,CAAc,CAAA,OAAA,CAASP,CAAQ,CAC7C,EAGA,GAAIQ,CAAAA,CAAQP,CAAa,CAAA,CACvB,OAAOO,CAAQP,CAAAA,CAAa,CAAE,CAAA,KAAA,CAIhC,IAAIxI,CAASwF,CAAAA,CAAAA,CAGPwD,CAAe,CAAA,CAAC,KAAM,IAAM,CAAA,IAAA,CAAM,IAAM,CAAA,IAAA,CAAM,KAAM,GAAK,CAAA,GAAA,CAAK,GAAK,CAAA,GAAA,CAAK,IAAK,GAAG,CAAA,CAEtF,IAAWC,IAAAA,CAAAA,IAASD,EAClB,GAAIhJ,CAAAA,CAAO,QAASiJ,CAAAA,CAAK,CAAKF,EAAAA,CAAAA,CAAQE,CAAK,CAAA,CAAG,CAC5C,GAAM,CAAE,OAAAC,CAAAA,CAAQ,EAAIH,CAAQE,CAAAA,CAAK,CAC3BE,CAAAA,CAAAA,CAAcD,EAAQ,QAAS,EAAA,CAAE,QAASD,CAAAA,CAAAA,CAAM,MAAQ,CAAA,GAAG,CACjEjJ,CAAAA,CAAAA,CAASA,EAAO,OAAQ,CAAA,IAAI,MAAOiJ,CAAAA,CAAAA,CAAO,GAAG,CAAGE,CAAAA,CAAW,EAC7D,CAGF,OAAOnJ,CACT,CAGA,IAAMoJ,CAAAA,CAAkB,EAAC,CAEzB,OAAI3C,CAAAA,CAAQ,GACV2C,CAAM,CAAA,IAAA,CAAK,CAAG3C,EAAAA,CAAK,QAAG,CAGpBF,CAAAA,CAAAA,CAAS,CACX6C,EAAAA,CAAAA,CAAM,KAAK,CAAG7C,EAAAA,CAAM,CAAI,YAAA,CAAA,CAAA,CAGtBF,EAAO,CACT+C,EAAAA,CAAAA,CAAM,IAAK,CAAA,CAAA,EAAG/C,CAAI,CAAG,MAAA,CAAA,CAAA,CAGnBgC,CAAQ,CAAA,CAAA,EACVe,EAAM,IAAK,CAAA,CAAA,EAAGf,CAAK,CAAA,YAAA,CAAI,EAGrBC,CAAU,CAAA,CAAA,EACZc,CAAM,CAAA,IAAA,CAAK,CAAGd,EAAAA,CAAO,CAAI,YAAA,CAAA,CAAA,CAAA,CAGvBC,EAAU,CAAKa,EAAAA,CAAAA,CAAM,MAAW,GAAA,CAAA,GAClCA,EAAM,IAAK,CAAA,CAAA,EAAGb,CAAO,CAAA,MAAA,CAAG,EAGnBa,CAAM,CAAA,IAAA,CAAK,EAAE,CACtB,CCxjBO,SAASC,EAAMC,CAAAA,CAAAA,CAAaC,EAAY,CAAW,CAAA,CACxD,IAAMC,CAAAA,CAAS,IAAMD,CACrB,CAAA,OAAO,IAAK,CAAA,KAAA,CAAMD,EAAME,CAAM,CAAA,CAAIA,CACpC,CAeO,SAASC,EAAAA,CAAgBH,CAAaI,CAAAA,CAAAA,CAAyB,CACpE,OAAOJ,CAAAA,CAAI,cAAeI,CAAAA,CAAM,CAClC,CAoBO,SAASC,EACd3K,CAAAA,CAAAA,CACA4E,EAKI,EAAC,CACG,CACR,GAAM,CACJ,QAAAgG,CAAAA,CAAAA,CAAW,KACX,CAAA,MAAA,CAAAF,EAAS,OACT,CAAA,qBAAA,CAAAG,CAAwB,CAAA,CAAA,CACxB,sBAAAC,CAAwB,CAAA,CAC1B,CAAIlG,CAAAA,CAAAA,CAEJ,OAAO,IAAI,IAAA,CAAK,YAAa8F,CAAAA,CAAAA,CAAQ,CACnC,KAAA,CAAO,UACP,CAAA,QAAA,CAAAE,EACA,qBAAAC,CAAAA,CAAAA,CACA,qBAAAC,CAAAA,CACF,CAAC,CAAE,CAAA,MAAA,CAAO9K,CAAK,CACjB,CAgBO,SAAS+K,EAAAA,CAAMT,CAAaU,CAAAA,CAAAA,CAAaC,CAAqB,CAAA,CACnE,OAAO,IAAA,CAAK,IAAI,IAAK,CAAA,GAAA,CAAIX,CAAKU,CAAAA,CAAG,EAAGC,CAAG,CACzC,CAeO,SAASC,GAAUF,CAAaC,CAAAA,CAAAA,CAAqB,CAC1D,OAAAD,CAAM,CAAA,IAAA,CAAK,IAAKA,CAAAA,CAAG,EACnBC,CAAM,CAAA,IAAA,CAAK,KAAMA,CAAAA,CAAG,EACb,IAAK,CAAA,KAAA,CAAM,IAAK,CAAA,MAAA,IAAYA,CAAMD,CAAAA,CAAAA,CAAM,CAAE,CAAA,CAAA,CAAIA,CACvD,CAeO,SAASG,EAAOb,CAAAA,CAAAA,CAAsB,CAC3C,OAAOA,CAAAA,CAAM,CAAM,GAAA,CACrB,CAeO,SAASc,EAAAA,CAAMd,CAAsB,CAAA,CAC1C,OAAO,CAACa,EAAAA,CAAOb,CAAG,CACpB,CAiBO,SAASe,EAAWrL,CAAAA,CAAAA,CAAesL,EAAef,CAAY,CAAA,CAAA,CAAW,CAC9E,OAAIe,IAAU,CACL,CAAA,CAAA,CACFjB,EAAOrK,CAAAA,CAAAA,CAAQsL,EAAS,GAAKf,CAAAA,CAAS,CAC/C,CAgBO,SAASgB,EAA4BjB,CAAAA,CAAAA,CAAakB,CAAiB,CAAA,CAAA,CAAW,CACnF,OAAKlB,CAAAA,CAGDA,CAAO,EAAA,GAAA,CAEF,IAAIA,CAAM,CAAA,GAAA,EAAO,OAAQkB,CAAAA,CAAc,CAAC,CAGxClB,MAAAA,CAAAA,CAAAA,CAAAA,CAAI,QAAS,EAAA,CAPb,GASX,CCrLO,SAASmB,EAAAA,CAAMC,EAA2B,CAC/C,OAAO,IAAI,OAAA,CAAQC,GAAW,UAAWA,CAAAA,CAAAA,CAASD,CAAE,CAAC,CACvD,CCOO,SAASE,EAAkBC,CAAAA,CAAAA,CAAwD,CACxF,GAAI,CAACA,CACH,CAAA,OAAO,GAET,IAAMC,CAAAA,CAAc3K,CAAoB0K,CAAAA,CAAM,EAE9C,OAAIC,CAAAA,CACK,CAAIA,CAAAA,EAAAA,CAAW,GAEjB,EACT","file":"index.js","sourcesContent":["/**\n * @module 类型检查\n * @description 提供各种数据类型检查函数,用于判断值的类型、判断对象特性等\n */\n\n/**\n * 检查值是否为字符串类型\n * @param val 要检查的值\n * @returns 如果是字符串则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isString('hello') // true\n * isString(123) // false\n * isString(new String('hello')) // false(注意:包装对象不是原始字符串类型)\n * ```\n */\nexport function isString(val: unknown): val is string {\n return typeof val === 'string'\n}\n\n/**\n * 检查值是否为数字类型\n * @param val 要检查的值\n * @returns 如果是数字则返回 true,否则返回 false(NaN 会返回 false)\n * @group Is\n * @example\n * ```ts\n * isNumber(123) // true\n * isNumber(-45.67) // true\n * isNumber('123') // false\n * isNumber(NaN) // false\n * isNumber(Infinity) // true\n * ```\n */\nexport function isNumber(val: unknown): val is number {\n return typeof val === 'number' && !isNaN(val)\n}\n\n/**\n * 检查值是否为布尔类型\n * @param val 要检查的值\n * @returns 如果是布尔值则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isBoolean(true) // true\n * isBoolean(false) // true\n * isBoolean(0) // false\n * isBoolean('false') // false\n * isBoolean(new Boolean(true)) // false(包装对象不是原始布尔类型)\n * ```\n */\nexport function isBoolean(val: unknown): val is boolean {\n return typeof val === 'boolean'\n}\n\n/**\n * 检查值是否为函数类型\n * @param val 要检查的值\n * @returns 如果是函数则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isFunction(() => {}) // true\n * isFunction(function() {}) // true\n * isFunction(class {}) // true\n * isFunction(Math.sin) // true\n * isFunction({}) // false\n * ```\n */\nexport function isFunction(val: unknown): val is ((...args: any[]) => any) {\n return typeof val === 'function'\n}\n\n/**\n * 检查值是否为类构造函数\n * @param val 要检查的值\n * @returns 如果是类构造函数则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isClass(class User {}) // true\n * isClass(class extends Array {}) // true\n * isClass(Array) // true\n * isClass(Date) // true\n * isClass(function() {}) // false\n * isClass(() => {}) // false\n * isClass(Math.sin) // false\n * isClass({}) // false\n * ```\n */\nexport function isClass(val: unknown): val is new (...args: any[]) => any {\n if (!isFunction(val)) {\n return false\n }\n\n const func = val as (...args: any[]) => any\n\n // 检查函数的字符串表示是否以 class 开头(ES6 类)\n const funcStr = func.toString()\n if (/^class\\s/.test(funcStr)) {\n return true\n }\n\n // 检查是否为内置构造函数或传统构造函数\n // 构造函数通常有 prototype 属性,且 prototype.constructor 指向自己\n const funcWithPrototype = func as any\n if (funcWithPrototype.prototype && funcWithPrototype.prototype.constructor === funcWithPrototype) {\n // 进一步检查:尝试检测是否为箭头函数(箭头函数没有 prototype)\n // 或者是否为明显的工具函数(通过一些启发式规则)\n if (!/^[a-z]/.test(funcWithPrototype.name || '')) {\n // 如果函数名不是以小写字母开头,更可能是构造函数\n return true\n }\n\n // 检查是否为内置构造函数\n if (/\\[native code\\]/.test(funcStr)) {\n return true\n }\n }\n\n return false\n}\n\n/**\n * 检查值是否为对象类型(不包括 null)\n * @param val 要检查的值\n * @returns 如果是对象则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isObject({}) // true\n * isObject([]) // true(数组也是对象)\n * isObject(new Date()) // true\n * isObject(null) // false\n * isObject(undefined) // false\n * isObject(123) // false\n * ```\n */\nexport function isObject(val: unknown): val is Record<any, any> {\n return val !== null && typeof val === 'object'\n}\n\n/**\n * 检查值是否为数组类型\n * @param val 要检查的值\n * @returns 如果是数组则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isArray([]) // true\n * isArray([1, 2, 3]) // true\n * isArray(new Array()) // true\n * isArray({}) // false\n * isArray('abc') // false\n * ```\n */\nexport function isArray(val: unknown): val is any[] {\n return Array.isArray(val)\n}\n\n/**\n * 检查值是否为日期类型\n * @param val 要检查的值\n * @returns 如果是日期对象则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isDate(new Date()) // true\n * isDate(Date.now()) // false(时间戳是数字,不是日期对象)\n * isDate('2023-05-15') // false(日期字符串不是日期对象)\n * ```\n */\nexport function isDate(val: unknown): val is Date {\n return val instanceof Date\n}\n\n/**\n * 检查值是否为正则表达式\n * @param val 要检查的值\n * @returns 如果是正则表达式则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isRegExp(/abc/) // true\n * isRegExp(new RegExp('abc')) // true\n * isRegExp('/abc/') // false(字符串不是正则表达式)\n * isRegExp({}) // false\n * ```\n */\nexport function isRegExp(val: unknown): val is RegExp {\n return val instanceof RegExp\n}\n\n/**\n * 检查值是否为 Promise\n * @param val 要检查的值\n * @returns 如果是 Promise 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isPromise(Promise.resolve()) // true\n * isPromise(new Promise(() => {})) // true\n * isPromise({ then: () => {}, catch: () => {} }) // true(类 Promise 对象也会返回 true)\n * isPromise({}) // false\n * isPromise(null) // false\n * ```\n */\nexport function isPromise<T = any>(val: unknown): val is Promise<T> {\n return isObject(val) && isFunction((val as any).then) && isFunction((val as any).catch)\n}\n\n/**\n * 检查值是否为 Map\n * @param val 要检查的值\n * @returns 如果是 Map 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isMap(new Map()) // true\n * isMap(new WeakMap()) // false\n * isMap({}) // false\n * ```\n */\nexport function isMap<K = any, V = any>(val: unknown): val is Map<K, V> {\n return val instanceof Map\n}\n\n/**\n * 检查值是否为 Set\n * @param val 要检查的值\n * @returns 如果是 Set 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isSet(new Set()) // true\n * isSet(new WeakSet()) // false\n * isSet([]) // false\n * ```\n */\nexport function isSet<T = any>(val: unknown): val is Set<T> {\n return val instanceof Set\n}\n\n/**\n * 检查值是否为 Symbol\n * @param val 要检查的值\n * @returns 如果是 Symbol 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isSymbol(Symbol('foo')) // true\n * isSymbol(Symbol.for('bar')) // true\n * isSymbol('symbol') // false\n * ```\n */\nexport function isSymbol(val: unknown): val is symbol {\n return typeof val === 'symbol'\n}\n\n/**\n * 检查值是否为原始类型(string、number、boolean、symbol、bigint、null、undefined)\n * @param val 要检查的值\n * @returns 如果是原始类型则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isPrimitive('hello') // true\n * isPrimitive(123) // true\n * isPrimitive(true) // true\n * isPrimitive(Symbol()) // true\n * isPrimitive(null) // true\n * isPrimitive(undefined) // true\n * isPrimitive(BigInt(123)) // true\n * isPrimitive({}) // false\n * isPrimitive([]) // false\n * isPrimitive(() => {}) // false\n * ```\n */\nexport function isPrimitive(val: unknown): boolean {\n return val === null || (typeof val !== 'object' && typeof val !== 'function')\n}\n\n/**\n * 检查值是否为 undefined\n * @param val 要检查的值\n * @returns 如果是 undefined 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isUndefined(undefined) // true\n * isUndefined(void 0) // true\n * isUndefined(null) // false\n * isUndefined('') // false\n * ```\n */\nexport function isUndefined(val: unknown): val is undefined {\n return typeof val === 'undefined'\n}\n\n/**\n * 检查值是否为 null\n * @param val 要检查的值\n * @returns 如果是 null 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isNull(null) // true\n * isNull(undefined) // false\n * isNull(0) // false\n * isNull('') // false\n * ```\n */\nexport function isNull(val: unknown): val is null {\n return val === null\n}\n\n/**\n * 检查值是否为 null 或 undefined\n * @param val 要检查的值\n * @returns 如果是 null 或 undefined 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isNullOrUndefined(null) // true\n * isNullOrUndefined(undefined) // true\n * isNullOrUndefined(void 0) // true\n * isNullOrUndefined('') // false\n * isNullOrUndefined(0) // false\n * ```\n */\nexport function isNullOrUndefined(val: unknown): val is null | undefined {\n return isNull(val) || isUndefined(val)\n}\n\n/**\n * 检查对象是否为空对象(没有自身可枚举属性)\n * @param val 要检查的值\n * @returns 如果是空对象则返回 true,否则返回 false;如果不是对象类型则返回 false\n * @group Is\n * @example\n * ```ts\n * isEmptyObject({}) // true\n * isEmptyObject({ a: 1 }) // false\n * isEmptyObject([]) // true(空数组也会返回 true)\n * isEmptyObject(null) // false(不是对象)\n * isEmptyObject(Object.create(null)) // true\n * isEmptyObject(Object.create({ toString: () => '' })) // true(不包括继承的属性)\n * ```\n */\nexport function isEmptyObject(val: unknown): boolean {\n return isObject(val) && Object.keys(val).length === 0\n}\n\n/**\n * 检查值是否为空(null、undefined、空字符串、空数组或空对象)\n * @param val 要检查的值\n * @returns 如果是空值则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isEmpty(null) // true\n * isEmpty(undefined) // true\n * isEmpty('') // true\n * isEmpty(' ') // true(空白字符串也视为空)\n * isEmpty([]) // true\n * isEmpty({}) // true\n * isEmpty(0) // false\n * isEmpty(false) // false\n * isEmpty(' hello ') // false\n * ```\n */\nexport function isEmpty(val: unknown): boolean {\n if (isNullOrUndefined(val))\n return true\n if (isString(val))\n return val.trim().length === 0\n if (isArray(val))\n return val.length === 0\n if (isObject(val))\n return isEmptyObject(val)\n return false\n}\n\n/**\n * 检查值是否为 NaN\n * @param value 要检查的值\n * @returns 如果值是 NaN 则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isNaN(NaN) // true\n * isNaN(Number('abc')) // true\n * isNaN(0 / 0) // true\n * isNaN(123) // false\n * isNaN('123') // false(不同于全局 isNaN,此函数不会先尝试转换为数字)\n * ```\n */\nexport function isNaN(value: unknown): boolean {\n return Number.isNaN(value)\n}\n\n/**\n * 检查值是否为普通对象(由 Object 构造函数创建或对象字面量)\n *\n * @param {unknown} value - 要检查的值\n * @returns {boolean} 如果值是普通对象则返回 true,否则返回 false\n *\n * @group Is\n * @example\n * ```ts\n * isPlainObject({}) // true\n * isPlainObject({ a: 1 }) // true\n * isPlainObject(new Date()) // false\n * isPlainObject([]) // false\n * isPlainObject(null) // false\n * ```\n */\nexport function isPlainObject(value: unknown): value is Record<string, any> {\n if (typeof value !== 'object' || value === null) {\n return false\n }\n\n const proto = Object.getPrototypeOf(value)\n return proto === Object.prototype || proto === null\n}\n\n/**\n * 检查值是否为有效的手机号码\n * @param val 要检查的值\n * @returns 如果是有效的手机号码则返回 true,否则返回 false\n * @group Is\n * @example\n * ```ts\n * isMobilePhone('13812345678') // true\n * isMobilePhone(13812345678) // true\n * isMobilePhone('15987654321') // true\n * isMobilePhone(15987654321) // true\n * isMobilePhone('18600000000') // true\n * isMobilePhone(18600000000) // true\n * isMobilePhone('12345678901') // false(不是有效号段)\n * isMobilePhone(12345678901) // false(不是有效号段)\n * isMobilePhone('1381234567') // false(长度不对)\n * isMobilePhone(1381234567) // false(长度不对)\n * isMobilePhone('138123456789') // false(长度不对)\n * isMobilePhone(138123456789) // false(长度不对)\n * isMobilePhone('') // false\n * isMobilePhone(null) // false\n * ```\n */\nexport function isMobilePhone(val: unknown): boolean {\n // 如果既不是字符串也不是数字,直接返回 false\n if (!isString(val) && !isNumber(val)) {\n return false\n }\n\n // 转换为字符串进行验证\n const phoneStr = String(val)\n\n // 中国大陆手机号码正则表达式\n // 11位数字,以1开头,第二位是3-9的数字\n const mobileRegex = /^1[3-9]\\d{9}$/\n\n return mobileRegex.test(phoneStr)\n}\n","/**\n * @module 对象操作\n * @description 提供各种对象处理函数,包括对象深拷贝、合并、转换等实用功能\n */\n\nimport { isPlainObject } from '../is'\n\n/**\n * 检查对象是否具有指定的自有属性。\n *\n * @param {object} obj - 需要检查的对象\n * @param {string | symbol} prop - 需要检查的属性键\n * @returns {boolean} 如果对象具有该自有属性,则返回 true,否则返回 false\n * @group Object\n * @example\n * ```ts\n * const obj = { name: 'Tom', age: 25 }\n * hasOwnProp(obj, 'name') // true\n * hasOwnProp(obj, 'toString') // false,toString 是继承的属性\n * ```\n */\nexport function hasOwnProp(obj: object, prop: string | symbol): boolean {\n return Object.prototype.hasOwnProperty.call(obj, prop)\n}\n\n/**\n * 深拷贝对象,支持基本类型、数组、对象、日期和正则表达式\n * @param obj 要拷贝的对象\n * @returns 深拷贝后的对象,与原对象完全独立\n * @group Object\n * @example\n * ```ts\n * const original = { a: 1, b: { c: 2 }, d: [1, 2, 3], e: new Date() }\n * const copy = deepClone(original)\n * copy.b.c = 100\n * console.log(original.b.c) // 2,原对象不受影响\n *\n * // 支持日期对象\n * const date = new Date()\n * const clonedDate = deepClone(date)\n * console.log(date === clonedDate) // false,不是同一引用\n * ```\n */\nexport function deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== 'object') {\n return obj\n }\n\n if (obj instanceof Date) {\n return new Date(obj.getTime()) as unknown as T\n }\n\n if (obj instanceof RegExp) {\n return new RegExp(obj.source, obj.flags) as unknown as T\n }\n\n if (Array.isArray(obj)) {\n return obj.map(item => deepClone(item)) as unknown as T\n }\n\n const cloned: Record<string, any> = {}\n Object.keys(obj as Record<string, any>).forEach((key) => {\n cloned[key] = deepClone((obj as Record<string, any>)[key])\n })\n\n return cloned as T\n}\n\n/**\n * 从对象中获取指定路径的值,支持点分隔的嵌套路径\n * @param obj 源对象\n * @param path 属性路径,如 'user.address.street'\n * @param defaultValue 默认值,当路径不存在时返回\n * @returns 路径对应的值,如果路径不存在则返回默认值\n * @group Object\n * @example\n * ```ts\n * const obj = { user: { profile: { name: 'Tom', age: 25 }, roles: ['admin'] } }\n *\n * get(obj, 'user.profile.name') // 'Tom'\n * get(obj, 'user.roles.0') // 'admin'\n * get(obj, 'user.settings', { theme: 'dark' }) // { theme: 'dark' }(路径不存在,返回默认值)\n * get(obj, 'user.profile.gender') // undefined(路径不存在且没提供默认值)\n * get(obj, 'user.profile.gender', 'unknown') // 'unknown'(使用默认值)\n * ```\n */\nexport function get<T = any>(obj: Record<string, any>, path: string, defaultValue?: T): T | undefined {\n const keys = path.split('.')\n let result = obj\n\n for (const key of keys) {\n if (result === undefined || result === null) {\n return defaultValue\n }\n result = result[key]\n }\n\n return (result === undefined) ? defaultValue : result as T\n}\n\n/**\n * 从对象中选择指定的属性,创建一个新对象\n * @param obj 原始对象\n * @param keys 要选择的键数组\n * @returns 包含指定键的新对象\n * @group Object\n * @example\n * ```ts\n * @group Object\n * const user = { id: 1, name: 'Tom', age: 25, email: 'tom@example.com' }\n *\n * @group Object\n * pick(user, ['name', 'email']) // { name: 'Tom', email: 'tom@example.com' }\n * pick(user, ['name', 'gender']) // { name: 'Tom' }(不存在的键会被忽略)\n * pick(user, []) // {}(空数组返回空对象)\n * ```\n */\nexport function pick<T extends Record<string, any>, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {\n return keys.reduce((result, key) => {\n if (key in obj) {\n result[key] = obj[key]\n }\n return result\n }, {} as Pick<T, K>)\n}\n\n/**\n * 从对象中排除指定的属性,创建一个新对象\n * @param obj 原始对象\n * @param keys 要排除的键数组\n * @returns 不包含指定键的新对象\n * @group Object\n * @example\n * ```ts\n * const user = { id: 1, name: 'Tom', age: 25, password: '123456' }\n *\n * omit(user, ['password']) // { id: 1, name: 'Tom', age: 25 }\n * omit(user, ['id', 'age']) // { name: 'Tom', password: '123456' }\n * omit(user, ['nonExistent']) // 原对象的拷贝,不存在的键会被忽略\n * ```\n */\nexport function omit<T extends Record<string, any>, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {\n const result = { ...obj }\n keys.forEach((key) => {\n delete result[key]\n })\n return result\n}\n\n/**\n * 将对象转换为 URL 查询字符串\n * @param obj 要转换的对象\n * @returns 格式化后的查询字符串(不包含前导?)\n * @group Object\n * @example\n * ```ts\n * objectToQueryString({ name: 'Tom', age: 25 }) // 'name=Tom&age=25'\n * objectToQueryString({ search: 'hello world', page: 1 }) // 'search=hello%20world&page=1'\n * objectToQueryString({ tags: ['js', 'ts'] }) // 'tags=js&tags=ts'\n * objectToQueryString({ name: 'Tom', empty: null }) // 'name=Tom'(null 和 undefined 会被过滤)\n * ```\n */\nexport function objectToQueryString(obj: Record<string, any>): string {\n return Object.entries(obj)\n .filter(([_, value]) => value !== undefined && value !== null)\n .map(([key, value]) => {\n if (Array.isArray(value)) {\n return value.map(item => `${encodeURIComponent(key)}=${encodeURIComponent(String(item))}`).join('&')\n }\n return `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`\n })\n .join('&')\n}\n\n/**\n * 合并多个对象,后面的对象的属性会覆盖前面的\n * @param objects 要合并的对象数组\n * @returns 合并后的新对象\n * @group Object\n * @example\n * ```ts\n * merge({ a: 1 }, { b: 2 }) // { a: 1, b: 2 }\n * merge({ a: 1 }, { a: 2, b: 2 }) // { a: 2, b: 2 }(后面对象的属性会覆盖前面的)\n * merge({ a: { x: 1 } }, { a: { y: 2 } }) // { a: { y: 2 } }(不是深度合并)\n * merge({}, { a: 1 }, { b: 2 }, { c: 3 }) // { a: 1, b: 2, c: 3 }\n * ```\n */\nexport function merge<T extends Record<string, any>>(...objects: T[]): T {\n return Object.assign({}, ...objects)\n}\n\n/**\n * 深度合并多个对象,会递归合并嵌套对象\n * @param objects 要合并的对象数组\n * @returns 深度合并后的新对象\n * @group Object\n * @example\n * ```ts\n * deepMerge({ a: { x: 1 } }, { a: { y: 2 } }) // { a: { x: 1, y: 2 } }\n * deepMerge({ a: 1 }, { b: 2 }) // { a: 1, b: 2 }\n * deepMerge({ a: [1, 2] }, { a: [3, 4] }) // { a: [3, 4] }(数组会被替换,不会合并)\n * deepMerge({}, { a: null }, { a: { b: 2 } }) // { a: { b: 2 } }(null 会被后面的对象覆盖)\n * ```\n */\nexport function deepMerge(...objects: Record<string, any>[]): Record<string, any> {\n const result: Record<string, any> = {}\n\n objects.forEach((obj) => {\n if (!isPlainObject(obj))\n return\n\n Object.keys(obj).forEach((key) => {\n const sourceValue = obj[key]\n const targetValue = result[key]\n\n // 如果两个值都是对象,就递归合并\n if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {\n result[key] = deepMerge(targetValue, sourceValue)\n }\n else {\n // 否则直接覆盖\n result[key] = deepClone(sourceValue)\n }\n })\n })\n\n return result\n}\n\n/**\n * 根据传入的键数组和键名映射对象过滤并重命名对象,返回一个新对象\n * @param originalObject 要过滤的原始对象\n * @param keysArray 要保留的键名数组\n * @param keyMapping 可选的键名映射对象,格式为 { 原键名: 新键名 }\n * @returns 返回一个新对象,其中只包含原对象中匹配的键值对,并根据映射重命名键\n * @group Object\n * @example\n * ```ts\n * const originalObject = { name: \"John\", age: 30, gender: \"male\", country: \"USA\" }\n * const keysToFilter = [\"name\", \"country\"]\n * const keyMapping = { name: \"fullName\", country: \"location\" }\n * const result = filterObjectByKeys(originalObject, keysToFilter, keyMapping)\n * // 结果: { fullName: 'John', location: 'USA' }\n * ```\n */\nexport function filterObjectByKeys(\n originalObject: Record<string, any>,\n keysArray: string[],\n keyMapping: Record<string, string> = {},\n): Record<string, any> {\n return keysArray.reduce((newObject: Record<string, any>, key: string) => {\n if (hasOwnProp(originalObject, key)) {\n const newKey = keyMapping[key] || key // 如果有映射,就用新键名,否则用原键名\n newObject[newKey] = originalObject[key]\n }\n return newObject\n }, {})\n}\n","/**\n * @module 字符串处理\n * @description 提供各种字符串处理函数,包括字符串转换、格式化、验证等实用功能\n */\n\nimport { isNaN, isString } from '../is'\n\n/**\n * 将字符串首字母转为大写\n * @param str 输入字符串\n * @returns 首字母大写后的字符串\n * @group String\n * @example\n * ```ts\n * capitalize('hello') // 'Hello'\n * capitalize('') // ''\n * capitalize('a') // 'A'\n * ```\n */\nexport function capitalize(str: string): string {\n if (str.length === 0)\n return str\n return str.charAt(0).toUpperCase() + str.slice(1)\n}\n\n/**\n * 将驼峰命名转换为短横线命名(kebab-case)\n * @param str 驼峰命名的字符串\n * @returns 短横线命名的字符串\n * @group String\n * @example\n * ```ts\n * camelToKebab('helloWorld') // 'hello-world'\n * camelToKebab('HelloWorld') // 'hello-world'\n * camelToKebab('APIVersion') // 'api-version'\n * camelToKebab('iOS9App') // 'i-os9-app'\n * ```\n */\nexport function camelToKebab(str: string): string {\n // 处理首字母大写的情况,如 APIVersion -> api-version\n return str\n .replace(/([A-Z])([A-Z]+)([A-Z])/g, '$1$2-$3') // 处理连续大写字母\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .toLowerCase()\n}\n\n/**\n * 将短横线命名(kebab-case)转换为驼峰命名(camelCase)\n * @param str 短横线命名的字符串\n * @returns 驼峰命名的字符串\n * @group String\n * @example\n * ```ts\n * kebabToCamel('hello-world') // 'helloWorld'\n * kebabToCamel('api-version') // 'apiVersion'\n * kebabToCamel('i-os9-app') // 'iOs9App'\n * kebabToCamel('--hello') // 'hello'\n * ```\n */\nexport function kebabToCamel(str: string): string {\n return str.replace(/-([a-z])/g, (_, char) => char.toUpperCase())\n}\n\n/**\n * 截取字符串并添加省略号\n * @param str 原始字符串\n * @param length 截取的最大长度,默认为 50\n * @param ellipsis 省略号字符,默认为'...'\n * @returns 截取后的字符串,如果原字符串长度小于等于截取长度,则返回原字符串\n * @group String\n * @example\n * ```ts\n * truncate('这是一个很长的字符串', 5) // '这是...'\n * truncate('这是一个很长的字符串', 5, '…') // '这是一…'\n * truncate('短字符', 10) // '短字符'\n * ```\n */\nexport function truncate(str: string, length: number = 50, ellipsis: string = '...'): string {\n if (str.length <= length)\n return str\n\n // 计算需要保留的字符数 = 总长度 - 省略号长度\n const truncatedLength = length - ellipsis.length\n return str.substring(0, truncatedLength) + ellipsis\n}\n\n/**\n * 生成指定长度的随机字符串\n * @param length 字符串长度\n * @param chars 可选的字符集,默认包含大小写字母和数字\n * @returns 生成的随机字符串\n * @group String\n * @example\n * ```ts\n * randomString(5) // 例如: 'aB9cD'\n * randomString(10) // 例如: 'aBcD1eF2gH'\n * randomString(3, '123456') // 例如: '426'\n * ```\n */\nexport function randomString(length: number, chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'): string {\n let result = ''\n const charsLength = chars.length\n\n for (let i = 0; i < length; i++) {\n result += chars.charAt(Math.floor(Math.random() * charsLength))\n }\n\n return result\n}\n\n/**\n * 将字符串中的 HTML 特殊字符转义,防止 XSS 攻击\n * @param html 包含 HTML 的字符串\n * @returns 转义后的安全字符串\n * @group String\n * @example\n * ```ts\n * escapeHtml('<div>Hello & World</div>') // '&lt;div&gt;Hello &amp; World&lt;/div&gt;'\n * escapeHtml('<script>alert(\"XSS\")</script>') // '&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;'\n * ```\n */\nexport function escapeHtml(html: string): string {\n const entityMap: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n '\\'': '&#39;',\n '/': '&#x2F;',\n '`': '&#x60;',\n '=': '&#x3D;',\n }\n\n return html.replace(/[&<>\"'`=/]/g, s => entityMap[s] || s)\n}\n\n/**\n * 检查字符串是否为有效的 URL\n * @param url 要检查的 URL 字符串\n * @returns 如果是有效的 URL 则返回 true,否则返回 false\n * @group String\n * @example\n * ```ts\n * isValidUrl('https://example.com') // true\n * isValidUrl('http://localhost:3000') // true\n * isValidUrl('example.com') // false,缺少协议\n * isValidUrl('not a url') // false\n * ```\n */\nexport function isValidUrl(url: string): boolean {\n try {\n return Boolean(new URL(url))\n }\n catch {\n return false\n }\n}\n\n/**\n * 检查字符串是否为有效的电子邮件地址\n * @param email 要检查的电子邮件地址\n * @returns 如果是有效的电子邮件地址则返回 true,否则返回 false\n * @group String\n * @example\n * ```ts\n * @group String\n * isValidEmail('user@example.com') // true\n * @group String\n * isValidEmail('user.name+tag@example.co.uk') // true\n * isValidEmail('invalid@email') // false,缺少顶级域名\n * isValidEmail('not an email') // false\n * ```\n */\nexport function isValidEmail(email: string): boolean {\n const re = /^[\\w.%+-]+@[a-z0-9.-]+\\.[a-z]{2,}$/i\n return re.test(email)\n}\n\n/**\n * 检查字符串是否为空或只包含空白字符\n * @param str 要检查的字符串\n * @returns 如果字符串为空或只包含空白字符,则返回 true\n * @group String\n * @example\n * ```ts\n * isEmptyString('') // true\n * isEmptyString(' \\t\\n ') // true\n * isEmptyString('hello') // false\n * isEmptyString(' hello ') // false\n * ```\n */\nexport function isEmptyString(str: string): boolean {\n return str.trim().length === 0\n}\n\n/**\n * 确保值具有 rpx 单位,主要用于小程序/uni-app 样式处理\n * @param val 需要转化的值,可以是数字或字符串\n * @returns 转化后带有 rpx 单位的字符串,如果输入不是数字则原样返回\n * @group String\n * @example\n * ```ts\n * ensureRpxUnit(100) // '100rpx'\n * ensureRpxUnit('100') // '100rpx'\n * ensureRpxUnit('100px') // '100px',不是数字,保持原样\n * ensureRpxUnit('auto') // 'auto',不是数字,保持原样\n * ```\n */\nexport function ensureRpxUnit(val: string | number): string {\n const str = Number(val)\n if (isNaN(str)) {\n return val as string\n }\n return `${val}rpx`\n}\n\n/**\n * 替换字符串中的 `&nbsp;` 为不换行空格\n *\n * @param {string} str - 字符串\n * @returns {string} 处理后的字符串\n *\n * @group String\n * @example\n * ```ts\n * // 基本用法\n * replaceNBSP('John&nbsp;Doe') // 'John Doe'\n * replaceNBSP('Jane&nbsp;&nbsp;Smith') // 'Jane Smith'\n * replaceNBSP('Smith') // 'Smith'\n * ```\n */\nexport function replaceNBSP(str?: string): string {\n if (!isString(str)) {\n return str as any // 应该忽略非字符串字段\n }\n\n return str.replace(/&nbsp;/g, ' ').replace(/\\u00A0/g, ' ')\n}\n\n/**\n * 解析JSON字符串,处理异常情况\n *\n * 支持解析各种有效的JSON格式,包括对象、数组、字符串、数字、布尔值等。\n * 如果解析失败,会在控制台输出错误信息并返回原始字符串。\n *\n * @param str 待解析的字符串\n * @param defaultValue 解析失败时的默认返回值,默认为原始字符串\n * @returns 解析后的对象、数组或其他JSON值,解析失败时返回默认值\n * @group String\n * @example\n * ```ts\n * // 解析对象\n * parseJsonStr('{\"name\": \"Tom\", \"age\": 25}') // { name: 'Tom', age: 25 }\n *\n * // 解析数组\n * parseJsonStr('[1, 2, 3]') // [1, 2, 3]\n *\n * // 解析基础类型\n * parseJsonStr('\"hello\"') // 'hello'\n * parseJsonStr('123') // 123\n * parseJsonStr('true') // true\n *\n * // 处理无效JSON\n * parseJsonStr('{invalid json}') // '{invalid json}'(返回原字符串)\n *\n * // 处理空值\n * parseJsonStr('') // {}\n * parseJsonStr(null) // {}\n * parseJsonStr(undefined) // {}\n *\n * // 自定义默认值\n * parseJsonStr('invalid', null) // null\n *\n * // 非JSON格式的字符串\n * parseJsonStr('hello world') // 'hello world'\n * ```\n */\nexport function parseJsonStr<T = any>(str?: string | null, defaultValue?: T): T | string | Record<string, any> {\n // 处理空值情况\n if (!str) {\n return defaultValue !== undefined ? defaultValue : {}\n }\n\n // 确保输入是字符串\n if (!isString(str)) {\n return defaultValue !== undefined ? defaultValue : str as any\n }\n\n const trimmedStr = str.trim()\n\n // 检查是否可能是JSON格式\n const isLikelyJson = (\n // 对象格式\n (trimmedStr.startsWith('{') && trimmedStr.endsWith('}'))\n // 数组格式\n || (trimmedStr.startsWith('[') && trimmedStr.endsWith(']'))\n // 字符串格式\n || (trimmedStr.startsWith('\"') && trimmedStr.endsWith('\"'))\n // 数字格式(包括负数、小数、科学计数法)\n || /^-?\\d+(?:\\.\\d+)?(?:e[+-]?\\d+)?$/i.test(trimmedStr)\n // 布尔值和null\n || ['true', 'false', 'null'].includes(trimmedStr)\n )\n\n // 如果不像JSON格式,直接返回原字符串\n if (!isLikelyJson) {\n return defaultValue !== undefined ? defaultValue : str\n }\n\n try {\n return JSON.parse(trimmedStr) as T\n }\n catch (error) {\n console.error('解析 JSON 失败: 输入字符串为', str, '错误详情: ', error)\n return defaultValue !== undefined ? defaultValue : str\n }\n}\n","/**\n * @module 数组操作\n * @description 提供各种数组处理函数,包括数组转换、查询、过滤等实用功能。这些函数都不会修改原始数组,而是返回新的数组或值。\n */\n\nimport { hasOwnProp } from '../object'\nimport { replaceNBSP } from '../string'\n\n/**\n * 从数组中移除指定的项\n * @param array 原始数组\n * @param item 要移除的项\n * @returns 移除指定项后的新数组,不会修改原始数组\n * @group Array\n * @example\n * ```ts\n * const numbers = [1, 2, 3, 4, 5]\n * const newArray = remove(numbers, 3) // [1, 2, 4, 5]\n * console.log(numbers) // 原数组不变: [1, 2, 3, 4, 5]\n *\n * // 移除字符串\n * const fruits = ['苹果', '香蕉', '橙子']\n * const newFruits = remove(fruits, '香蕉') // ['苹果', '橙子']\n * ```\n */\nexport function remove<T>(array: T[], item: T): T[] {\n return array.filter(i => i !== item)\n}\n\n/**\n * 数组去重,移除数组中的重复元素\n * @param array 原始数组\n * @returns 去重后的新数组,保持原始顺序\n * @group Array\n * @example\n * ```ts\n * const numbers = [1, 2, 2, 3, 3, 4, 5, 5]\n * const uniqueArray = unique(numbers) // [1, 2, 3, 4, 5]\n *\n * // 对象数组会基于引用去重,内容相同但引用不同的对象会被视为不同元素\n * const objArray = [{id: 1}, {id: 2}, {id: 1}]\n * const uniqueObjArray = unique(objArray) // [{id: 1}, {id: 2}, {id: 1}]\n * ```\n */\nexport function unique<T>(array: T[]): T[] {\n return Array.from(new Set(array))\n}\n\n/**\n * 将数组分成指定大小的块\n * @param array 原始数组\n * @param size 每个块的大小,必须为正整数\n * @returns 二维数组,每个子数组最多包含 size 个元素\n * @group Array\n * @example\n * ```ts\n * const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n * const chunks = chunk(numbers, 3)\n * // 结果: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]\n *\n * // 如果 size 小于等于 0,则返回包含原始数组的数组\n * chunk(numbers, 0) // [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]\n * ```\n */\nexport function chunk<T>(array: T[], size: number): T[][] {\n if (size <= 0)\n return [array]\n\n const result: T[][] = []\n for (let i = 0; i < array.length; i += size) {\n result.push(array.slice(i, i + size))\n }\n\n return result\n}\n\n/**\n * 获取数组中的最后一个元素\n * @param array 数组\n * @returns 最后一个元素,如果数组为空则返回 undefined\n * @group Array\n * @example\n * ```ts\n * const numbers = [1, 2, 3, 4, 5]\n * const lastNumber = last(numbers) // 5\n *\n * const empty = []\n * const lastEmpty = last(empty) // undefined\n * ```\n */\nexport function last<T>(array: T[]): T | undefined {\n return array.length > 0 ? array[array.length - 1] : undefined\n}\n\n/**\n * 获取数组中的第一个元素\n * @param array 数组\n * @returns 第一个元素,如果数组为空则返回 undefined\n * @group Array\n * @example\n * ```ts\n * const numbers = [1, 2, 3, 4, 5]\n * const firstNumber = first(numbers) // 1\n *\n * const empty = []\n * const firstEmpty = first(empty) // undefined\n * ```\n */\nexport function first<T>(array: T[]): T | undefined {\n return array.length > 0 ? array[0] : undefined\n}\n\n/**\n * 随机打乱数组元素顺序\n * @param array 原始数组\n * @returns 打乱后的新数组,不会修改原始数组\n * @group Array\n * @example\n * ```ts\n * const numbers = [1, 2, 3, 4, 5]\n * const shuffled = shuffle(numbers)\n * // 可能的结果: [3, 1, 5, 2, 4]\n * console.log(numbers) // 原数组不变: [1, 2, 3, 4, 5]\n * ```\n */\nexport function shuffle<T>(array: T[]): T[] {\n const result = [...array]\n\n for (let i = result.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [result[i], result[j]] = [result[j] as T, result[i] as T]\n }\n\n return result\n}\n\n/**\n * 从数组中随机选择一个元素\n * @param array 数组\n * @returns 随机选择的元素,如果数组为空则返回 undefined\n * @group Array\n * @example\n * ```ts\n * const fruits = ['苹果', '香蕉', '橙子', '梨', '葡萄']\n * const randomFruit = sample(fruits)\n * // 可能返回: '香蕉'\n *\n * const empty = []\n * const emptyResult = sample(empty) // undefined\n * ```\n */\nexport function sample<T>(array: T[]): T | undefined {\n if (array.length === 0)\n return undefined\n const index = Math.floor(Math.random() * array.length)\n return array[index]\n}\n\n/**\n * 比较两个数组是否相等(元素顺序和值都相同)\n * @param a 第一个数组\n * @param b 第二个数组\n * @returns 如果两个数组长度相同且对应位置的元素全部相等则返回 true,否则返回 false\n * @group Array\n * @example\n * ```ts\n * isEqual([1, 2, 3], [1, 2, 3]) // true\n * isEqual([1, 2, 3], [1, 3, 2]) // false,顺序不同\n * isEqual([1, 2, 3], [1, 2]) // false,长度不同\n *\n * // 对象比较是基于引用的\n * const obj = { id: 1 }\n * isEqual([obj], [obj]) // true,同一个对象引用\n * isEqual([{ id: 1 }], [{ id: 1 }]) // false,不同对象引用\n * ```\n */\nexport function isEqual<T>(a: T[], b: T[]): boolean {\n if (a.length !== b.length)\n return false\n return a.every((item, index) => item === b[index])\n}\n\n/**\n * 根据条件对数组中的元素进行分组\n * @param array 原始数组\n * @param keyFn 用于生成分组键的函数,接收数组元素并返回用作分组键的值\n * @returns 分组后的对象,键为分组键,值为对应的元素数组\n * @group Array\n * @example\n * ```ts\n * const people = [\n * { name: '张三', age: 25 },\n * { name: '李四', age: 30 },\n * { name: '王五', age: 25 },\n * { name: '赵六', age: 30 }\n * ]\n *\n * // 按年龄分组\n * const groupedByAge = groupBy(people, person => person.age)\n * // 结果:\n * // {\n * // 25: [{ name: '张三', age: 25 }, { name: '王五', age: 25 }],\n * // 30: [{ name: '李四', age: 30 }, { name: '赵六', age: 30 }]\n * // }\n *\n * // 使用字符串键\n * const groupedByAgeRange = groupBy(people, person =>\n * person.age < 30 ? '青年' : '成年')\n * // 结果:\n * // {\n * // '青年': [{ name: '张三', age: 25 }, { name: '王五', age: 25 }],\n * // '成年': [{ name: '李四', age: 30 }, { name: '赵六', age: 30 }]\n * // }\n * ```\n */\nexport function groupBy<T, K extends string | number | symbol>(array: T[], keyFn: (item: T) => K): Record<K, T[]> {\n return array.reduce((result, item) => {\n const key = keyFn(item);\n (result[key] = result[key] || []).push(item)\n return result\n }, {} as Record<K, T[]>)\n}\n\n/**\n * 在选项数组前添加一个\"全部\"选项\n *\n * @param options - 原始选项数组\n * @param config - 配置选项\n * @param config.name - 选项标签的字段名 (默认为 'label')\n * @param config.value - \"全部\"选项的值 (默认为 '')\n * @param config.valueKey - 选项值的字段名 (默认为 'value')\n * @returns 添加了\"全部\"选项的新数组\n *\n * @group Array\n * @example\n * ```ts\n * // 基本用法\n * const options = [\n * { label: '选项1', value: 1 },\n * { label: '选项2', value: 2 }\n * ]\n * const result = appendUniversalOption(options)\n * // 结果: [\n * // { label: '全部', value: '' },\n * // { label: '选项1', value: 1 },\n * // { label: '选项2', value: 2 }\n * // ]\n *\n * // 自定义字段名和值\n * const customOptions = [\n * { text: '选项1', id: 1 },\n * { text: '选项2', id: 2 }\n * ]\n * const customResult = appendUniversalOption(customOptions, {\n * name: 'text',\n * valueKey: 'id',\n * value: 0\n * })\n * // 结果: [\n * // { text: '全部', id: 0 },\n * // { text: '选项1', id: 1 },\n * // { text: '选项2', id: 2 }\n * // ]\n * ```\n */\nexport function appendUniversalOption<T extends Record<string, any>, V = any>(\n options: T[],\n {\n name = 'label',\n value = '' as V,\n valueKey = 'value',\n }: {\n name?: string\n value?: V\n valueKey?: string\n } = {},\n): T[] {\n return [\n {\n [name]: '全部',\n [valueKey]: value,\n } as unknown as T,\n ...options,\n ]\n}\n\n/**\n * 递归重命名树形结构节点的属性。\n *\n * @param tree - 需要处理的树形数据(根节点数组)\n * @param renameMap - 属性重命名映射对象,格式为 `{ oldKey: newKey }`\n * @param childKey - 子节点的键名(默认为 `'children'`)\n * @param deleteOldKeys - 是否删除旧属性(默认 `true`)\n * @returns 处理后的树形结构数组\n *\n * @group Array\n * @example\n * const tree = [\n * { id: 1, name: 'Node 1', children: [...] }\n * ];\n * const renamedTree = renameTreeNodes(tree, { name: 'label' });\n * // 输出:节点中的 `name` 被替换为 `label`,且旧属性被删除\n */\nexport function renameTreeNodes(\n tree: any[],\n renameMap: Record<string, string>,\n childKey: string = 'children',\n deleteOldKeys: boolean = true,\n): any[] {\n return tree.map((node) => {\n const newNode = { ...node }\n\n // 重命名属性\n Object.entries(renameMap).forEach(([oldKey, newKey]) => {\n if (hasOwnProp(newNode, oldKey)) {\n newNode[newKey] = newNode[oldKey]\n if (deleteOldKeys) {\n delete newNode[oldKey]\n }\n }\n })\n\n // 递归处理子节点\n if (Array.isArray(newNode[childKey])) {\n newNode[childKey] = renameTreeNodes(\n newNode[childKey],\n renameMap,\n childKey,\n deleteOldKeys,\n )\n }\n\n return newNode\n })\n}\n\n/**\n * 递归转换树形结构的节点,支持重命名、新增、过滤属性。\n *\n * @param tree - 需要处理的树形数据(根节点数组)\n * @param transformer - 转换函数,接收当前节点和子节点,返回处理后的节点\n * @param childKey - 子节点的键名(默认为 `'children'`)\n * @returns 转换后的树形结构数组\n *\n * @group Array\n * @example\n * // 重命名 `name` 为 `label`,并添加 `isLeaf` 属性\n * const transformedTree = transformTree(tree, (node, children) => ({\n * id: node.id,\n * label: node.name,\n * isLeaf: children.length === 0,\n * children,\n * }));\n */\nexport function transformTree(\n tree: any[],\n transformer: (node: any, children: any[]) => any,\n childKey: string = 'children',\n): any[] {\n return tree.map((node) => {\n const children = node[childKey] ? transformTree(node[childKey], transformer, childKey) : []\n return transformer(node, children)\n })\n}\n\ntype KeysMatching<T, V> = {\n [K in keyof T]-?: T[K] extends V ? K : never\n}[keyof T]\n/**\n * 数组转为对象\n * @param arr - 原始数组\n * @param key - 数组对象键\n * @returns 对应键的对象\n * @group Array\n * @example\n * ```ts\n * const arr = [{ key: 'tom', name: '汤姆' }, { key: 'jack', name: '杰克' }]\n * arrayToObject(arr) // { tom: { key: 'tom', name: '汤姆' }, jack: { key: 'jack', name: '杰克' } }\n *\n * // key 值为空\n * const arr = [{ key: 'tom', name: '汤姆' }, { key: '', name: '杰克' }]\n * arrayToObject(arr) // { tom: { key: 'tom', name: '汤姆' } }\n * ```\n */\nexport function arrayToObject<T>(\n arr: T[],\n key: KeysMatching<T, string | number>,\n): Record<string, T> {\n const result: Record<string, T> = {}\n for (const item of arr) {\n const K = item[key] // K 在这里被 TS 推断为 string | number\n if (!K)\n continue\n result[K as string] = item\n }\n return result\n}\n\n/**\n * 替换数组数据中指定字段的 `&nbsp;` 为不换行空格\n *\n * @param {Array<Record<string, any>>} arr - 数组\n * @param {string} key - 需要处理的字段名\n * @returns {Array<Record<string, any>>} 处理后的数据数组\n *\n * @group Array\n * @example\n * ```ts\n * // 基本用法\n * const data = [\n * { name: 'John&nbsp;Doe', age: 30 },\n * { name: 'Jane&nbsp;&nbsp;Smith', age: 25 }\n * ]\n * const result = arrayReplaceNBSP(data, 'name')\n * // 结果: [\n * // { name: 'John Doe', age: 30 },\n * // { name: 'Jane Smith', age: 25 }\n * // ]\n *\n * // 空数组情况\n * const emptyData = []\n * const result = arrayReplaceNBSP(emptyData, 'name')\n * // 结果: []\n * ```\n */\nexport function arrayReplaceNBSP<T extends Record<string, string | number | boolean | object>>(\n arr: T[],\n key: string,\n): T[] {\n if (arr.length) {\n return arr.map((item) => {\n if (typeof item[key] === 'string') {\n // 创建一个新对象,不修改原对象\n return {\n ...item,\n [key]: replaceNBSP(item[key] as string),\n }\n }\n return item\n })\n }\n\n return []\n}\n","import type { Dayjs, ManipulateType, OpUnitType, QUnitType } from 'dayjs'\n/**\n * @module 日期时间\n * @description 提供各种日期时间处理函数,包括日期格式化、日期计算、比较等实用功能。基于 dayjs 库实现。\n */\nimport dayjs from 'dayjs'\nimport isBetween from 'dayjs/plugin/isBetween'\nimport isSameOrAfter from 'dayjs/plugin/isSameOrAfter'\nimport isSameOrBefore from 'dayjs/plugin/isSameOrBefore'\nimport relativeTime from 'dayjs/plugin/relativeTime'\nimport { isNaN, isNumber } from '../is'\n\n// 导入本地化语言\nimport 'dayjs/locale/zh-cn'\n\n// 设置全局语言为中文\ndayjs.locale('zh-cn')\n\n/**\n * 表示日期的各种类型,可以是日期对象、日期字符串或时间戳\n */\nexport type DateLike = Date | string | number\n\n// 注册插件\ndayjs.extend(relativeTime)\ndayjs.extend(isSameOrBefore)\ndayjs.extend(isSameOrAfter)\ndayjs.extend(isBetween)\n\n// 可以按需导入更多插件\n// import weekOfYear from 'dayjs/plugin/weekOfYear'\n// import isLeapYear from 'dayjs/plugin/isLeapYear'\n// import customParseFormat from 'dayjs/plugin/customParseFormat'\n// dayjs.extend(weekOfYear)\n// dayjs.extend(isLeapYear)\n// dayjs.extend(customParseFormat)\n\n/**\n * 创建 dayjs 对象\n * @param date 日期参数,可以是日期对象、时间戳或日期字符串\n * @returns dayjs 对象\n * @group Date\n * @example\n * ```ts\n * createDate() // 当前日期时间\n * createDate('2023-05-15') // 指定日期\n * createDate(1684123456000) // 时间戳\n * ```\n */\nexport function createDate(date?: DateLike | Dayjs): Dayjs {\n return dayjs(date)\n}\n\n/**\n * 判断时间戳是否为毫秒级时间戳\n * @param value 要检查的值\n * @returns 如果是毫秒级时间戳则返回 true,否则返回 false\n * @group Date\n * @example\n * ```ts\n * isMillisecondTimestamp(1673740800000) // true,13位数字\n * isMillisecondTimestamp(1673740800) // false,10位数字,秒级时间戳\n * isMillisecondTimestamp('1673740800000') // false, 字符串不是时间戳\n * isMillisecondTimestamp(new Date()) // false,日期对象不是时间戳\n * ```\n */\nexport function isMillisecondTimestamp(value: unknown): boolean {\n if (!isNumber(value) || value.toString().length !== 13)\n return false\n\n const date = new Date(value)\n return !isNaN(date.getTime())\n}\n\n/**\n * 转换为 dayjs 可接收的参数格式\n * @param value 日期参数,可以是日期对象、时间戳(秒或毫秒)或日期字符串\n * @returns 转换后的参数,如果是秒级时间戳则转为毫秒级,其他类型保持不变\n * @group Date\n * @example\n * ```ts\n * convertToDayjsParam(1673740800) // 1673740800000,秒转为毫秒\n * convertToDayjsParam(1673740800000) // 1673740800000,毫秒保持不变\n * convertToDayjsParam(new Date()) // Date对象,保持不变\n * convertToDayjsParam('2023-01-15') // '2023-01-15',字符串保持不变\n * ```\n */\nexport function convertToDayjsParam(value: DateLike) {\n // 如果已经是毫秒级时间戳,直接返回\n if (isMillisecondTimestamp(value)) {\n return value\n }\n\n // 如果是数字且是10位数字,则可能是秒级时间戳,需要转为毫秒\n if (isNumber(value) && String(value).length === 10) {\n return value * 1000\n }\n\n return value\n}\n\n/**\n * 格式化日期为指定格式的字符串\n * @param date 日期对象、时间戳或日期字符串\n * @param format 格式字符串,支持的占位符请参考 [dayjs 文档](https://day.js.org/docs/zh-CN/durations/format#%E6%94%AF%E6%8C%81%E7%9A%84%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%8D%A0%E4%BD%8D%E7%AC%A6%E5%88%97%E8%A1%A8)\n * @returns 格式化后的日期字符串\n * @group Date\n * @example\n * ```ts\n * formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss') // \"2023-05-16 14:30:45\"\n * formatDate('2023-05-15', 'YYYY年MM月DD日') // \"2023年05月15日\"\n * formatDate(1684123456000, 'MM/DD/YYYY') // \"05/15/2023\"\n * ```\n */\nexport function formatDate(date: DateLike, format = 'YYYY-MM-DD'): string {\n return dayjs(convertToDayjsParam(date)).format(format)\n}\n\n/**\n * 将日期格式化为完整的时间字符串(YYYY-MM-DD HH:mm:ss)\n * @param date 日期对象、时间戳或日期字符串\n * @returns 格式为 YYYY-MM-DD HH:mm:ss 的日期时间字符串\n * @group Date\n * @example\n * ```ts\n * formatFullTime(new Date()) // \"2023-05-16 14:30:45\"\n * formatFullTime('2023-05-15') // \"2023-05-15 00:00:00\"\n * formatFullTime(1684123456) // \"2023-05-15 10:30:56\"(秒级时间戳会被自动转换)\n * ```\n */\nexport function formatFullTime(date: DateLike) {\n return formatDate(date, 'YYYY-MM-DD HH:mm:ss')\n}\n\n/**\n * 获取当前日期时间的时间戳\n * @returns 当前时间戳(毫秒)\n * @group Date\n * @example\n * ```ts\n * now() // 例如: 1684123456789\n * ```\n */\nexport function now(): number {\n return Date.now()\n}\n\n/**\n * 解析日期字符串为日期对象\n * @param dateStr 日期字符串\n * @returns 日期对象\n * @throws 如果日期格式无效,抛出 TypeError\n * @group Date\n * @example\n * ```ts\n * parseDate('2023-05-15') // Date 对象: Mon May 15 2023 00:00:00\n * parseDate('2023/05/15 14:30:45') // Date 对象: Mon May 15 2023 14:30:45\n * parseDate('invalid date') // 抛出 TypeError: 无效的日期格式\n * ```\n */\nexport function parseDate(dateStr: string): Date {\n const d = dayjs(dateStr)\n if (!d.isValid())\n throw new TypeError('无效的日期格式')\n return d.toDate()\n}\n\n/**\n * 计算两个日期之间的差异\n * @param date1 第一个日期\n * @param date2 第二个日期\n * @param unit 计量单位,默认为 'day',可以是 'year', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond'\n * @returns 两个日期之间的差值,正数表示 date1 晚于 date2,负数表示 date1 早于 date2\n * @group Date\n * @example\n * ```ts\n * diff('2023-05-15', '2023-05-10') // 5(相差5天)\n * diff('2023-05-15', '2023-06-15', 'month') // -1(相差1个月,且第一个日期早于第二个)\n * diff('2023-05-15 08:00', '2023-05-15 06:00', 'hour') // 2(相差2小时)\n * ```\n */\nexport function diff(date1: DateLike, date2: DateLike, unit: QUnitType | OpUnitType = 'day'): number {\n return dayjs(convertToDayjsParam(date1)).diff(dayjs(convertToDayjsParam(date2)), unit)\n}\n\n/**\n * 向日期添加指定时间\n * @param date 原始日期\n * @param amount 要添加的数量,可以为负数\n * @param unit 时间单位,默认为 'day',可以是 'year', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond'\n * @returns 添加后的新日期对象\n * @group Date\n * @example\n * ```ts\n * add(new Date('2023-05-15'), 2, 'day') // Date 对象: Wed May 17 2023\n * add('2023-05-15', -1, 'month') // Date 对象: Sat Apr 15 2023\n * add('2023-05-15 12:00', 30, 'minute') // Date 对象: Mon May 15 2023 12:30:00\n * ```\n */\nexport function add(date: DateLike, amount: number, unit: ManipulateType = 'day'): Date {\n return dayjs(convertToDayjsParam(date)).add(amount, unit).toDate()\n}\n\n/**\n * 向日期添加指定天数\n * @param date 原始日期\n * @param days 要添加的天数(可以为负数)\n * @returns 添加天数后的新日期对象\n * @group Date\n * @example\n * ```ts\n * addDays(new Date('2023-05-15'), 5) // Date 对象: Sat May 20 2023\n * addDays('2023-05-15', -3) // Date 对象: Fri May 12 2023\n * ```\n */\nexport function addDays(date: DateLike, days: number): Date {\n return add(convertToDayjsParam(date), days, 'day')\n}\n\n/**\n * 向日期添加指定月数\n * @param date 原始日期\n * @param months 要添加的月数(可以为负数)\n * @returns 添加月数后的新日期对象\n * @group Date\n * @example\n * ```ts\n * addMonths(new Date('2023-05-15'), 2) // Date 对象: Sat Jul 15 2023\n * addMonths('2023-05-31', 1) // Date 对象: Fri Jun 30 2023(注意月份天数自动调整)\n * addMonths('2023-01-15', -3) // Date 对象: Wed Oct 15 2022\n * ```\n */\nexport function addMonths(date: DateLike, months: number): Date {\n return add(convertToDayjsParam(date), months, 'month')\n}\n\n/**\n * 向日期添加指定年数\n * @param date 原始日期\n * @param years 要添加的年数(可以为负数)\n * @returns 添加年数后的新日期对象\n * @group Date\n * @example\n * ```ts\n * addYears(new Date('2023-05-15'), 1) // Date 对象: Wed May 15 2024\n * addYears('2023-05-15', -3) // Date 对象: Fri May 15 2020\n * addYears('2024-02-29', 1) // Date 对象: Fri Feb 28 2025(注意闰年自动调整)\n * ```\n */\nexport function addYears(date: DateLike, years: number): Date {\n return add(convertToDayjsParam(date), years, 'year')\n}\n\n/**\n * 获取指定日期是一周中的第几天\n * @param date 日期\n * @param startOnMonday 是否从周一开始计算(默认为 false,即从周日开始)\n * @returns 一周中的第几天(0-6),周日为 0,周六为 6;如果 startOnMonday 为 true,则周一为 0,周日为 6\n * @group Date\n * @example\n * ```ts\n * getDayOfWeek(new Date('2023-05-15')) // 1(周一,从周日开始算是第1天)\n * getDayOfWeek('2023-05-15', true) // 0(周一,从周一开始算是第0天)\n * getDayOfWeek('2023-05-14') // 0(周日,从周日开始算是第0天)\n * getDayOfWeek('2023-05-14', true) // 6(周日,从周一开始算是第6天)\n * ```\n */\nexport function getDayOfWeek(date: DateLike, startOnMonday = false): number {\n const d = dayjs(convertToDayjsParam(date))\n const day = d.day()\n if (startOnMonday) {\n return day === 0 ? 6 : day - 1\n }\n return day\n}\n\n/**\n * 检查日期是否在指定范围内\n * @param date 要检查的日期\n * @param startDate 范围起始日期\n * @param endDate 范围结束日期\n * @returns 如果日期在指定范围内(不包括边界)则返回 true,否则返回 false\n * @group Date\n * @example\n * ```ts\n * isDateInRange('2023-05-15', '2023-05-10', '2023-05-20') // true\n * isDateInRange('2023-05-15', '2023-05-15', '2023-05-20') // false(不包括开始日期)\n * isDateInRange('2023-05-15', '2023-05-10', '2023-05-15') // false(不包括结束日期)\n * ```\n */\nexport function isDateInRange(date: DateLike, startDate: DateLike, endDate: DateLike): boolean {\n const d = dayjs(convertToDayjsParam(date))\n return d.isAfter(dayjs(convertToDayjsParam(startDate))) && d.isBefore(dayjs(convertToDayjsParam(endDate)))\n}\n\n/**\n * 获取指定月份的天数\n * @param year 年份\n * @param month 月份(0-11),0 表示一月,11 表示十二月\n * @returns 该月的天数\n * @group Date\n * @example\n * ```ts\n * getDaysInMonth(2023, 1) // 28(2023年2月有28天)\n * getDaysInMonth(2024, 1) // 29(2024年2月有29天,闰年)\n * getDaysInMonth(2023, 0) // 31(2023年1月有31天)\n * ```\n */\nexport function getDaysInMonth(year: number, month: number): number {\n return dayjs(new Date(year, month, 1)).daysInMonth()\n}\n\n/**\n * 获取相对时间描述\n * @param date 日期\n * @param baseDate 基准日期,默认为当前时间\n * @returns 相对时间描述,如\"几分钟前\"、\"几天后\"等\n * @group Date\n * @example\n * ```ts\n * fromNow(new Date(Date.now() - 5 * 60 * 1000)) // \"5分钟前\"\n * fromNow(new Date(Date.now() + 24 * 60 * 60 * 1000)) // \"1天内\"\n * fromNow('2023-01-01', '2023-01-05') // \"4天前\"\n * ```\n */\nexport function fromNow(date: DateLike, baseDate?: DateLike): string {\n date = convertToDayjsParam(date)\n baseDate = baseDate && convertToDayjsParam(baseDate)\n if (baseDate)\n return dayjs(date).from(dayjs(baseDate))\n return dayjs(date).fromNow()\n}\n\n/**\n * 获取日期的开始时间\n * @param date 日期\n * @param unit 单位,可以是 'year', 'month', 'week', 'day', 'hour', 'minute', 'second'\n * @returns 单位开始时间的日期对象\n * @group Date\n * @example\n * ```ts\n * startOf(new Date('2023-05-15 15:30:45'), 'day') // Date 对象: Mon May 15 2023 00:00:00\n * startOf('2023-05-15 15:30:45', 'month') // Date 对象: Mon May 01 2023 00:00:00\n * startOf('2023-05-15 15:30:45', 'hour') // Date 对象: Mon May 15 2023 15:00:00\n * ```\n */\nexport function startOf(date: DateLike, unit: OpUnitType): Date {\n return dayjs(convertToDayjsParam(date)).startOf(unit).toDate()\n}\n\n/**\n * 获取日期的结束时间\n * @param date 日期\n * @param unit 单位,可以是 'year', 'month', 'week', 'day', 'hour', 'minute', 'second'\n * @returns 单位结束时间的日期对象\n * @group Date\n * @example\n * ```ts\n * endOf(new Date('2023-05-15 15:30:45'), 'day') // Date 对象: Mon May 15 2023 23:59:59.999\n * endOf('2023-05-15 15:30:45', 'month') // Date 对象: Wed May 31 2023 23:59:59.999\n * endOf('2023-05-15 15:30:45', 'hour') // Date 对象: Mon May 15 2023 15:59:59.999\n * ```\n */\nexport function endOf(date: DateLike, unit: OpUnitType): Date {\n return dayjs(convertToDayjsParam(date)).endOf(unit).toDate()\n}\n\n/**\n * 格式化日期为人类友好的格式\n * @param date 日期\n * @returns 人类友好的日期描述,如\"今天 HH:mm\"、\"昨天 HH:mm\"、\"明天 HH:mm\"或\"YYYY-MM-DD HH:mm\"\n * @group Date\n * @example\n * ```ts\n * formatHumanReadable(new Date()) // \"今天 15:30\"\n * formatHumanReadable(new Date(Date.now() - 24 * 60 * 60 * 1000)) // \"昨天 15:30\"\n * formatHumanReadable(new Date(Date.now() + 24 * 60 * 60 * 1000)) // \"明天 15:30\"\n * formatHumanReadable('2023-01-01') // \"2023-01-01 00:00\"\n * ```\n */\nexport function formatHumanReadable(date: DateLike): string {\n const d = dayjs(convertToDayjsParam(date))\n const now = dayjs()\n\n if (d.isSame(now, 'day'))\n return `今天 ${d.format('HH:mm')}`\n if (d.isSame(now.subtract(1, 'day'), 'day'))\n return `昨天 ${d.format('HH:mm')}`\n if (d.isSame(now.add(1, 'day'), 'day'))\n return `明天 ${d.format('HH:mm')}`\n if (d.isSame(now, 'year'))\n return d.format('M月D日 HH:mm')\n return d.format('YYYY年M月D日 HH:mm')\n}\n\n/**\n * 格式化日期为聊天列表时间格式(类似微信聊天列表)\n * @param date 日期\n * @returns 格式化后的时间字符串\n * - 当天:HH:mm\n * - 昨天:昨天 HH:mm\n * - 7天内(不包括今天和昨天):星期几\n * - 今年其他日期:MM月DD日\n * - 往年日期:YYYY年MM月DD日\n * @group Date\n * @example\n * ```ts\n * formatChatTime(new Date()) // \"14:30\"\n * formatChatTime(new Date(Date.now() - 24 * 60 * 60 * 1000)) // \"昨天 09:15\"\n * formatChatTime(new Date(Date.now() - 3 * 24 * 60 * 60 * 1000)) // \"星期三\"\n * formatChatTime('2023-05-15') // \"05月15日\"\n * formatChatTime('2022-12-25') // \"2022年12月25日\"\n * ```\n */\nexport function formatChatTime(date: DateLike): string {\n const d = dayjs(convertToDayjsParam(date))\n const now = dayjs()\n\n // 当天:显示时间\n if (d.isSame(now, 'day')) {\n return d.format('HH:mm')\n }\n\n // 昨天:显示\"昨天 HH:mm\"\n if (d.isSame(now.subtract(1, 'day'), 'day')) {\n return `昨天 ${d.format('HH:mm')}`\n }\n\n // 7天内(不包括今天和昨天):显示星期几\n const daysDiff = now.diff(d, 'day')\n if (daysDiff >= 2 && daysDiff < 7) {\n const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']\n const dayIndex = d.day()\n return weekdays[dayIndex] || '星期日'\n }\n\n // 今年其他日期:显示MM月DD日\n if (d.isSame(now, 'year')) {\n return d.format('MM月DD日')\n }\n\n // 往年日期:显示YYYY年MM月DD日\n return d.format('YYYY年MM月DD日')\n}\n\n/**\n * 格式化时间戳为时长字符串\n *\n * 支持自定义格式化模板和智能单位显示。如果提供格式化字符串,则按照指定格式返回;\n * 如果不提供,则智能判断显示单位,只显示有意义的时间单位。\n *\n * @param timestamp 时间戳(毫秒)或者持续时间(毫秒)\n * @param format 可选的格式化字符串,支持 YY(年)、MM(月)、DD(天)、HH(小时)、mm(分钟)、ss(秒钟)占位符\n * @returns 格式化后的时长字符串\n * @group Date\n * @example\n * ```ts\n * // 使用自定义格式\n * formatDuration(3661000, 'HH:mm:ss') // \"01:01:01\"\n * formatDuration(3661000, 'HH时mm分ss秒') // \"01时01分01秒\"\n * formatDuration(90061000, 'DD天HH时mm分ss秒') // \"01天01时01分01秒\"\n * formatDuration(2592000000, 'MM月DD天') // \"01月00天\"\n * formatDuration(31536000000, 'YY年MM月DD天') // \"01年00月00天\"\n *\n * // 单一格式返回数字(双字母或单字母)\n * formatDuration(604800000, 'HH') // 168(总小时数)\n * formatDuration(604800000, 'H') // 168(总小时数)\n * formatDuration(3600000, 'mm') // 60(总分钟数)\n * formatDuration(3600000, 'm') // 60(总分钟数)\n * formatDuration(604800000, 'DD') // 7(总天数)\n * formatDuration(604800000, 'D') // 7(总天数)\n *\n * // 智能格式(只显示有意义的单位)\n * formatDuration(3600000) // \"1小时\"(正好1小时)\n * formatDuration(3661000) // \"1小时1分钟1秒\"(1小时1分钟1秒)\n * formatDuration(90061000) // \"1天1小时1分钟1秒\"(1天1小时1分钟1秒)\n * formatDuration(2592000000) // \"1个月\"(30天)\n * formatDuration(31536000000) // \"1年\"(365天)\n * formatDuration(125000) // \"2分钟5秒\"(2分钟5秒)\n * formatDuration(30000) // \"30秒\"(30秒)\n * formatDuration(5000) // \"5秒\"(5秒)\n * formatDuration(0) // \"0秒\"(0秒)\n *\n * // 处理超大时间值\n * formatDuration(94608000000) // \"3年\"(3*365天)\n * formatDuration(94694461000) // \"3年1天1小时1分钟1秒\"\n * ```\n */\nexport function formatDuration(timestamp: number, format?: string): string | number {\n // 确保timestamp是正数\n const duration = Math.abs(timestamp)\n\n // 定义时间单位常量(毫秒)\n const SECOND = 1000\n const MINUTE = 60 * SECOND\n const HOUR = 60 * MINUTE\n const DAY = 24 * HOUR\n const MONTH = 30 * DAY // 30天 = 1个月\n const YEAR = 365 * DAY // 365天 = 1年\n\n // 也可以在当前时间上加上 timestamp,使用 dayjs 计算距离现在多少时间,不需要手动计算\n // 计算各个时间单位\n const years = Math.floor(duration / YEAR)\n const months = Math.floor((duration % YEAR) / MONTH)\n const days = Math.floor((duration % MONTH) / DAY)\n const hours = Math.floor((duration % DAY) / HOUR)\n const minutes = Math.floor((duration % HOUR) / MINUTE)\n const seconds = Math.floor((duration % MINUTE) / SECOND)\n\n // 如果提供了格式化字符串,按照格式返回\n if (format) {\n const trimmedFormat = format.trim()\n\n // 计算各单位的总数\n const totalYears = Math.floor(duration / YEAR)\n const totalMonths = Math.floor(duration / MONTH)\n const totalDays = Math.floor(duration / DAY)\n const totalHours = Math.floor(duration / HOUR)\n const totalMinutes = Math.floor(duration / MINUTE)\n const totalSeconds = Math.floor(duration / SECOND)\n\n // 单位映射表\n const unitMap: Record<string, { total: number, current: number }> = {\n YY: { total: totalYears, current: years },\n Y: { total: totalYears, current: years },\n MM: { total: totalMonths, current: months },\n M: { total: totalMonths, current: months },\n DD: { total: totalDays, current: days },\n D: { total: totalDays, current: days },\n HH: { total: totalHours, current: hours },\n H: { total: totalHours, current: hours },\n mm: { total: totalMinutes, current: minutes },\n m: { total: totalMinutes, current: minutes },\n ss: { total: totalSeconds, current: seconds },\n s: { total: totalSeconds, current: seconds },\n }\n\n // 如果是单一单位格式,返回该单位的总数(数字)\n if (unitMap[trimmedFormat]) {\n return unitMap[trimmedFormat].total\n }\n\n // 复合格式:替换所有匹配的格式标记\n let result = format\n\n // 按照长度降序处理,避免短格式覆盖长格式(如 H 覆盖 HH)\n const formatTokens = ['YY', 'MM', 'DD', 'HH', 'mm', 'ss', 'Y', 'M', 'D', 'H', 'm', 's']\n\n for (const token of formatTokens) {\n if (result.includes(token) && unitMap[token]) {\n const { current } = unitMap[token]\n const paddedValue = current.toString().padStart(token.length, '0')\n result = result.replace(new RegExp(token, 'g'), paddedValue)\n }\n }\n\n return result\n }\n\n // 智能格式:只显示有意义的单位\n const parts: string[] = []\n\n if (years > 0) {\n parts.push(`${years}年`)\n }\n\n if (months > 0) {\n parts.push(`${months}个月`)\n }\n\n if (days > 0) {\n parts.push(`${days}天`)\n }\n\n if (hours > 0) {\n parts.push(`${hours}小时`)\n }\n\n if (minutes > 0) {\n parts.push(`${minutes}分钟`)\n }\n\n if (seconds > 0 || parts.length === 0) {\n parts.push(`${seconds}秒`)\n }\n\n return parts.join('')\n}\n","/**\n * @module 数字处理\n * @description 提供各种数字处理函数,包括数字格式化、计算、范围控制等实用功能\n */\n\n/**\n * 将数字四舍五入到指定小数位\n * @param num 要处理的数字\n * @param precision 小数位数,默认为0(整数)\n * @returns 四舍五入后的数字\n * @group Number\n * @example\n * ```ts\n * round(3.1415) // 3\n * round(3.1415, 2) // 3.14\n * round(3.1415, 3) // 3.142\n * round(3.9999) // 4\n * ```\n */\nexport function round(num: number, precision = 0): number {\n const factor = 10 ** precision\n return Math.round(num * factor) / factor\n}\n\n/**\n * 将数字格式化为带千位分隔符的字符串\n * @param num 要格式化的数字\n * @param locale 区域设置,默认为浏览器默认区域\n * @returns 格式化后的字符串\n * @group Number\n * @example\n * ```ts\n * formatThousands(1234567) // '1,234,567'(根据浏览器默认区域可能有所不同)\n * formatThousands(1234567.89, 'en-US') // '1,234,567.89'\n * formatThousands(1234567.89, 'de-DE') // '1.234.567,89'\n * ```\n */\nexport function formatThousands(num: number, locale?: string): string {\n return num.toLocaleString(locale)\n}\n\n/**\n * 将数字格式化为货币字符串\n * @param value 要格式化的数字\n * @param options 格式化选项\n * @param options.currency 货币代码(如 'CNY', 'USD'),默认为 'CNY'\n * @param options.locale 地区设置(如 'zh-CN', 'en-US'),默认为 'zh-CN'\n * @param options.minimumFractionDigits 最小小数位数,默认为 2\n * @param options.maximumFractionDigits 最大小数位数,默认为 2\n * @returns 格式化后的货币字符串\n * @group Number\n * @example\n * ```ts\n * formatCurrency(1234.56) // '¥1,234.56'\n * formatCurrency(1234.56, { currency: 'USD', locale: 'en-US' }) // '$1,234.56'\n * formatCurrency(1234.56789, { maximumFractionDigits: 4 }) // '¥1,234.5679'\n * formatCurrency(1234, { minimumFractionDigits: 0 }) // '¥1,234'\n * ```\n */\nexport function formatCurrency(\n value: number,\n options: {\n currency?: string\n locale?: string\n minimumFractionDigits?: number\n maximumFractionDigits?: number\n } = {},\n): string {\n const {\n currency = 'CNY',\n locale = 'zh-CN',\n minimumFractionDigits = 2,\n maximumFractionDigits = 2,\n } = options\n\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency,\n minimumFractionDigits,\n maximumFractionDigits,\n }).format(value)\n}\n\n/**\n * 确保数字在指定范围内\n * @param num 要限制范围的数字\n * @param min 最小值\n * @param max 最大值\n * @returns 在指定范围内的数字:如果小于最小值返回最小值,如果大于最大值返回最大值\n * @group Number\n * @example\n * ```ts\n * clamp(5, 0, 10) // 5 - 在范围内,保持不变\n * clamp(-5, 0, 10) // 0 - 小于最小值,返回最小值\n * clamp(15, 0, 10) // 10 - 大于最大值,返回最大值\n * ```\n */\nexport function clamp(num: number, min: number, max: number): number {\n return Math.min(Math.max(num, min), max)\n}\n\n/**\n * 生成指定范围内的随机整数(包含边界值)\n * @param min 最小值(包含)\n * @param max 最大值(包含)\n * @returns 指定范围内的随机整数\n * @group Number\n * @example\n * ```ts\n * randomInt(1, 10) // 返回 1 到 10 之间的随机整数,包括 1 和 10\n * randomInt(0, 1) // 返回 0 或 1\n * randomInt(5, 5) // 总是返回 5\n * ```\n */\nexport function randomInt(min: number, max: number): number {\n min = Math.ceil(min)\n max = Math.floor(max)\n return Math.floor(Math.random() * (max - min + 1)) + min\n}\n\n/**\n * 检查一个数字是否为偶数\n * @param num 要检查的数字\n * @returns 如果是偶数则返回 true,否则返回 false\n * @group Number\n * @example\n * ```ts\n * isEven(2) // true\n * isEven(3) // false\n * isEven(0) // true\n * isEven(-4) // true\n * ```\n */\nexport function isEven(num: number): boolean {\n return num % 2 === 0\n}\n\n/**\n * 检查一个数字是否为奇数\n * @param num 要检查的数字\n * @returns 如果是奇数则返回 true,否则返回 false\n * @group Number\n * @example\n * ```ts\n * isOdd(3) // true\n * isOdd(2) // false\n * isOdd(0) // false\n * isOdd(-3) // true\n * ```\n */\nexport function isOdd(num: number): boolean {\n return !isEven(num)\n}\n\n/**\n * 计算百分比值\n * @param value 当前值\n * @param total 总值\n * @param precision 结果保留的小数位数,默认为2\n * @returns 百分比值,如果总值为零则返回 0\n * @group Number\n * @example\n * ```ts\n * percentage(25, 100) // 25\n * percentage(1, 3) // 33.33\n * percentage(1, 3, 0) // 33\n * percentage(5, 0) // 0,避免除以零错误\n * ```\n */\nexport function percentage(value: number, total: number, precision = 2): number {\n if (total === 0)\n return 0\n return round((value / total) * 100, precision)\n}\n\n/**\n * 将数值转换为带\"万\"单位的字符串\n * @param num 要转换的数值\n * @param fractionDigits 小数位数,默认为2\n * @returns 转换后的字符串,如果值大于等于10000则转为\"万\"单位\n * @group Number\n * @example\n * ```ts\n * formatNumberWithTenThousand(1234) // '1234'\n * formatNumberWithTenThousand(12345) // '1.23万'\n * formatNumberWithTenThousand(12345, 1) // '1.2万'\n * formatNumberWithTenThousand(0) // '0'\n * ```\n */\nexport function formatNumberWithTenThousand(num: number, fractionDigits = 2): string {\n if (!num) {\n return '0'\n }\n if (num >= 10000) {\n // 保留两位小数\n return `${(num / 10000).toFixed(fractionDigits)}万`\n }\n else {\n return num.toString()\n }\n}\n","/**\n * 延迟指定的毫秒数\n *\n * @param {number} ms - 需要延迟的毫秒数\n * @returns {Promise<void>} 返回一个在指定时间后resolve的Promise\n * @group Promise\n * @example\n * ```ts\n * // 延迟2秒\n * await delay(2000)\n * console.log('2秒后执行')\n *\n * // 在异步函数中使用\n * async function fetchWithDelay() {\n * await delay(1000)\n * return fetch('/api/data')\n * }\n * ```\n */\nexport function delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n","import { objectToQueryString } from '../object'\n\n/**\n * 将对象转换为URL查询字符串\n *\n * @deprecated 请使用 objectToQueryString 函数代替,它在 object 模块中。该函数将在下一个主要版本中移除。\n * @param {Record<string, any>} params - 需要转换的对象\n * @returns {string} 转换后的查询字符串,如果有参数则以?开头\n * @group Url\n * @example\n * ```ts\n * // 基本用法\n * getQueryStringify({ name: 'John', age: 30 })\n * // 返回: ?name=John&age=30\n *\n * // 包含对象的情况\n * getQueryStringify({ user: { name: 'John', age: 30 }, active: true })\n * // 返回: ?user={\"name\":\"John\",\"age\":30}&active=true\n *\n * // 空对象\n * getQueryStringify({})\n * // 返回: ''\n *\n * // null 或 undefined\n * getQueryStringify(null)\n * // 返回: ''\n * ```\n */\nexport function getQueryStringify(params: Record<string, any> | null | undefined): string {\n if (!params)\n return ''\n\n const queryString = objectToQueryString(params)\n\n if (queryString)\n return `?${queryString}`\n\n return ''\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pmun/utils",
3
3
  "type": "module",
4
- "version": "0.3.5",
4
+ "version": "0.3.7",
5
5
  "description": "Sunpm 的主观个人工具函数库",
6
6
  "author": "sunpm",
7
7
  "license": "MIT",