@jhqn/utils-core 0.3.3 → 0.4.0

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.js CHANGED
@@ -1,2 +1,1072 @@
1
- import e from"dayjs";import t from"dayjs/locale/zh-cn.js";import n from"dayjs/plugin/customParseFormat.js";import r from"dayjs/plugin/duration.js";import i from"dayjs/plugin/localeData.js";import a from"dayjs/plugin/objectSupport.js";import o from"dayjs/plugin/relativeTime.js";import{detect as s}from"detect-browser";const c=e=>e.replace(/_([a-z])/g,(e,t)=>`-${t.toLowerCase()}`),l=e=>e.replace(/_([a-z])/g,(e,t)=>t.toUpperCase()).replace(/^[a-z]/,e=>e.toUpperCase()),ee=e=>e.replace(/(_([a-z]))/g,e=>e[1].toUpperCase()),te=e=>e.replace(/_([a-z])/g,(e,t)=>` ${t.toUpperCase()}`).replace(/^[a-z]/,e=>e.toUpperCase()),ne=e=>e.replace(/-([a-z])/g,(e,t)=>`_${t.toLowerCase()}`),re=e=>e.replace(/-([a-z])/g,(e,t)=>t.toUpperCase()).replace(/^[a-z]/,e=>e.toUpperCase()),ie=e=>{let[t,...n]=e.split(`-`);return[t,...n.map(e=>e.replace(/^[a-z]/,e[0].toUpperCase()))].join(``)},ae=e=>e.replace(/-([a-z])/g,(e,t)=>` ${t.toUpperCase()}`).replace(/^[a-z]/,e=>e.toUpperCase()),oe=e=>e.replace(/([A-Z])/g,(e,t)=>`_${t.toLowerCase()}`).replace(/^_/,``),se=e=>e.replace(/([A-Z])/g,(e,t)=>`-${t.toLowerCase()}`).replace(/^-/,``),ce=e=>e.replace(/^[A-Z]/,e=>e.toLowerCase()),le=e=>e.replace(/([A-Z])/g,(e,t)=>` ${t}`).replace(/^ /,``),ue=e=>e.replace(/([A-Z])/g,(e,t)=>`_${t.toLowerCase()}`),de=e=>e.replace(/([A-Z])/g,(e,t)=>`-${t.toLowerCase()}`),fe=e=>e.replace(/(^[a-z])|(\s+[a-z])/g,e=>e.toUpperCase()),pe=e=>e.replace(/([A-Z])/g,(e,t)=>` ${t}`).replace(/^[a-z]/,e=>e.toUpperCase()),me=e=>e.replace(/([A-Z])/g,e=>e.toLowerCase()).replace(/ /g,`_`),he=e=>e.replace(/([A-Z])/g,e=>e.toLowerCase()).replace(/ /g,`-`),ge=e=>e.replace(/ /g,``),_e=e=>e.replace(/ /g,``).replace(/^[A-Z]/,e=>e.toLowerCase()),u=`color: white; padding: 1px 2px;`,d=(...e)=>__DEV__?console.log.bind(console,`%c[${e.length===1?`LOG`:`%s`}]`,`${u} background: seagreen;`)(...e):()=>{},ve=(...e)=>console.log.bind(console,`%c[${e.length===1?`DEBUG`:`%s`}]`,`${u} background: linear-gradient(LightSalmon, Tomato); border-radius: 2px; font-weight: bold; border-top: 2px solid Crimson; border-bottom: 2px solid Crimson;`)(...e),f=(...e)=>console.warn.bind(console,`%c[${e.length===1?`WARN`:`%s`}]`,`${u} background: tomato;`)(...e),p=(...e)=>console.error.bind(console,`%c[${e.length===1?`ERROR`:`%s`}]`,`${u} background: crimson;`)(...e),m=(...e)=>console.info.bind(console,`%c[${e.length===1?`INFO`:`%s`}]`,`${u} background: dodgerblue;`)(...e),ye=console.group.bind(console,`%c[%s]`,`${u} background: slateblue;`),be=console.groupCollapsed.bind(console,`%c[%s]`,`${u} background: slateblue;`),xe=console.groupEnd.bind(console),Se=e=>console.time(`[${e}]`),Ce=e=>console.timeEnd(`[${e}]`),we=console.clear.bind(console);let h;const Te=(...e)=>{e.length!==0&&(e.length>0&&h&&JSON.stringify(e)===JSON.stringify(h)||(d(...e),h=e))};let Ee=!1;const De=(...e)=>{Ee||=(d(...e),!0)},g=e=>(Object.prototype.toString.call(e).match(/\[object (\S*)\]/)?.[1])?.toLocaleLowerCase(),Oe=e=>g(e)===`number`,ke=e=>g(e)===`bigint`,Ae=e=>g(e)===`string`,je=e=>g(e)===`boolean`,_=e=>g(e)===`null`,v=e=>g(e)===`undefined`,Me=e=>g(e)===`symbol`,y=e=>g(e)===`object`,b=e=>g(e)===`array`,x=e=>g(e)===`function`,S=e=>g(e)===`date`,Ne=e=>g(e)===`regexp`,Pe=e=>g(e)===`promise`,C=e=>g(e)===`set`,w=e=>g(e)===`map`,Fe=e=>g(e)===`weakset`,Ie=e=>g(e)===`weakmap`,Le=e=>g(e)===`file`,Re=e=>g(e)===`blob`,ze=e=>g(e)===`url`,Be=e=>g(e)===`error`,T=e=>!_(e)&&!v(e),E=e=>{if(T(e))try{return JSON.parse(e),!0}catch(e){return f(`Failed to parse invalid string`,e),!1}return!1},D=e=>{if(T(e))try{return Array.isArray(JSON.parse(e))}catch(e){return f(`Failed to parse invalid string`,e),!1}return!1},Ve=(e,t)=>e in t,He=e=>!T(e)||e===``||b(e)&&e.length===0||y(e)&&Object.keys(e).length===0||w(e)&&e.size===0||C(e)&&e.size===0,O=/^1[3-9]\d{9}$/,k=/^0\d{2,3}-?\d{7,8}$/,A=/^\(0\d{2,3}\)\d{7,8}$/,j=/^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/,M=/^[1-9]\d{7}(?:0[1-9]|10|11|12)(?:[0-2][1-9]|10|20|30|31)\d{2}[\dX]$/i,N=/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|1[0-2])(?:[0-2][1-9]|10|20|30|31)\d{3}[\dX]$/i,P=/^[a-z\d](?:\w|-)+@[a-z\d]+\.[a-z]{2,5}$/i,Ue=e=>T(e)?j.test(e):!1,F=e=>T(e)?O.test(e.toString()):!1,We=e=>{if(!T(e))return!1;let t=e.toString().replaceAll(` `,``);return k.test(t)||A.test(t)},Ge=e=>T(e)?P.test(e):!1,I=(e,t=!0)=>T(e)?N.test(e)||(t?M.test(e):!1):!1,Ke=e=>T(e)?typeof e==`number`||/^\d+(?:\.\d+)?$/.test(e):!1,L=e=>/\.(?:jpg|jpeg|png|gif)$/.test((e||``).toLowerCase()),qe=e=>/[^\x20-\x7E]/.test(e),Je=(e,t)=>{if(t===`*`)return!0;let n=e.type,r=e.name;function i(e){return e.startsWith(`.`)?r.endsWith(e):e.endsWith(`/*`)?n.startsWith(e.replace(`/*`,``)):e.includes(`/`)?n===e:!1}let a=t.split(`,`).map(e=>e.trim());return a.length>1?a.some(i):i(t)},R=(e,t=200)=>e.code===t,Ye=(e,t)=>R(e,t)&&T(e.data),Xe=(e,t)=>R(e,t)&&Array.isArray(e.data),Ze=(e,t)=>R(e,t)&&T(e.data)&&Array.isArray(e.data.list||e.data.records),z=e=>{if(!T(e))return``;let t=e.length;return t<=2?`*`.repeat(t):e[0]+`*`.repeat(t-2)+e[t-1]},Qe=e=>{if(!T(e))return``;let t=e.length;if(t<=2)return`*`.repeat(t);let n=Math.round(t/3);return e.slice(0,n)+`*`.repeat(t-2*n)+e.slice(t-n)},B=(e,t)=>{if(!T(e))return``;let n=e.length;if(n<=1)return`*`.repeat(n);let r=t===void 0?n-1:Math.max(Math.min(n,t),0);return`*`.repeat(r)+e.substring(r)},V=(e,t)=>{if(!T(e))return``;let n=e.length;if(n<=1)return`*`.repeat(n);let r=t===void 0?n-1:Math.max(Math.min(n,t),0);return e.substring(0,n-r)+`*`.repeat(r)},$e=e=>{if(e){let t=e.replaceAll(` `,``);return F(t)?t.replace(/^(\d{3})\d{4}(\d{4})$/,`$1****$2`):k.test(t)?t.replace(/^(0\d{2,3})-?\d{3,4}(\d{4})$/,`$1****$2`):A.test(t)?t.replace(/^\((0\d{2,3})\)\d{3,4}(\d{4})$/,`$1****$2`):e}return``},et=(e,t=!0)=>e?I(e,!1)?t?z(e):e.replace(/^(.)\d{3}(.{8})\d{5}(.)$/,`$1***$2*****$3`):e:``,tt=e=>B(e,1),nt=e=>V(e),rt=e=>{if(e){if(!e.includes(`@`))return e;let t=e.split(`@`),n=t[0],r=t[1];if(!n||!r)return e;let i=n.length,a;return a=i<=3?`${n}***`:V(n,3),`${a}@${r}`}return``},it=e=>{if(e){let t=e.length;return t<=10?e:e.substring(0,6)+`*`.repeat(t-10)+e.substring(t-4,t)}return``};let H;function at(e){H=e}function U(){return H}function ot(e,t){return E(e)?JSON.parse(e,t):null}function W(e,t=K){return E(e)?JSON.parse(e,t):null}function st(e,t=G){return JSON.stringify(e,t)}function G(e,t){return w(t)?{dataType:`Map`,value:[...t]}:C(t)?{dataType:`Set`,value:[...t]}:S(t)?{dataType:`Date`,value:t.toISOString()}:t}function K(e,t){if(typeof t==`object`&&t){if(t.dataType===`Map`)return new Map(t.value);if(t.dataType===`Set`)return new Set(t.value);if(t.dataType===`Date`)return new Date(t.value)}return t}const ct=(e,t,n=!1)=>Object.fromEntries(Object.entries(e).filter(([e])=>n?!t.includes(e):t.includes(e))),lt=e=>{let t=e.toString().split(`.`),n=t[0]?.replace(/(\d)(?=(?:\d{3})+$)/g,`$1,`);return t.length>1?`${n}.${t[1]}`:n},ut=(e,t=2)=>{if(!T(e))return``;let n=Number(e);return Number.isNaN(n)?``:new Intl.NumberFormat(`zh-CN`,{style:`currency`,currency:`CNY`,maximumFractionDigits:t}).format(n)};e.extend(i).locale(`zh-cn`,t),e.extend(r),e.extend(o),e.extend(n),e.extend(a);const q=(t,n=`YYYY-MM-DD HH:mm:ss`)=>{let r=e(t),i=n;return!T(t)||!r.isValid()?``:(n===`date`&&(i=`YYYY-MM-DD`),n===`datetime`&&(i=`YYYY-MM-DD HH:mm:ss`),r.format(i))},dt=(t,n)=>{let r=e(t),i=e(n);if(!T(t)||!r.isValid()||!T(n)||!i.isValid())return``;let a=e.duration(i.diff(r)),o=a.years(),s=a.months(),c=a.days(),l=[o===0?``:`Y年`,s===0?``:`M个月`,c===0?``:`${c<10&&(o!==0||s!==0)?`零`:``}D天`].join(``);return a.format(l)},J=e=>{if(!T(e))return;if(/^https?:\/\//.test(e)||e.startsWith(`blob:`)||e.startsWith(`data:`))return e;let t=U();if(!T(t))throw Error(`请先使用 setBaseAttachUrl 设置文件基本路径`);return`${t}/${e}`.replace(/([^:]\/)\/+/g,`$1`)},Y=e=>{let{uri:t,url:n,...r}=e,i=t||n;return{...r,url:J(i),content:J(i),uri:i||``,status:`done`,percent:100}},ft=e=>{let t=[],n=Object.prototype.toString.call(e);if(!e)t=[];else if(Array.isArray(e))t=e.filter(Boolean);else if(typeof e==`string`)if(D(e))t=(W(e)||[]).filter(Boolean);else{let n=W(e);t=Object.prototype.toString.call(n)===`[object Object]`?[n].filter(Boolean):[{id:e,name:`name`,uri:e,group:`default`}]}else n===`[object Object]`&&(t=[e]);return t.map(Y)},pt=e=>e.every(e=>typeof e==`number`),mt=e=>e.every(e=>typeof e==`string`),X=e=>{if(pt(e))return[...e].sort((e,t)=>e-t);if(mt(e))return[...e].sort((e,t)=>e.localeCompare(t,`zh-CN`)).sort((e,t)=>e.length-t.length);let t=[...e].sort(),n=t.filter(e=>T(e)),r=t.filter(e=>!T(e));return X(n).concat(r)},ht=(e,t)=>[...e].sort((e,n)=>X([e[t],n[t]])[0]===e[t]?-1:1),Z=typeof window<`u`,gt=(e=100)=>new Promise(t=>{setTimeout(()=>{t(!0)},e)}),_t=()=>{if(CSS.supports(`(font:-apple-system-body) and (-webkit-touch-callout:none) and (-webkit-tap-highlight-color:hotpink)`)){let e=document.querySelector(`meta[name="viewport"]`);e!==null&&(e.content=`${e.content}, user-scalable=no`)}},Q=Z?window.navigator.userAgent:``,vt=()=>{let e=Q.toLowerCase();return{zlb:e.includes(`@zlb`),zyd:e.includes(`saas`),zzd:e.includes(`zhejiang`)||e.includes(`tauruszjd`)||e.includes(`dingtalk-win`),dd:e.includes(`taurusapp`),wx:e.includes(`micromessenger`),zfb:e.includes(`alipay`),mini:e.includes(`miniprogram`)}},$=e=>Math.abs(new Date(e).getTime()^1111),yt=()=>$(`${q(new Date,`YYYY-MM-DD HH`)}:00:00`),bt=()=>$(q(new Date,`date`)),xt=()=>$(q(e().endOf(`month`),`date`)),St=(e,t=200,n=`success`)=>({data:e,code:t,message:n}),Ct=(e,t,n=200,r=`success`,i={})=>({data:{records:e,list:e,total:t,...i},code:n,message:r});export{ft as attachFmt,de as camel2Kebab,fe as camel2Pascal,ue as camel2Snake,pe as camel2Title,L as checkImg,M as cnIdCard15Re,N as cnIdCard18Re,O as cnPhoneRE,k as cnTelRE,A as cnTelRE2,j as cnUSCIRE,ut as currencyFmt,dt as dateDuration,q as dateFmt,e as dayjs,s as detect,P as emailRE,ct as filterObj,_t as fixiOSInputAutoZoomIn,U as getBaseAttachUrl,bt as getCodeOfDate,yt as getCodeOfHour,xt as getCodeOfMonth,vt as getHostEnv,St as getMockData,Ct as getMockDataList,g as getVariableType,it as hideBankCard,et as hideCardNo,rt as hideEmail,nt as hideFirstName,B as hideHead,z as hideMiddle,$e as hidePhone,tt as hideSurname,V as hideTail,Qe as hideThird,Z as inBrowser,D as isArrStr,b as isArray,ke as isBigInt,Re as isBlob,je as isBoolean,S as isDate,T as isDef,Ge as isEmail,He as isEmpty,Be as isError,Le as isFile,x as isFunction,I as isIdCard,E as isJSONStr,w as isMap,_ as isNull,Oe as isNumber,Ke as isNumeric,y as isObject,F as isPhone,Pe as isPromise,Ne as isRegExp,C as isSet,Ae as isString,Me as isSymbol,We as isTel,ze as isURL,Ue as isUSCI,v as isUndefined,Xe as isValidArrRes,Je as isValidFileType,Ve as isValidKey,Ze as isValidPageRes,Ye as isValidRes,R as isValidResCode,Ie as isWeakMap,Fe as isWeakSet,we as jClear,ve as jDebug,p as jError,ye as jGroup,be as jGroupC,xe as jGroupE,m as jInfo,d as jLog,Te as jLogDedupe,De as jLogOnce,Se as jTime,Ce as jTimeEnd,f as jWarn,ie as kebab2Camel,re as kebab2Pascal,ne as kebab2Snake,ae as kebab2Title,X as naturalSort,ht as naturalSortBy,qe as nonASCII,W as parseJSON,ot as parseToJSON,ce as pascal2Camel,se as pascal2Kebab,oe as pascal2Snake,le as pascal2Title,Y as recoverFile,G as replacer,J as resUrl,K as reviver,at as setBaseAttachUrl,ee as snake2Camel,c as snake2Kebab,l as snake2Pascal,te as snake2Title,st as stringifyJSON,_e as title2Camel,he as title2Kebab,ge as title2Pascal,me as title2Snake,lt as toThousands,Q as userAgent,gt as waitTime};
2
- //# sourceMappingURL=index.js.map
1
+ import dayjs from "dayjs";
2
+ import zhCN from "dayjs/locale/zh-cn.js";
3
+ import customParseFormat from "dayjs/plugin/customParseFormat.js";
4
+ import duration from "dayjs/plugin/duration.js";
5
+ import localeData from "dayjs/plugin/localeData.js";
6
+ import objectSupport from "dayjs/plugin/objectSupport.js";
7
+ import relativeTime from "dayjs/plugin/relativeTime.js";
8
+
9
+ //#region src/case.ts
10
+ /**
11
+ * snake2Kebab
12
+ * @param {string} str 字符串
13
+ * @returns {string} 转换后的字符串
14
+ * @example snake2Kebab('hello_world') // hello_world => hello-world
15
+ */
16
+ const snake2Kebab = (str) => str.replace(/_([a-z])/g, (_, letter) => `-${letter.toLowerCase()}`);
17
+ /**
18
+ * snake2Pascal
19
+ * @param {string} str 字符串
20
+ * @returns {string} 转换后的字符串
21
+ * @example snake2Pascal('hello_world') // hello_world => HelloWorld
22
+ */
23
+ const snake2Pascal = (str) => str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()).replace(/^[a-z]/, (letter) => letter.toUpperCase());
24
+ /**
25
+ * snake2Camel
26
+ * @param {string} str 字符串
27
+ * @returns {string} 转换后的字符串
28
+ * @example snake2Camel('hello_world') // hello_world => helloWorld
29
+ */
30
+ const snake2Camel = (str) => str.replace(/(_([a-z]))/g, (match) => match[1].toUpperCase());
31
+ /**
32
+ * snake2Title
33
+ * @param {string} str 字符串
34
+ * @returns {string} 转换后的字符串
35
+ * @example snake2Title('hello_world') // hello_world => Hello World
36
+ */
37
+ const snake2Title = (str) => str.replace(/_([a-z])/g, (_, letter) => ` ${letter.toUpperCase()}`).replace(/^[a-z]/, (letter) => letter.toUpperCase());
38
+ /**
39
+ * kebab2Snake
40
+ * @param {string} str 字符串
41
+ * @returns {string} 转换后的字符串
42
+ * @example kebab2Snake('hello-world') // hello-world => hello_world
43
+ */
44
+ const kebab2Snake = (str) => str.replace(/-([a-z])/g, (_, letter) => `_${letter.toLowerCase()}`);
45
+ /**
46
+ * kebab2Pascal
47
+ * @param {string} str 字符串
48
+ * @returns {string} 转换后的字符串
49
+ * @example kebab2Pascal('hello-world') // hello-world => HelloWorld
50
+ */
51
+ const kebab2Pascal = (str) => str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()).replace(/^[a-z]/, (letter) => letter.toUpperCase());
52
+ /**
53
+ * kebab2Camel
54
+ * @param {string} str 字符串
55
+ * @returns {string} 转换后的字符串
56
+ * @example kebab2Camel('hello-world') // hello-world => helloWorld
57
+ */
58
+ const kebab2Camel = (str) => {
59
+ const [first, ...rest] = str.split("-");
60
+ return [first, ...rest.map((s) => s.replace(/^[a-z]/, s[0].toUpperCase()))].join("");
61
+ };
62
+ /**
63
+ * kebab2Title
64
+ * @param {string} str 字符串
65
+ * @returns {string} 转换后的字符串
66
+ * @example kebab2Title('hello-world') // hello-world => Hello World
67
+ */
68
+ const kebab2Title = (str) => str.replace(/-([a-z])/g, (_, letter) => ` ${letter.toUpperCase()}`).replace(/^[a-z]/, (letter) => letter.toUpperCase());
69
+ /**
70
+ * pascal2Snake
71
+ * @param {string} str 字符串
72
+ * @returns {string} 转换后的字符串
73
+ * @example pascal2Snake('HelloWorld') // HelloWorld => hello_world
74
+ */
75
+ const pascal2Snake = (str) => str.replace(/([A-Z])/g, (_, letter) => `_${letter.toLowerCase()}`).replace(/^_/, "");
76
+ /**
77
+ * pascal2Kebab
78
+ * @param {string} str 字符串
79
+ * @returns {string} 转换后的字符串
80
+ * @example pascal2Kebab('HelloWorld') // HelloWorld => hello-world
81
+ */
82
+ const pascal2Kebab = (str) => str.replace(/([A-Z])/g, (_, letter) => `-${letter.toLowerCase()}`).replace(/^-/, "");
83
+ /**
84
+ * pascal2Camel
85
+ * @param {string} str 字符串
86
+ * @returns {string} 转换后的字符串
87
+ * @example pascal2Camel('HelloWorld') // HelloWorld => helloWorld
88
+ */
89
+ const pascal2Camel = (str) => str.replace(/^[A-Z]/, (letter) => letter.toLowerCase());
90
+ /**
91
+ * pascal2Title
92
+ * @param {string} str 字符串
93
+ * @returns {string} 转换后的字符串
94
+ * @example pascal2Title('HelloWorld') // HelloWorld => Hello World
95
+ */
96
+ const pascal2Title = (str) => str.replace(/([A-Z])/g, (_, letter) => ` ${letter}`).replace(/^ /, "");
97
+ /**
98
+ * camel2Snake
99
+ * @param {string} str 字符串
100
+ * @returns {string} 转换后的字符串
101
+ * @example camel2Snake('helloWorld') // helloWorld => hello_world
102
+ */
103
+ const camel2Snake = (str) => str.replace(/([A-Z])/g, (_, letter) => `_${letter.toLowerCase()}`);
104
+ /**
105
+ * camel2Kebab
106
+ * @param {string} str 字符串
107
+ * @returns {string} 转换后的字符串
108
+ * @example camel2Kebab('helloWorld') // helloWorld => hello-world
109
+ */
110
+ const camel2Kebab = (str) => str.replace(/([A-Z])/g, (_, letter) => `-${letter.toLowerCase()}`);
111
+ /**
112
+ * camel2Pascal
113
+ * @param {string} str 字符串
114
+ * @returns {string} 转换后的字符串
115
+ * @example camel2Pascal('helloWorld') // helloWorld => HelloWorld
116
+ */
117
+ const camel2Pascal = (str) => str.replace(/(^[a-z])|(\s+[a-z])/g, (match) => match.toUpperCase());
118
+ /**
119
+ * camel2Title
120
+ * @param {string} str 字符串
121
+ * @returns {string} 转换后的字符串
122
+ * @example camel2Title('helloWorld') // helloWorld => Hello World
123
+ */
124
+ const camel2Title = (str) => str.replace(/([A-Z])/g, (_, letter) => ` ${letter}`).replace(/^[a-z]/, (match) => match.toUpperCase());
125
+ /**
126
+ * title2Snake
127
+ * @param {string} str 字符串
128
+ * @returns {string} 转换后的字符串
129
+ * @example title2Snake('Hello World') // Hello World => hello_world
130
+ */
131
+ const title2Snake = (str) => str.replace(/([A-Z])/g, (match) => match.toLowerCase()).replace(/ /g, "_");
132
+ /**
133
+ * title2Kebab
134
+ * @param {string} str 字符串
135
+ * @returns {string} 转换后的字符串
136
+ * @example title2Kebab('Hello World') // Hello World => hello-world
137
+ */
138
+ const title2Kebab = (str) => str.replace(/([A-Z])/g, (match) => match.toLowerCase()).replace(/ /g, "-");
139
+ /**
140
+ * title2Pascal
141
+ * @param {string} str 字符串
142
+ * @returns {string} 转换后的字符串
143
+ * @example title2Pascal('Hello World') // Hello World => HelloWorld
144
+ */
145
+ const title2Pascal = (str) => str.replace(/ /g, "");
146
+ /**
147
+ * title2Camel
148
+ * @param {string} str 字符串
149
+ * @returns {string} 转换后的字符串
150
+ * @example title2Camel('Hello World') // Hello World => helloWorld
151
+ */
152
+ const title2Camel = (str) => str.replace(/ /g, "").replace(/^[A-Z]/, (match) => match.toLowerCase());
153
+
154
+ //#endregion
155
+ //#region src/console/log-common.ts
156
+ const sharedStyle = "color: white; padding: 1px 2px;";
157
+ /**
158
+ * jade console.log
159
+ * only for development
160
+ * @param {...any} args
161
+ */
162
+ const jLog = (...args) => __DEV__ ? console.log.bind(console, `%c[${args.length === 1 ? "LOG" : "%s"}]`, `${sharedStyle} background: seagreen;`)(...args) : () => {};
163
+ /**
164
+ * jade console.debug
165
+ * @param {...any} args
166
+ */
167
+ const jDebug = (...args) => console.log.bind(console, `%c[${args.length === 1 ? "DEBUG" : "%s"}]`, `${sharedStyle} background: linear-gradient(LightSalmon, Tomato); border-radius: 2px; font-weight: bold; border-top: 2px solid Crimson; border-bottom: 2px solid Crimson;`)(...args);
168
+ /**
169
+ * jade console.warn
170
+ */
171
+ const jWarn = (...args) => console.warn.bind(console, `%c[${args.length === 1 ? "WARN" : "%s"}]`, `${sharedStyle} background: tomato;`)(...args);
172
+ /**
173
+ * jade console.error
174
+ */
175
+ const jError = (...args) => console.error.bind(console, `%c[${args.length === 1 ? "ERROR" : "%s"}]`, `${sharedStyle} background: crimson;`)(...args);
176
+ /**
177
+ * jade console.info
178
+ * @param {...any} args
179
+ */
180
+ const jInfo = (...args) => console.info.bind(console, `%c[${args.length === 1 ? "INFO" : "%s"}]`, `${sharedStyle} background: dodgerblue;`)(...args);
181
+ /**
182
+ * jade console.group
183
+ */
184
+ const jGroup = console.group.bind(console, "%c[%s]", `${sharedStyle} background: slateblue;`);
185
+ /**
186
+ * jade console.groupCollapsed
187
+ */
188
+ const jGroupC = console.groupCollapsed.bind(console, "%c[%s]", `${sharedStyle} background: slateblue;`);
189
+ /**
190
+ * jade console.groupEnd
191
+ */
192
+ const jGroupE = console.groupEnd.bind(console);
193
+ /**
194
+ * jade console.time
195
+ * @param str
196
+ */
197
+ const jTime = (str) => console.time(`[${str}]`);
198
+ /**
199
+ * jade console.timeEnd
200
+ * @param str
201
+ */
202
+ const jTimeEnd = (str) => console.timeEnd(`[${str}]`);
203
+ /**
204
+ * jade console.clear
205
+ */
206
+ const jClear = console.clear.bind(console);
207
+
208
+ //#endregion
209
+ //#region src/console/log-dedupe.ts
210
+ let prevValue;
211
+ /**
212
+ * 不连续打印相同的值
213
+ * @param args
214
+ */
215
+ const jLogDedupe = (...args) => {
216
+ if (args.length === 0) return;
217
+ if (args.length > 0 && prevValue && JSON.stringify(args) === JSON.stringify(prevValue)) return;
218
+ jLog(...args);
219
+ prevValue = args;
220
+ };
221
+
222
+ //#endregion
223
+ //#region src/console/log-once.ts
224
+ let logged = false;
225
+ /**
226
+ * 只打印一次
227
+ * @param args
228
+ */
229
+ const jLogOnce = (...args) => {
230
+ if (logged) return;
231
+ jLog(...args);
232
+ logged = true;
233
+ };
234
+
235
+ //#endregion
236
+ //#region src/validate/core.ts
237
+ /**
238
+ * 检测变量类型
239
+ * @param {any} val 检测的目标
240
+ */
241
+ const getVariableType = (val) => Object.prototype.toString.call(val).toLocaleLowerCase().slice(8, -1);
242
+ /**
243
+ * 是否是数字
244
+ * @param {any} val 检测的目标
245
+ */
246
+ const isNumber = (val) => typeof val === "number";
247
+ /**
248
+ * 是否是任意精度整数
249
+ * @param {any} val 检测的目标
250
+ */
251
+ const isBigInt = (val) => typeof val === "bigint";
252
+ /**
253
+ * 是否是字符串
254
+ * @param {any} val 检测的目标
255
+ */
256
+ const isString = (val) => typeof val === "string";
257
+ /**
258
+ * 是否是布尔值
259
+ * @param {any} val 检测的目标
260
+ */
261
+ const isBoolean = (val) => typeof val === "boolean";
262
+ /**
263
+ * 是否是null
264
+ * @param {any} val 检测的目标
265
+ */
266
+ const isNull = (val) => val === null;
267
+ /**
268
+ * 是否是undefined
269
+ * @param {any} val 检测的目标
270
+ */
271
+ const isUndefined = (val) => typeof val === "undefined";
272
+ /**
273
+ * 是否是symbol
274
+ * @param {any} val 检测的目标
275
+ */
276
+ const isSymbol = (val) => typeof val === "symbol";
277
+ /**
278
+ * 是否是对象
279
+ * @param {any} val 检测的目标
280
+ */
281
+ const isObject = (val) => getVariableType(val) === "object";
282
+ /**
283
+ * 是否是数组
284
+ * @param {any} val 检测的目标
285
+ */
286
+ const isArray = (val) => Array.isArray(val);
287
+ /**
288
+ * 是否是函数,但也有可能是Class
289
+ * @param {any} val 检测的目标
290
+ */
291
+ const isFunction = (val) => getVariableType(val) === "function";
292
+ /**
293
+ * 是否是日期
294
+ * @param {any} val 检测的目标
295
+ */
296
+ const isDate = (val) => val instanceof Date;
297
+ /**
298
+ * 是否是正则
299
+ * @param {any} val 检测的目标
300
+ */
301
+ const isRegExp = (val) => getVariableType(val) === "regexp";
302
+ /**
303
+ * 是否是期望(Promise)
304
+ * @param {any} val 检测的目标
305
+ */
306
+ const isPromise = (val) => getVariableType(val) === "promise";
307
+ /**
308
+ * 是否是集合(Set)
309
+ * @param {any} val 检测的目标
310
+ */
311
+ const isSet = (val) => getVariableType(val) === "set";
312
+ /**
313
+ * 是否是映射(Map)
314
+ * @param {any} val 检测的目标
315
+ */
316
+ const isMap = (val) => getVariableType(val) === "map";
317
+ /**
318
+ * 是否是弱集合(WeakSet)
319
+ * @param {any} val 检测的目标
320
+ */
321
+ const isWeakSet = (val) => getVariableType(val) === "weakset";
322
+ /**
323
+ * 是否是弱映射(WeakMap)
324
+ * @param {any} val 检测的目标
325
+ */
326
+ const isWeakMap = (val) => getVariableType(val) === "weakmap";
327
+ /**
328
+ * 是否是File
329
+ * @param {any} val 检测的目标
330
+ */
331
+ const isFile = (val) => getVariableType(val) === "file";
332
+ /**
333
+ * 是否是Blob
334
+ * @param {any} val 检测的目标
335
+ */
336
+ const isBlob = (val) => getVariableType(val) === "blob";
337
+ /**
338
+ * 是否是URL
339
+ * @param {any} val 检测的目标
340
+ */
341
+ const isURL = (val) => getVariableType(val) === "url";
342
+ /**
343
+ * 是否是错误(Error)
344
+ * @param {any} val 检测的目标
345
+ */
346
+ const isError = (val) => getVariableType(val) === "error";
347
+ /**
348
+ * 是否既不是 null 也不是 undefined
349
+ * @param {any} val 检测的目标
350
+ */
351
+ const isDef = (val) => !isNull(val) && !isUndefined(val);
352
+ /**
353
+ * 判断是否合法JSON字符串
354
+ * @param str
355
+ */
356
+ const isJSONStr = (str) => {
357
+ if (isDef(str)) try {
358
+ JSON.parse(str);
359
+ return true;
360
+ } catch (e) {
361
+ jWarn("Failed to parse invalid string", e);
362
+ return false;
363
+ }
364
+ return false;
365
+ };
366
+ /**
367
+ * 判断是否合法JSON数组字符串
368
+ * @param str
369
+ */
370
+ const isArrStr = (str) => {
371
+ if (isDef(str)) try {
372
+ return Array.isArray(JSON.parse(str));
373
+ } catch (e) {
374
+ jWarn("Failed to parse invalid string", e);
375
+ return false;
376
+ }
377
+ return false;
378
+ };
379
+ /**
380
+ * 判断是否有效的key
381
+ * @param {string | number | symbol} key 键名
382
+ * @param {object} object 对象
383
+ * @example
384
+ *
385
+ * ```ts
386
+ * const obj = { a: 1, b: 2 }
387
+ * isValidKey('a', obj) // true
388
+ * ```
389
+ * @returns {boolean} boolean
390
+ */
391
+ const isValidKey = (key, object) => key in object;
392
+ /**
393
+ * 是否是空值/空数组/空对象 null undefined '' [] {} Set Map
394
+ * @param val
395
+ */
396
+ const isEmpty = (val) => {
397
+ return !isDef(val) || val === "" || isArray(val) && val.length === 0 || isObject(val) && Object.keys(val).length === 0 || isMap(val) && val.size === 0 || isSet(val) && val.size === 0;
398
+ };
399
+
400
+ //#endregion
401
+ //#region src/validate/form.ts
402
+ const cnPhoneRE = /^1[3-9]\d{9}$/;
403
+ const cnTelRE = /^0\d{2,3}-?\d{7,8}$/;
404
+ const cnTelRE2 = /^\(0\d{2,3}\)\d{7,8}$/;
405
+ const cnUSCIRE = /^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/;
406
+ const cnIdCard15Re = /^[1-9]\d{7}(?:0[1-9]|10|11|12)(?:[0-2][1-9]|10|20|30|31)\d{2}[\dX]$/i;
407
+ const cnIdCard18Re = /^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|1[0-2])(?:[0-2][1-9]|10|20|30|31)\d{3}[\dX]$/i;
408
+ const emailRE = /^[a-z\d](?:\w|-)+@[a-z\d]+\.[a-z]{2,5}$/i;
409
+ /**
410
+ * 是否是[统一社会信用代码](https://zh.wikipedia.org/zh-cn/%E7%BB%9F%E4%B8%80%E7%A4%BE%E4%BC%9A%E4%BF%A1%E7%94%A8%E4%BB%A3%E7%A0%81)
411
+ * @example
412
+ * ```ts
413
+ * isUSCI('91350100M000100Y43') // true
414
+ * ```
415
+ * @param {UnDef<string>} val - 待校验的值
416
+ * @returns {boolean} 是否是统一社会信用代码
417
+ */
418
+ const isUSCI = (val) => {
419
+ if (!isDef(val)) return false;
420
+ return cnUSCIRE.test(val);
421
+ };
422
+ /**
423
+ * 是否是中国手机号
424
+ * @param val
425
+ */
426
+ const isPhone = (val) => {
427
+ if (!isDef(val)) return false;
428
+ return cnPhoneRE.test(val.toString());
429
+ };
430
+ /**
431
+ * 是否是中国座机号
432
+ * @param val
433
+ */
434
+ const isTel = (val) => {
435
+ if (!isDef(val)) return false;
436
+ const valStr = val.toString().replaceAll(" ", "");
437
+ return cnTelRE.test(valStr) || cnTelRE2.test(valStr);
438
+ };
439
+ /**
440
+ * 是否是邮箱
441
+ * @param val
442
+ */
443
+ const isEmail = (val) => {
444
+ if (!isDef(val)) return false;
445
+ return emailRE.test(val);
446
+ };
447
+ /**
448
+ * 是否是身份证
449
+ * @param {string|null|undefined} val 身份证号
450
+ * @param {boolean} compatible 兼容15位
451
+ */
452
+ const isIdCard = (val, compatible = true) => {
453
+ if (!isDef(val)) return false;
454
+ return cnIdCard18Re.test(val) || (compatible ? cnIdCard15Re.test(val) : false);
455
+ };
456
+ /**
457
+ * 是否是数字或字符
458
+ * @param val
459
+ */
460
+ const isNumeric = (val) => {
461
+ if (isDef(val)) return typeof val === "number" || /^\d+(?:\.\d+)?$/.test(val);
462
+ return false;
463
+ };
464
+ /**
465
+ * 是否是需要的图片格式
466
+ * @param ext
467
+ */
468
+ const checkImg = (ext) => {
469
+ return /\.(?:jpg|jpeg|png|gif)$/.test((ext || "").toLowerCase());
470
+ };
471
+ /**
472
+ * 包含非ASCII字符
473
+ * @param text
474
+ */
475
+ const nonASCII = (text) => /[^\x20-\x7E]/.test(text);
476
+ /**
477
+ * 校验文件类型
478
+ * 1. XXX/YYY 2. .xxx 3. XXX/* 4.*
479
+ * @param {File} file 需要校验的文件
480
+ * @param {string} type 文件类型, 与input.accept一致
481
+ */
482
+ const isValidFileType = (file, type) => {
483
+ if (type === "*") return true;
484
+ const fileType = file.type;
485
+ const fileName = file.name;
486
+ /**
487
+ * 校验
488
+ * @param item
489
+ */
490
+ function validate(item) {
491
+ if (item.startsWith(".")) return fileName.endsWith(item);
492
+ if (item.endsWith("/*")) return fileType.startsWith(item.replace("/*", ""));
493
+ if (item.includes("/")) return fileType === item;
494
+ return false;
495
+ }
496
+ const typeArrStr = type.split(",").map((item) => item.trim());
497
+ if (typeArrStr.length > 1) return typeArrStr.some(validate);
498
+ return validate(type);
499
+ };
500
+
501
+ //#endregion
502
+ //#region src/validate/request.ts
503
+ /**
504
+ * 校验接口返回code
505
+ * @param res
506
+ * @param code
507
+ */
508
+ const isValidResCode = (res, code = 200) => res.code === code;
509
+ /**
510
+ * 校验接口返回内容
511
+ * @param res
512
+ * @param code
513
+ */
514
+ const isValidRes = (res, code) => isValidResCode(res, code) && isDef(res.data);
515
+ /**
516
+ * 校验接口数组返回内容
517
+ * @param res
518
+ * @param code
519
+ */
520
+ const isValidArrRes = (res, code) => isValidResCode(res, code) && Array.isArray(res.data);
521
+ /**
522
+ * 校验接口分页返回内容
523
+ * @param res
524
+ * @param code
525
+ */
526
+ const isValidPageRes = (res, code) => isValidResCode(res, code) && isDef(res.data) && Array.isArray(res.data.list || res.data.records);
527
+
528
+ //#endregion
529
+ //#region src/desensitize.ts
530
+ /**
531
+ * 隐藏中间部分
532
+ * @param {string} str 需要隐藏的字符串
533
+ * @returns {string} 隐藏后的字符串
534
+ * @example
535
+ * ```ts
536
+ * hideMiddle('A') // '*'
537
+ * hideMiddle('AB') // '**'
538
+ * hideMiddle('ABC') // 'A*C'
539
+ * hideMiddle('ABCD') // 'A**D'
540
+ * hideMiddle('ABCDE') // 'A***E'
541
+ * ```
542
+ */
543
+ const hideMiddle = (str) => {
544
+ if (!isDef(str)) return "";
545
+ const len = str.length;
546
+ if (len <= 2) return "*".repeat(len);
547
+ return str[0] + "*".repeat(len - 2) + str[len - 1];
548
+ };
549
+ /**
550
+ * 隐藏首尾仅保留中间3分之1
551
+ * @param {string} str 需要隐藏的字符串
552
+ * @returns {string} 隐藏后的字符串
553
+ * @example
554
+ * ```ts
555
+ * hideThird('A') // '*'
556
+ * hideThird('AB') // '**'
557
+ * hideThird('ABC') // 'A*C'
558
+ * hideThird('ABCD') // 'A**D'
559
+ * hideThird('ABCDE') // 'AB*DE'
560
+ * ```
561
+ */
562
+ const hideThird = (str) => {
563
+ if (!isDef(str)) return "";
564
+ const len = str.length;
565
+ if (len <= 2) return "*".repeat(len);
566
+ const partLen = Math.round(len / 3);
567
+ return str.slice(0, partLen) + "*".repeat(len - 2 * partLen) + str.slice(len - partLen);
568
+ };
569
+ /**
570
+ * 隐藏首部
571
+ * @param {string} str 需要隐藏的字符串
572
+ * @param {number} count 隐藏的字符数
573
+ * @returns {string} 隐藏后的字符串
574
+ * @example
575
+ * ```ts
576
+ * hideHead('A') // '*'
577
+ * hideHead('AB') // '*B'
578
+ * hideHead('ABC') // '**C'
579
+ * hideHead('ABC', 1) // '*BC'
580
+ * ```
581
+ */
582
+ const hideHead = (str, count) => {
583
+ if (!isDef(str)) return "";
584
+ const len = str.length;
585
+ if (len <= 1) return "*".repeat(len);
586
+ const cnt = count !== void 0 ? Math.max(Math.min(len, count), 0) : len - 1;
587
+ return "*".repeat(cnt) + str.substring(cnt);
588
+ };
589
+ /**
590
+ * 隐藏尾部
591
+ * @param {string} str 需要隐藏的字符串
592
+ * @param {number} count 隐藏的字符数
593
+ * @returns {string} 隐藏后的字符串
594
+ * @example
595
+ * ```ts
596
+ * hideTail('A') // '*'
597
+ * hideTail('AB') // 'A*'
598
+ * hideTail('ABC') // 'A**'
599
+ * hideTail('ABC', 1) // 'AB*'
600
+ * ```
601
+ */
602
+ const hideTail = (str, count) => {
603
+ if (!isDef(str)) return "";
604
+ const len = str.length;
605
+ if (len <= 1) return "*".repeat(len);
606
+ const cnt = count !== void 0 ? Math.max(Math.min(len, count), 0) : len - 1;
607
+ return str.substring(0, len - cnt) + "*".repeat(cnt);
608
+ };
609
+ /**
610
+ * 隐藏手机号/座机号
611
+ * @param {string} phone 手机号/座机号
612
+ * @returns {string} 隐藏后的手机号/座机号
613
+ * @example
614
+ * ```ts
615
+ * hidePhone('13800138000') // '138****8000'
616
+ * hidePhone('010-12345678') // '010****5678'
617
+ * hidePhone('(010)12345678') // '(010)****5678'
618
+ * ```
619
+ */
620
+ const hidePhone = (phone) => {
621
+ if (phone) {
622
+ const phoneStr = phone.replaceAll(" ", "");
623
+ if (isPhone(phoneStr)) return phoneStr.replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2");
624
+ if (cnTelRE.test(phoneStr)) return phoneStr.replace(/^(0\d{2,3})-?\d{3,4}(\d{4})$/, "$1****$2");
625
+ if (cnTelRE2.test(phoneStr)) return phoneStr.replace(/^\((0\d{2,3})\)\d{3,4}(\d{4})$/, "$1****$2");
626
+ return phone;
627
+ }
628
+ return "";
629
+ };
630
+ /**
631
+ * 隐藏身份证号
632
+ * @param {string} cardNo 身份证号
633
+ * @param {boolean} strong 是否强化隐藏
634
+ * @returns {string} 隐藏后的身份证号
635
+ * @example
636
+ * ```ts
637
+ * hideCardNo('33072319641008384x') // '3****************x'
638
+ * hideCardNo('33072319641008384x', false) // '3***23196410*****x'
639
+ * ```
640
+ */
641
+ const hideCardNo = (cardNo, strong = true) => {
642
+ if (cardNo) {
643
+ if (isIdCard(cardNo, false)) return strong ? hideMiddle(cardNo) : cardNo.replace(/^(.)\d{3}(.{8})\d{5}(.)$/, "$1***$2*****$3");
644
+ return cardNo;
645
+ }
646
+ return "";
647
+ };
648
+ /**
649
+ * 隐藏姓氏
650
+ * @param {string} name 姓名
651
+ * @returns {string} 隐藏后的姓名
652
+ * @example
653
+ * ```ts
654
+ * hideSurname('张三') // '*三'
655
+ * hideSurname('司马懿') // '*马懿'
656
+ * ```
657
+ */
658
+ const hideSurname = (name) => {
659
+ return hideHead(name, 1);
660
+ };
661
+ /**
662
+ * 隐藏名字
663
+ * @param {string} name 姓名
664
+ * @returns {string} 隐藏后的姓名
665
+ * @example
666
+ * ```ts
667
+ * hideFirstName('张三') // '张*'
668
+ * hideFirstName('司马懿') // '司马*'
669
+ * ```
670
+ */
671
+ const hideFirstName = (name) => {
672
+ return hideTail(name);
673
+ };
674
+ /**
675
+ * 隐藏邮箱
676
+ * @param {string} email 邮箱
677
+ * @returns {string} 隐藏后的邮箱
678
+ * @example
679
+ * ```ts
680
+ * hideEmail('example@hotmail.com') // 'exam***@hotmail.com'
681
+ * ```
682
+ */
683
+ const hideEmail = (email) => {
684
+ if (email) {
685
+ if (!email.includes("@")) return email;
686
+ const arr = email.split("@");
687
+ const name = arr[0];
688
+ const domain = arr[1];
689
+ if (!name || !domain) return email;
690
+ const len = name.length;
691
+ let newName;
692
+ if (len <= 3) newName = `${name}***`;
693
+ else newName = hideTail(name, 3);
694
+ return `${newName}@${domain}`;
695
+ }
696
+ return "";
697
+ };
698
+ /**
699
+ * 隐藏银行卡号
700
+ * @param {string} bankCard 银行卡号
701
+ * @returns {string} 隐藏后的银行卡号
702
+ * @example
703
+ * ```ts
704
+ * hideBankCard('6222021001123456789') // '622202********6789'
705
+ * ```
706
+ */
707
+ const hideBankCard = (bankCard) => {
708
+ if (bankCard) {
709
+ const len = bankCard.length;
710
+ if (len <= 10) return bankCard;
711
+ return bankCard.substring(0, 6) + "*".repeat(len - 10) + bankCard.substring(len - 4, len);
712
+ }
713
+ return "";
714
+ };
715
+
716
+ //#endregion
717
+ //#region src/format/baseAttachUrl.ts
718
+ /**
719
+ * 文件基本路径
720
+ */
721
+ let __BASE_ATTACH_URL__;
722
+ /**
723
+ * 设置文件基本路径
724
+ * @param baseUrl
725
+ */
726
+ function setBaseAttachUrl(baseUrl) {
727
+ __BASE_ATTACH_URL__ = baseUrl;
728
+ }
729
+ /**
730
+ * 获取文件基本路径
731
+ */
732
+ function getBaseAttachUrl() {
733
+ return __BASE_ATTACH_URL__;
734
+ }
735
+
736
+ //#endregion
737
+ //#region src/format/core.ts
738
+ /**
739
+ * JSON 字符串转对象
740
+ * @template T - 解析数据类型
741
+ * @param {string} str - JSON字符串
742
+ * @param {(this: any, key: string, value: any) => any} reviverFn - 解析函数
743
+ * @returns {Nullable<T>} 解析后的对象
744
+ * @deprecated 请使用 `parseJSON`
745
+ */
746
+ function parseToJSON(str, reviverFn) {
747
+ return isJSONStr(str) ? JSON.parse(str, reviverFn) : null;
748
+ }
749
+ /**
750
+ * JSON 字符串转对象
751
+ * @alias parseToJSON
752
+ * @template T - 解析数据类型
753
+ * @param {string} str - JSON字符串
754
+ * @param {(this: any, key: string, value: any) => any} reviverFn - 解析函数
755
+ * @returns {Nullable<T>} 解析后的对象
756
+ */
757
+ function parseJSON(str, reviverFn = reviver) {
758
+ return isJSONStr(str) ? JSON.parse(str, reviverFn) : null;
759
+ }
760
+ /**
761
+ * 对象转 JSON 字符串
762
+ * @param {any} value - 对象
763
+ * @param {(this: any, key: string, value: any) => any} replacerFn - 替换函数
764
+ * @returns {string} JSON 字符串
765
+ */
766
+ function stringifyJSON(value, replacerFn = replacer) {
767
+ return JSON.stringify(value, replacerFn);
768
+ }
769
+ /**
770
+ * JSON.stringify() second param
771
+ * @param _key
772
+ * @param value
773
+ */
774
+ function replacer(_key, value) {
775
+ if (isMap(value)) return {
776
+ dataType: "Map",
777
+ value: [...value]
778
+ };
779
+ if (isSet(value)) return {
780
+ dataType: "Set",
781
+ value: [...value]
782
+ };
783
+ if (isDate(value)) return {
784
+ dataType: "Date",
785
+ value: value.toISOString()
786
+ };
787
+ return value;
788
+ }
789
+ /**
790
+ * JSON.parse() second param
791
+ * @param _key
792
+ * @param value
793
+ */
794
+ function reviver(_key, value) {
795
+ if (typeof value === "object" && value !== null) {
796
+ if (value.dataType === "Map") return new Map(value.value);
797
+ if (value.dataType === "Set") return new Set(value.value);
798
+ if (value.dataType === "Date") return new Date(value.value);
799
+ }
800
+ return value;
801
+ }
802
+ /**
803
+ * 过滤obj中仅包括keys的部分
804
+ * @param {Record<string,any>} obj 对象
805
+ * @param {string[]} keys 属性
806
+ * @param {boolean} reverse 为true时过滤取反
807
+ */
808
+ const filterObj = (obj, keys, reverse = false) => Object.fromEntries(Object.entries(obj).filter(([key]) => reverse ? !keys.includes(key) : keys.includes(key)));
809
+
810
+ //#endregion
811
+ //#region src/format/currency.ts
812
+ /**
813
+ * 千位分隔符
814
+ * @param num
815
+ */
816
+ const toThousands = (num) => {
817
+ const arr = num.toString().split(".");
818
+ const integer = arr[0]?.replace(/(\d)(?=(?:\d{3})+$)/g, "$1,");
819
+ if (arr.length > 1) return `${integer}.${arr[1]}`;
820
+ return integer;
821
+ };
822
+ /**
823
+ * 货币格式化
824
+ * @param {number | string | bigint} currency - 金额
825
+ * @param {number} [maximumFractionDigits] - 小数位数
826
+ */
827
+ const currencyFmt = (currency, maximumFractionDigits = 2) => {
828
+ if (!isDef(currency)) return "";
829
+ const num = Number(currency);
830
+ if (Number.isNaN(num)) return "";
831
+ return new Intl.NumberFormat("zh-CN", {
832
+ style: "currency",
833
+ currency: "CNY",
834
+ maximumFractionDigits
835
+ }).format(num);
836
+ };
837
+
838
+ //#endregion
839
+ //#region src/format/dayjs.ts
840
+ dayjs.extend(localeData).locale("zh-cn", zhCN);
841
+ dayjs.extend(duration);
842
+ dayjs.extend(relativeTime);
843
+ dayjs.extend(customParseFormat);
844
+ dayjs.extend(objectSupport);
845
+ /**
846
+ * 格式化时间
847
+ * @param {import('dayjs').ConfigType} date 时间
848
+ * @param {string} format 格式
849
+ * @returns {string} 格式化后的时间
850
+ */
851
+ const dateFmt = (date, format = "YYYY-MM-DD HH:mm:ss") => {
852
+ const dd = dayjs(date);
853
+ let fmt = format;
854
+ if (!isDef(date) || !dd.isValid()) return "";
855
+ if (format === "date") fmt = "YYYY-MM-DD";
856
+ if (format === "datetime") fmt = "YYYY-MM-DD HH:mm:ss";
857
+ return dd.format(fmt);
858
+ };
859
+ /**
860
+ * 格式化时间间隔
861
+ * @param {import('dayjs').ConfigType} start 开始时间
862
+ * @param {import('dayjs').ConfigType} end 结束时间
863
+ */
864
+ const dateDuration = (start, end) => {
865
+ const startDay = dayjs(start);
866
+ const endDay = dayjs(end);
867
+ if (!isDef(start) || !startDay.isValid() || !isDef(end) || !endDay.isValid()) return "";
868
+ const dd = dayjs.duration(endDay.diff(startDay));
869
+ const year = dd.years();
870
+ const month = dd.months();
871
+ const day = dd.days();
872
+ const format = [
873
+ year === 0 ? "" : "Y年",
874
+ month === 0 ? "" : "M个月",
875
+ day === 0 ? "" : `${day < 10 && (year !== 0 || month !== 0) ? "零" : ""}D天`
876
+ ].join("");
877
+ return dd.format(format);
878
+ };
879
+
880
+ //#endregion
881
+ //#region src/format/resource.ts
882
+ /**
883
+ * 获取文件完整地址
884
+ * @param url
885
+ */
886
+ const resUrl = (url) => {
887
+ if (!isDef(url)) return void 0;
888
+ if (/^https?:\/\//.test(url) || url.startsWith("blob:") || url.startsWith("data:")) return url;
889
+ const baseUrl = getBaseAttachUrl();
890
+ if (!isDef(baseUrl)) throw new Error("请先使用 setBaseAttachUrl 设置文件基本路径");
891
+ return `${baseUrl}/${url}`.replace(/([^:]\/)\/+/g, "$1");
892
+ };
893
+ /**
894
+ * 统一文件字段内容
895
+ * @param resource
896
+ */
897
+ const recoverFile = (resource) => {
898
+ const { uri, url, ...item } = resource;
899
+ const validUrl = uri || url;
900
+ return {
901
+ ...item,
902
+ url: resUrl(validUrl),
903
+ content: resUrl(validUrl),
904
+ uri: validUrl || "",
905
+ status: "done",
906
+ percent: 100
907
+ };
908
+ };
909
+ /**
910
+ * 附件格式
911
+ * @param data
912
+ */
913
+ const attachFmt = (data) => {
914
+ let attachList = [];
915
+ const prototype = Object.prototype.toString.call(data);
916
+ if (!data) attachList = [];
917
+ else if (Array.isArray(data)) attachList = data.filter(Boolean);
918
+ else if (typeof data === "string") if (isArrStr(data)) attachList = (parseJSON(data) || []).filter(Boolean);
919
+ else {
920
+ const jsonData = parseJSON(data);
921
+ if (Object.prototype.toString.call(jsonData) === "[object Object]") attachList = [jsonData].filter(Boolean);
922
+ else attachList = [{
923
+ id: data,
924
+ name: "name",
925
+ uri: data,
926
+ group: "default"
927
+ }];
928
+ }
929
+ else if (prototype === "[object Object]") attachList = [data];
930
+ return attachList.map(recoverFile);
931
+ };
932
+
933
+ //#endregion
934
+ //#region src/format/sort.ts
935
+ /**
936
+ * 判断是否为数字数组
937
+ * @param {UnDef<string>[] | UnDef<number>[]} arr - 数字或字符串数组
938
+ * @returns {boolean} - 是否为数字数组
939
+ */
940
+ const isNumberArray = (arr) => arr.every((item) => typeof item === "number");
941
+ /**
942
+ * 判断是否为字符串数组
943
+ * @param {UnDef<string>[] | UnDef<number>[]} arr - 数字或字符串数组
944
+ * @returns {boolean} - 是否为字符串数组
945
+ */
946
+ const isStringArray = (arr) => arr.every((item) => typeof item === "string");
947
+ /**
948
+ * 自然排序
949
+ * @param {UnDef<string>[] | UnDef<number>[]} arr - 数字或字符串数组
950
+ * @returns {UnDef<string>[] | UnDef<number>[]} - 排序后的数组
951
+ */
952
+ const naturalSort = (arr) => {
953
+ if (isNumberArray(arr)) return [...arr].sort((a, b) => a - b);
954
+ if (isStringArray(arr)) return [...arr].sort((a, b) => a.localeCompare(b, "zh-CN")).sort((a, b) => a.length - b.length);
955
+ const newArr = [...arr].sort();
956
+ const validArr = newArr.filter((a) => isDef(a));
957
+ const invalidArr = newArr.filter((a) => !isDef(a));
958
+ return naturalSort(validArr).concat(invalidArr);
959
+ };
960
+ /**
961
+ * 根据 key 自然排序
962
+ * @param arr - 数组
963
+ * @param {string} key - 键名
964
+ * @returns {any[]} - 排序后的数组
965
+ */
966
+ const naturalSortBy = (arr, key) => {
967
+ return [...arr].sort((a, b) => naturalSort([a[key], b[key]])[0] === a[key] ? -1 : 1);
968
+ };
969
+
970
+ //#endregion
971
+ //#region src/dom.ts
972
+ /**
973
+ * 判断是否是浏览器环境
974
+ */
975
+ const inBrowser = typeof window !== "undefined";
976
+ /**
977
+ * 等待一段时间
978
+ * @param time
979
+ */
980
+ const waitTime = (time = 100) => {
981
+ return new Promise((resolve) => {
982
+ setTimeout(() => {
983
+ resolve(true);
984
+ }, time);
985
+ });
986
+ };
987
+ /**
988
+ * 防止 iOS 因输入框字体小于16px 而在 focus 时页面缩放变大需要手动回正的情况。
989
+ * iOS 10+ 允许 pinch-zoom, 即使 viewport 是不可伸缩的。
990
+ * 只对这些浏览器添加额外规则
991
+ */
992
+ const fixiOSInputAutoZoomIn = () => {
993
+ if (CSS.supports("(font:-apple-system-body) and (-webkit-touch-callout:none) and (-webkit-tap-highlight-color:hotpink)")) {
994
+ const viewport = document.querySelector("meta[name=\"viewport\"]");
995
+ if (viewport !== null) viewport.content = `${viewport.content}, user-scalable=no`;
996
+ }
997
+ };
998
+ /**
999
+ * 用户代理
1000
+ */
1001
+ const userAgent = inBrowser ? window.navigator.userAgent : "";
1002
+ /**
1003
+ * 获取宿主环境信息
1004
+ * @returns {HostEnv} 宿主环境信息
1005
+ * @example
1006
+ * const hostEnv = getHostEnv()
1007
+ * hostEnv.zlb // 浙里办
1008
+ * hostEnv.zyd // 专有钉
1009
+ * hostEnv.zzd // 浙政钉
1010
+ * hostEnv.dd // 钉钉/专有钉/浙政钉
1011
+ * hostEnv.wx // 微信
1012
+ * hostEnv.zfb // 支付宝
1013
+ * hostEnv.mini // 小程序
1014
+ */
1015
+ const getHostEnv = () => {
1016
+ const ua = userAgent.toLowerCase();
1017
+ return {
1018
+ zlb: ua.includes("@zlb"),
1019
+ zyd: ua.includes("saas"),
1020
+ zzd: ua.includes("zhejiang") || ua.includes("tauruszjd") || ua.includes("dingtalk-win"),
1021
+ dd: ua.includes("taurusapp"),
1022
+ wx: ua.includes("micromessenger"),
1023
+ zfb: ua.includes("alipay"),
1024
+ mini: ua.includes("miniprogram")
1025
+ };
1026
+ };
1027
+ /**
1028
+ * 异或值
1029
+ */
1030
+ const XOR_VAL = 1111;
1031
+ /**
1032
+ * 获取某个时间戳的异或值的绝对值,在某个时间段内获取的值是唯一的
1033
+ * @param time
1034
+ */
1035
+ const getCodeOfTime = (time) => Math.abs(new Date(time).getTime() ^ XOR_VAL);
1036
+ /**
1037
+ * 获取当小时的时间戳的异或值的绝对值, 当小时内获取的值是唯一的
1038
+ */
1039
+ const getCodeOfHour = () => getCodeOfTime(`${dateFmt(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH")}:00:00`);
1040
+ /**
1041
+ * 获取当天的时间戳的异或值的绝对值, 当天内获取的值是唯一的
1042
+ */
1043
+ const getCodeOfDate = () => getCodeOfTime(dateFmt(/* @__PURE__ */ new Date(), "date"));
1044
+ /**
1045
+ * 获取当月的时间戳的异或值的绝对值, 当月内获取的值是唯一的
1046
+ */
1047
+ const getCodeOfMonth = () => getCodeOfTime(dateFmt(dayjs().endOf("month"), "date"));
1048
+
1049
+ //#endregion
1050
+ //#region src/mock.ts
1051
+ const getMockData = (data, code = 200, message = "success") => {
1052
+ return {
1053
+ data,
1054
+ code,
1055
+ message
1056
+ };
1057
+ };
1058
+ const getMockDataList = (data, total, code = 200, message = "success", options = {}) => {
1059
+ return {
1060
+ data: {
1061
+ records: data,
1062
+ list: data,
1063
+ total,
1064
+ ...options
1065
+ },
1066
+ code,
1067
+ message
1068
+ };
1069
+ };
1070
+
1071
+ //#endregion
1072
+ export { attachFmt, camel2Kebab, camel2Pascal, camel2Snake, camel2Title, checkImg, cnIdCard15Re, cnIdCard18Re, cnPhoneRE, cnTelRE, cnTelRE2, cnUSCIRE, currencyFmt, dateDuration, dateFmt, dayjs, emailRE, filterObj, fixiOSInputAutoZoomIn, getBaseAttachUrl, getCodeOfDate, getCodeOfHour, getCodeOfMonth, getHostEnv, getMockData, getMockDataList, getVariableType, hideBankCard, hideCardNo, hideEmail, hideFirstName, hideHead, hideMiddle, hidePhone, hideSurname, hideTail, hideThird, inBrowser, isArrStr, isArray, isBigInt, isBlob, isBoolean, isDate, isDef, isEmail, isEmpty, isError, isFile, isFunction, isIdCard, isJSONStr, isMap, isNull, isNumber, isNumeric, isObject, isPhone, isPromise, isRegExp, isSet, isString, isSymbol, isTel, isURL, isUSCI, isUndefined, isValidArrRes, isValidFileType, isValidKey, isValidPageRes, isValidRes, isValidResCode, isWeakMap, isWeakSet, jClear, jDebug, jError, jGroup, jGroupC, jGroupE, jInfo, jLog, jLogDedupe, jLogOnce, jTime, jTimeEnd, jWarn, kebab2Camel, kebab2Pascal, kebab2Snake, kebab2Title, naturalSort, naturalSortBy, nonASCII, parseJSON, parseToJSON, pascal2Camel, pascal2Kebab, pascal2Snake, pascal2Title, recoverFile, replacer, resUrl, reviver, setBaseAttachUrl, snake2Camel, snake2Kebab, snake2Pascal, snake2Title, stringifyJSON, title2Camel, title2Kebab, title2Pascal, title2Snake, toThousands, userAgent, waitTime };