jinbi-utils 1.0.19 → 1.0.21
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/.babelrc +19 -0
- package/.cz-config.js +55 -0
- package/.dockerignore +3 -0
- package/.editorconfig +12 -0
- package/.eslintignore +8 -0
- package/.eslintrc.js +54 -0
- package/.versionrc.json +9 -0
- package/CHUNK_OPTIMIZER_USAGE.md +132 -0
- package/Dockerfile +3 -0
- package/QUICK_RELEASE.md +85 -0
- package/RELEASE_GUIDE.md +243 -0
- package/api-extractor.json +15 -0
- package/commitlint.config.js +3 -0
- package/jest.config.js +15 -0
- package/package.json +8 -35
- package/rollup.config.chunk-optimizer.js +32 -0
- package/rollup.config.js +73 -0
- package/src/array/index.ts +85 -0
- package/src/build/chunk-optimizer/ARCHITECTURE.md +347 -0
- package/src/build/chunk-optimizer/QUICK_START.md +370 -0
- package/src/build/chunk-optimizer/README.md +240 -0
- package/src/build/chunk-optimizer/core/chunk-generator.ts +166 -0
- package/src/build/chunk-optimizer/core/classifier.ts +148 -0
- package/src/build/chunk-optimizer/core/dependency-reader.ts +138 -0
- package/src/build/chunk-optimizer/examples/basic-usage.ts +234 -0
- package/src/build/chunk-optimizer/index.ts +166 -0
- package/src/build/chunk-optimizer/rules/common-rules.ts +131 -0
- package/src/build/chunk-optimizer/rules/framework-rules.ts +93 -0
- package/src/build/chunk-optimizer/rules/index.ts +27 -0
- package/src/build/chunk-optimizer/test.ts +94 -0
- package/src/build/chunk-optimizer/types.ts +128 -0
- package/src/color/index.ts +58 -0
- package/src/common/index.ts +353 -0
- package/src/constant/common.constant.ts +13 -0
- package/src/date/index.ts +143 -0
- package/src/dom/index.ts +198 -0
- package/src/file/index.ts +319 -0
- package/src/http/apiBuilder/README.md +648 -0
- package/src/http/apiBuilder/api-builder.ts +502 -0
- package/src/http/apiBuilder/example.ts +243 -0
- package/src/http/apiBuilder/index.ts +1 -0
- package/src/http/apiBuilder//345/277/253/351/200/237/345/217/202/350/200/203.md +199 -0
- package/src/http/http.ts +79 -0
- package/src/http/httpEnums.ts +61 -0
- package/src/iam/index.ts +46 -0
- package/src/index.ts +20 -0
- package/src/middleware/requestLogger.middware.ts +371 -0
- package/src/middleware/requestLoggerUnified.ts +371 -0
- package/src/number/index.ts +362 -0
- package/src/object/index.ts +54 -0
- package/src/print/index.ts +102 -0
- package/src/string/index.ts +189 -0
- package/src/utils/curl.ts +108 -0
- package/src/validate/index.ts +100 -0
- package/src/websocket/emitter.ts +39 -0
- package/src/websocket/index.ts +6 -0
- package/src/websocket/manager.ts +151 -0
- package/src/websocket/pinia-store.ts +91 -0
- package/src/websocket/service.ts +34 -0
- package/src/websocket/types.ts +45 -0
- package/test/common/index.test.ts +19 -0
- package/test/date/index.test.ts +107 -0
- package/test/file/index.test.ts +104 -0
- package/test/number/index.test.ts +108 -0
- package/test/object/index.test.ts +20 -0
- package/test/string/index.test.ts +82 -0
- package/tsconfig.json +39 -0
- package/typedoc.json +12 -0
- package/types/file/index.d.ts +7 -0
- package/types/index.d.ts +1 -0
- package/types/websocket/emitter.d.ts +16 -0
- package/types/websocket/index.d.ts +6 -0
- package/types/websocket/manager.d.ts +36 -0
- package/types/websocket/pinia-store.d.ts +25 -0
- package/types/websocket/service.d.ts +13 -0
- package/types/websocket/types.d.ts +34 -0
- package/dist/chunk-optimizer.cjs +0 -703
- package/dist/index.esm.js +0 -2791
- package/dist/index.esm.min.js +0 -15
- package/dist/index.umd.js +0 -2899
- package/dist/index.umd.min.js +0 -16
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通用函数
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
* @module Common
|
|
5
|
+
* @preferred
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {fromTypeMap, IFromType} from "@/constant/common.constant";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 延迟函数(Promise 版本)
|
|
12
|
+
* @param ms - 延迟毫秒数
|
|
13
|
+
* @returns Promise
|
|
14
|
+
*/
|
|
15
|
+
export function delay(ms: number): Promise<void> {
|
|
16
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 检测设备是否为移动设备
|
|
21
|
+
* @returns 是否为移动设备
|
|
22
|
+
*/
|
|
23
|
+
export function deviceDetection(): boolean {
|
|
24
|
+
if (typeof window === 'undefined') return false;
|
|
25
|
+
|
|
26
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
27
|
+
const isMidp = /midp/i.test(userAgent);
|
|
28
|
+
const isUcweb = /ucweb/i.test(userAgent);
|
|
29
|
+
const isAndroid = /android/i.test(userAgent);
|
|
30
|
+
const isIos = /iphone os/i.test(userAgent);
|
|
31
|
+
const isWinCe = /windows ce/i.test(userAgent);
|
|
32
|
+
const isWinMobile = /windows mobile/i.test(userAgent);
|
|
33
|
+
const isRv = /rv:1.2.3.4/i.test(userAgent);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
isMidp || isUcweb || isAndroid || isIos || isWinCe || isWinMobile || isRv
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 判断val 是否是空值
|
|
42
|
+
#### 使用说明
|
|
43
|
+
```
|
|
44
|
+
isEmpty('') 返回 true
|
|
45
|
+
isEmpty(null) 返回 true
|
|
46
|
+
isEmpty(undefined) 返回 true
|
|
47
|
+
isEmpty(12323) 返回 false
|
|
48
|
+
```
|
|
49
|
+
*/
|
|
50
|
+
export function isEmpty(val: any): boolean {
|
|
51
|
+
return val === null || val === '' || val === undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// from jinbizhihui
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
// 获取环境变量
|
|
58
|
+
// export const getEnvValue = (type: string) => {
|
|
59
|
+
// return import.meta.env[type]
|
|
60
|
+
// }
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
export const clearLoginData = () => {
|
|
65
|
+
// 这里做指定的 key 清除 是为了 避免 清除掉 jsapiticket 等一些需要有的字段 防止多次请求
|
|
66
|
+
localStorage.removeItem('wecom_userinfo')
|
|
67
|
+
localStorage.removeItem('wecom_token')
|
|
68
|
+
localStorage.removeItem('projectActive')
|
|
69
|
+
localStorage.removeItem('loginTime')
|
|
70
|
+
localStorage.removeItem('projectInfo')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 获取url中参数
|
|
74
|
+
export const getQueryString = (url: string, queryKey: string) => {
|
|
75
|
+
const reg = new RegExp(`&{1}${queryKey}\\=[a-zA-Z0-9_-]+`, 'g')
|
|
76
|
+
const matchResult = url.replace(/\?/g, '&').match(reg)![0]
|
|
77
|
+
return matchResult.substr(matchResult.indexOf('=') + 1)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const getQueryVariable = (variable) => {
|
|
81
|
+
var query = window.location.search.substring(1)
|
|
82
|
+
var vars = query.split('&')
|
|
83
|
+
for (var i = 0; i < vars.length; i++) {
|
|
84
|
+
var pair = vars[i].split('=')
|
|
85
|
+
if (pair[0] === variable) { return pair[1] }
|
|
86
|
+
}
|
|
87
|
+
return (false)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const getWecomToken = () => {
|
|
91
|
+
return localStorage.getItem('wecom_token')
|
|
92
|
+
}
|
|
93
|
+
// 判断设备类型
|
|
94
|
+
export const getDeviceType = (): IFromType => {
|
|
95
|
+
const ua = navigator.userAgent.toLowerCase();
|
|
96
|
+
const isWxWork = /wxwork/.test(ua); // 企业微信
|
|
97
|
+
const isWeixin = /micromessenger/.test(ua) && !isWxWork; // 微信,排除企业微信
|
|
98
|
+
|
|
99
|
+
// 使用屏幕宽度判断是否移动设备
|
|
100
|
+
const isMobileScreen = window.innerWidth <= 768;
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
isWxWork,
|
|
104
|
+
isWeixin,
|
|
105
|
+
isMobileScreen,
|
|
106
|
+
isMobileAny: isWxWork || isWeixin || isMobileScreen,
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const getFromType = (res: IFromType) => {
|
|
111
|
+
let result = 'wecom'
|
|
112
|
+
for( let key in res) {
|
|
113
|
+
const value = res[key]
|
|
114
|
+
if (value) {
|
|
115
|
+
result = fromTypeMap[key]
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
export function randomString(e) {
|
|
122
|
+
e = e || 32
|
|
123
|
+
let t = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
|
|
124
|
+
let a = t.length
|
|
125
|
+
let n = ''
|
|
126
|
+
for (let i = 0; i < e; i++) n += t.charAt(Math.floor(Math.random() * a))
|
|
127
|
+
return n
|
|
128
|
+
}
|
|
129
|
+
export const getCookie = (cookieName) => {
|
|
130
|
+
let cookie = ''
|
|
131
|
+
let cookieArr = document.cookie.split('; ')
|
|
132
|
+
for (let i = 0; i < cookieArr.length; i++) {
|
|
133
|
+
var valueArr = cookieArr[i].split('=')
|
|
134
|
+
if (valueArr[0] === cookieName) {
|
|
135
|
+
cookie = valueArr[1]
|
|
136
|
+
break
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return cookie
|
|
140
|
+
}
|
|
141
|
+
// || 'cc51882c781d8035b98335ccb44787b42dd9d91ba3b85a45ff3e867857a526bfbbddcdc27b8edce9a52882d1838e0b8e6ccf658ed105df9a898b1c977c588bd1'
|
|
142
|
+
export const buildUUID = () => {
|
|
143
|
+
const hexList: string[] = []
|
|
144
|
+
for (let i = 0; i <= 15; i++) {
|
|
145
|
+
hexList[i] = i.toString(16) as string
|
|
146
|
+
}
|
|
147
|
+
let uuid = ''
|
|
148
|
+
for (let i = 1; i <= 36; i++) {
|
|
149
|
+
if (i === 9 || i === 14 || i === 19 || i === 24) {
|
|
150
|
+
uuid += '-'
|
|
151
|
+
} else if (i === 15) {
|
|
152
|
+
uuid += 4
|
|
153
|
+
} else if (i === 20) {
|
|
154
|
+
uuid += hexList[(Math.random() * 4) | 8];
|
|
155
|
+
} else {
|
|
156
|
+
uuid += hexList[(Math.random() * 16) | 0];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return uuid.replace(/-/g, '')
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export const getTicket = () => {
|
|
163
|
+
return localStorage.getItem('jsapiTicket')
|
|
164
|
+
}
|
|
165
|
+
export const setTicket = (jsapiTicket) => {
|
|
166
|
+
return localStorage.setItem('jsapiTicket', jsapiTicket)
|
|
167
|
+
}
|
|
168
|
+
export const removeTicket = () => {
|
|
169
|
+
return localStorage.removeItem('jsapiTicket')
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
export const formateTimestamp = (timestamp: number | null, type = 'YYYY年MM月DD日 hh:mm:ss') => {
|
|
174
|
+
if (!timestamp) {
|
|
175
|
+
return '-'
|
|
176
|
+
}
|
|
177
|
+
const date = new Date(timestamp)
|
|
178
|
+
const year = date.getFullYear() + ''
|
|
179
|
+
const month = date.getMonth() + 1
|
|
180
|
+
const day = date.getDate()
|
|
181
|
+
const houre = date.getHours() > 9 ? date.getHours() : '0' + date.getHours()
|
|
182
|
+
const minute = date.getMinutes() > 9 ? date.getMinutes() : '0' + date.getMinutes()
|
|
183
|
+
const second = date.getSeconds() > 9 ? date.getSeconds() : '0' + date.getSeconds()
|
|
184
|
+
const result = type.replace('YYYY', year + '').replace('MM', month + '').replace('DD', day + '').replace('hh', houre + '').replace('mm', minute + '').replace('ss', second + '')
|
|
185
|
+
return result
|
|
186
|
+
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
export const convertBase64UrlToBlob = (urlData) => {
|
|
191
|
+
// console.log('urlData', urlData)
|
|
192
|
+
// 将以base64的图片url数据转换为Blob
|
|
193
|
+
var bytes = window.atob(urlData.split(',')[1]) // 去掉url的头,并转换为byte
|
|
194
|
+
// 处理异常,将ascii码小于0的转换为大于0
|
|
195
|
+
var ab = new ArrayBuffer(bytes.length)
|
|
196
|
+
var ia = new Uint8Array(ab)
|
|
197
|
+
for (var i = 0; i < bytes.length; i++) {
|
|
198
|
+
ia[i] = bytes.charCodeAt(i)
|
|
199
|
+
}
|
|
200
|
+
return new Blob([ab], { type: 'image/png' })
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
export const getEnvironment = () => {
|
|
205
|
+
const isMobile = window.navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i); // 是否手机端
|
|
206
|
+
const isWx = /micromessenger/i.test(navigator.userAgent); // 是否微信
|
|
207
|
+
const isComWx = /wxwork/i.test(navigator.userAgent); // 是否企业微信
|
|
208
|
+
|
|
209
|
+
if (isComWx && isMobile) { //手机端企业微信
|
|
210
|
+
return 'com-wx-mobile'
|
|
211
|
+
}
|
|
212
|
+
else if (isComWx && !isMobile) { //PC端企业微信
|
|
213
|
+
return 'com-wx-pc'
|
|
214
|
+
}
|
|
215
|
+
else if (isWx && isMobile) { // 手机端微信
|
|
216
|
+
return 'wx-mobile';
|
|
217
|
+
}
|
|
218
|
+
else if (isWx && !isMobile) { // PC端微信
|
|
219
|
+
return 'wx-pc';
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
return 'other';
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export const getIsComWx = () => {
|
|
227
|
+
const isComWx = /wxwork/i.test(navigator.userAgent); // 是否企业微信
|
|
228
|
+
return isComWx
|
|
229
|
+
}
|
|
230
|
+
export const getIsDevelopment = () => {
|
|
231
|
+
let result = false
|
|
232
|
+
if (location.origin === 'uat2-h5-wecom.hengdayun.com' || location.origin === '127.0.0.1:8081' || location.origin === 'localhost:8081') {
|
|
233
|
+
result = true
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return result
|
|
237
|
+
}
|
|
238
|
+
// 处理时间,企微不能通过dayjs以及moment处理时间
|
|
239
|
+
|
|
240
|
+
// 过滤数组重复数据
|
|
241
|
+
export const filterRepeat = (arr: any[], fieldName: string) => {
|
|
242
|
+
const set = new Set()
|
|
243
|
+
return arr.filter(item => {
|
|
244
|
+
const key = item[fieldName]
|
|
245
|
+
if (set.has(key)) {
|
|
246
|
+
return false
|
|
247
|
+
} else {
|
|
248
|
+
set.add(key)
|
|
249
|
+
return true
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// 将 base64 转换为 Blob
|
|
255
|
+
export const base64ToBlob = (base64) => {
|
|
256
|
+
var arr = base64.split(","),
|
|
257
|
+
mime = arr[0].match(/:(.\*?);/)[1],
|
|
258
|
+
bstr = atob(arr[1]),
|
|
259
|
+
n = bstr.length,
|
|
260
|
+
u8arr = new Uint8Array(n);
|
|
261
|
+
while (n--) {
|
|
262
|
+
u8arr[n] = bstr.charCodeAt(n);
|
|
263
|
+
}
|
|
264
|
+
return new Blob([u8arr], {
|
|
265
|
+
type: mime,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
// 将 base64 转换为 File
|
|
269
|
+
export const base64ToFile = (base64, fileName) => {
|
|
270
|
+
let arr = base64.split(",");
|
|
271
|
+
let mime = arr[0].match(/:(.*);/)[1];
|
|
272
|
+
|
|
273
|
+
let bstr = atob(arr[1]);
|
|
274
|
+
let n = bstr.length;
|
|
275
|
+
let u8arr = new Uint8Array(n);
|
|
276
|
+
|
|
277
|
+
while (n--) {
|
|
278
|
+
u8arr[n] = bstr.charCodeAt(n);
|
|
279
|
+
}
|
|
280
|
+
return new File([u8arr], fileName, { type: mime });
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// 处理时间,企微不能通过dayjs以及moment处理时间
|
|
284
|
+
export const isImage = (type: string) => {
|
|
285
|
+
return /^image\//i.test(type);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// 获取文件数据
|
|
289
|
+
export const getFileData = (optionName: string) => {
|
|
290
|
+
const [name = '', url = ''] = optionName.split(',')
|
|
291
|
+
return {
|
|
292
|
+
name: decodeURIComponent(name),
|
|
293
|
+
url: decodeURIComponent(url)
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// 验证是否为数字
|
|
297
|
+
export const validateTwoDecimal = (v: number | string) => {
|
|
298
|
+
if (!v || v === '') return true
|
|
299
|
+
return /^(\d+)(\.\d{1,2})?$/.test(v.toString())
|
|
300
|
+
}
|
|
301
|
+
// 生成uuid
|
|
302
|
+
export const uuid = (len, radix) => {
|
|
303
|
+
let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
|
|
304
|
+
let uuid: string[] = [], i: string | number;
|
|
305
|
+
radix = radix || chars.length;
|
|
306
|
+
|
|
307
|
+
if (len) {
|
|
308
|
+
// Compact form
|
|
309
|
+
for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];
|
|
310
|
+
} else {
|
|
311
|
+
// rfc4122, version 4 form
|
|
312
|
+
let r;
|
|
313
|
+
|
|
314
|
+
// rfc4122 requires these characters
|
|
315
|
+
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
|
|
316
|
+
uuid[14] = '4';
|
|
317
|
+
|
|
318
|
+
// Fill in random data. At i==19 set the high bits of clock sequence as
|
|
319
|
+
// per rfc4122, sec. 4.1.5
|
|
320
|
+
for (i = 0; i < 36; i++) {
|
|
321
|
+
if (!uuid[i]) {
|
|
322
|
+
r = 0 | Math.random() * 16;
|
|
323
|
+
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return uuid.join('');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export const getToken = (cacheType: string, key = 'token') => {
|
|
332
|
+
let token = ''
|
|
333
|
+
const cacheTypeToLower = cacheType.toLowerCase();
|
|
334
|
+
if (cacheTypeToLower === 'localstorage') {
|
|
335
|
+
token = localStorage.getItem(key) || '';
|
|
336
|
+
} else if (cacheTypeToLower === 'sessionstorage') {
|
|
337
|
+
token = sessionStorage.getItem(key) || '';
|
|
338
|
+
} else {
|
|
339
|
+
token = localStorage.getItem(key) || sessionStorage.getItem('token') || '';
|
|
340
|
+
}
|
|
341
|
+
return token;
|
|
342
|
+
}
|
|
343
|
+
export const removeToken = (cacheType: string, key = 'token') => {
|
|
344
|
+
const cacheTypeToLower = cacheType.toLowerCase();
|
|
345
|
+
if (cacheTypeToLower === 'localstorage') {
|
|
346
|
+
localStorage.removeItem(key);
|
|
347
|
+
} else if (cacheTypeToLower === 'sessionstorage') {
|
|
348
|
+
sessionStorage.removeItem(key);
|
|
349
|
+
} else {
|
|
350
|
+
localStorage.removeItem(key);
|
|
351
|
+
sessionStorage.removeItem(key);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// 判断设备类型
|
|
2
|
+
export interface IFromType {
|
|
3
|
+
isWxWork: boolean;
|
|
4
|
+
isWeixin: boolean;
|
|
5
|
+
isMobileScreen: boolean;
|
|
6
|
+
isMobileAny: boolean;
|
|
7
|
+
}
|
|
8
|
+
export const fromTypeMap = {
|
|
9
|
+
isWxWork: 'wecom',
|
|
10
|
+
isWeixin: 'wechat',
|
|
11
|
+
isMobile: 'mobile',
|
|
12
|
+
isMobileAny: 'mobile',
|
|
13
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 时间处理相关
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
* @module Date
|
|
5
|
+
* @preferred
|
|
6
|
+
*/
|
|
7
|
+
import { isEmpty } from '../common';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 解决ios不支持 new Date('2020-02-02')问题
|
|
11
|
+
#### 使用说明
|
|
12
|
+
```
|
|
13
|
+
compatibleDate('2020-03-04') 返回 2020/03/04
|
|
14
|
+
compatibleDate(Date.now()) 返回 Date.now()
|
|
15
|
+
compatibleDate(new Date()) 返回 new Date()
|
|
16
|
+
```
|
|
17
|
+
*/
|
|
18
|
+
export function compatibleDate(date: string | number | Date) {
|
|
19
|
+
if (typeof date === 'string') {
|
|
20
|
+
return date.replace(/-/g, '/');
|
|
21
|
+
}
|
|
22
|
+
return date;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 调用原生 new Date 返回 Date实例 解决ios不支持 yyyy-MM-dd HH:mm:ss 格式new Date
|
|
27
|
+
#### 使用说明
|
|
28
|
+
```
|
|
29
|
+
newDate() 返回 new Date()
|
|
30
|
+
newDate('2020-03-04') 返回 new Date('2020-03-04')
|
|
31
|
+
```
|
|
32
|
+
*/
|
|
33
|
+
export function newDate(date?: string | number | Date): Date {
|
|
34
|
+
if (!date) return new Date();
|
|
35
|
+
return new Date(compatibleDate(date));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 格式化时间
|
|
40
|
+
#### 使用说明
|
|
41
|
+
```
|
|
42
|
+
formatTime() 返回 yyyy-MM-dd HH:mm:ss 格式的当前时间
|
|
43
|
+
formatTime('2018-02-02 00:00:00') 返回 2018-02-02 00:00:00
|
|
44
|
+
formatTime('2018-02-02', 'yyyy/MM/dd') 返回 2018/02/02
|
|
45
|
+
formatTime('2018-02-02 23:50:50', 'yyyy/MM/dd HH:mm:ss') 返回 2018/02/02 23:50:50
|
|
46
|
+
formatTime('2018-02-02 23:50:50', 'MM/dd HH:mm:ss') 返回 02/02 23:50:50
|
|
47
|
+
formatTime(new Date('2018-02-02 23:50:50') + 200, 'yyyy/MM/dd HH:mm:ss S') 返回 2018/02/02 23:50:50 200
|
|
48
|
+
```
|
|
49
|
+
*/
|
|
50
|
+
export function formatTime(date: string | number | Date = Date.now(), fmt = 'yyyy-MM-dd HH:mm:ss') {
|
|
51
|
+
date = compatibleDate(date);
|
|
52
|
+
|
|
53
|
+
date = new Date(date);
|
|
54
|
+
const o: any = {
|
|
55
|
+
'M+': date.getMonth() + 1, // 月份
|
|
56
|
+
'd+': date.getDate(), // 日
|
|
57
|
+
'h+': date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, // 小时
|
|
58
|
+
'H+': date.getHours(), // 小时
|
|
59
|
+
'm+': date.getMinutes(), // 分
|
|
60
|
+
's+': date.getSeconds(), // 秒
|
|
61
|
+
'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
|
|
62
|
+
S: date.getMilliseconds(), // 毫秒
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
if (/(y+)/.test(fmt)) {
|
|
66
|
+
fmt = fmt.replace(RegExp.$1, (`${date.getFullYear()}`).substr(4 - RegExp.$1.length));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
Object.keys(o).forEach((k) => {
|
|
70
|
+
if (new RegExp(`(${k})`).test(fmt)) {
|
|
71
|
+
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : ((`00${o[k]}`).substr((`${o[k]}`).length)));
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return fmt;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 格式化相对时间
|
|
80
|
+
#### 使用说明
|
|
81
|
+
```
|
|
82
|
+
fromNow('') 返回 -
|
|
83
|
+
fromNow(Date.now() - 2000) 返回 刚刚
|
|
84
|
+
fromNow(now - 3420 * 1000) 返回 58分钟前面
|
|
85
|
+
fromNow(now - 3600 * 1000 * 3) 返回 3小时前
|
|
86
|
+
fromNow(now - 3600 * 1000 * 24 * 1.5) 返回 1天前
|
|
87
|
+
fromNow(now - day5) 返回 当前时间的5天前日期 格式 yyyy年MM月dd日 HH时mm分ss秒
|
|
88
|
+
fromNow(Date.now() + 2000) 返回 Date.now() + 2000
|
|
89
|
+
fromNow('', '', 'defaultValue') 等于 defaultValue
|
|
90
|
+
```
|
|
91
|
+
*/
|
|
92
|
+
export function fromNow(time: Date | string | number, fmt: string = 'yyyy年MM月dd日 HH时mm分ss秒', defaultValue = '-') {
|
|
93
|
+
if (isEmpty(time)) {
|
|
94
|
+
return defaultValue;
|
|
95
|
+
}
|
|
96
|
+
time = compatibleDate(time);
|
|
97
|
+
const d = new Date(time);
|
|
98
|
+
const now = Date.now();
|
|
99
|
+
const diff = (now - d.getTime()) / 1000;
|
|
100
|
+
if (diff < 0) {
|
|
101
|
+
return time;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (diff < 30) {
|
|
105
|
+
return '刚刚';
|
|
106
|
+
}
|
|
107
|
+
if (diff < 3600) {
|
|
108
|
+
return `${Math.ceil(diff / 60)}分钟前`;
|
|
109
|
+
}
|
|
110
|
+
if (diff < 3600 * 24) {
|
|
111
|
+
return `${Math.floor(diff / 3600)}小时前`;
|
|
112
|
+
}
|
|
113
|
+
if (diff < 3600 * 24 * 2) {
|
|
114
|
+
return '1天前';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return formatTime(time, fmt);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 增加日期天数
|
|
122
|
+
#### 使用说明
|
|
123
|
+
```
|
|
124
|
+
addDays('') 返回 -
|
|
125
|
+
addDays('2018-02-02') 返回 2018-02-02
|
|
126
|
+
addDays('2018-02-02', 2) 返回 2018-02-04
|
|
127
|
+
addDays('2018-02-02', 2, 'yyyy/MM/dd') 返回 2018/02/04
|
|
128
|
+
addDays(new Date('2020-03-04'), 2, 'yyyy/MM/dd') 返回 2020/03/06
|
|
129
|
+
addDays(Date.now(), 2, 'yyyy/MM/dd') 返回 今天 + 2天
|
|
130
|
+
```
|
|
131
|
+
*/
|
|
132
|
+
export function addDays(date: Date | string | number, days: string | number = 0, fmt: string = 'yyyy-MM-dd') {
|
|
133
|
+
if (isEmpty(date)) {
|
|
134
|
+
return '-';
|
|
135
|
+
}
|
|
136
|
+
date = compatibleDate(date);
|
|
137
|
+
const d = new Date(date);
|
|
138
|
+
d.setDate(d.getDate() + Number(days));
|
|
139
|
+
|
|
140
|
+
return formatTime(d, fmt);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 获取当前时间月份
|
package/src/dom/index.ts
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM 操作相关
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
* @module DOM
|
|
5
|
+
* @preferred
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 检查元素是否包含指定的 class
|
|
10
|
+
* @param el - DOM 元素
|
|
11
|
+
* @param cls - class 名称
|
|
12
|
+
* @returns 是否包含该 class
|
|
13
|
+
*/
|
|
14
|
+
export function hasClass(el: Element | null, cls: string): boolean {
|
|
15
|
+
if (typeof window === 'undefined' || !el) return false;
|
|
16
|
+
return !!el.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 添加 class 到元素
|
|
21
|
+
* @param el - DOM 元素
|
|
22
|
+
* @param cls - class 名称
|
|
23
|
+
*/
|
|
24
|
+
export function addClass(el: Element | null, cls: string): void {
|
|
25
|
+
if (typeof window === 'undefined' || !el) return;
|
|
26
|
+
if (!hasClass(el, cls)) {
|
|
27
|
+
el.className += ' ' + cls;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 从元素移除 class
|
|
33
|
+
* @param el - DOM 元素
|
|
34
|
+
* @param cls - class 名称
|
|
35
|
+
*/
|
|
36
|
+
export function removeClass(el: Element | null, cls: string): void {
|
|
37
|
+
if (typeof window === 'undefined' || !el) return;
|
|
38
|
+
if (hasClass(el, cls)) {
|
|
39
|
+
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
|
|
40
|
+
el.className = el.className.replace(reg, ' ').trim();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 切换元素的 class
|
|
46
|
+
* @param flag - 是否添加 class
|
|
47
|
+
* @param clsName - class 名称
|
|
48
|
+
* @param target - 目标元素,默认为 document.body
|
|
49
|
+
*/
|
|
50
|
+
export function toggleClass(
|
|
51
|
+
flag: boolean,
|
|
52
|
+
clsName: string,
|
|
53
|
+
target?: HTMLElement
|
|
54
|
+
): void {
|
|
55
|
+
if (typeof window === 'undefined') return;
|
|
56
|
+
|
|
57
|
+
const targetEl = target || document.body;
|
|
58
|
+
let { className } = targetEl;
|
|
59
|
+
className = className.replace(clsName, '').trim();
|
|
60
|
+
targetEl.className = flag ? `${className} ${clsName}`.trim() : className;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 打开链接
|
|
65
|
+
* @param url - URL 地址
|
|
66
|
+
* @param target - 打开方式,默认 "_blank"
|
|
67
|
+
*/
|
|
68
|
+
export function openLink(url: string, target: string = '_blank'): void {
|
|
69
|
+
if (typeof window === 'undefined') return;
|
|
70
|
+
|
|
71
|
+
const link = document.createElement('a');
|
|
72
|
+
link.setAttribute('href', url);
|
|
73
|
+
link.setAttribute('target', target);
|
|
74
|
+
link.setAttribute('rel', 'noreferrer noopener');
|
|
75
|
+
link.setAttribute('id', 'external');
|
|
76
|
+
|
|
77
|
+
const existingLink = document.getElementById('external');
|
|
78
|
+
if (existingLink) {
|
|
79
|
+
document.body.removeChild(existingLink);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
document.body.appendChild(link);
|
|
83
|
+
link.click();
|
|
84
|
+
link.remove();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 复制文本到剪贴板
|
|
89
|
+
* @param text - 要复制的文本
|
|
90
|
+
* @returns 是否复制成功
|
|
91
|
+
*/
|
|
92
|
+
export function copyTextToClipboard(text: string): boolean {
|
|
93
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 创建临时文本框
|
|
98
|
+
const textarea = document.createElement('textarea');
|
|
99
|
+
const activeElement = document.activeElement as HTMLElement;
|
|
100
|
+
|
|
101
|
+
textarea.value = text;
|
|
102
|
+
textarea.setAttribute('readonly', '');
|
|
103
|
+
textarea.style.position = 'absolute';
|
|
104
|
+
textarea.style.left = '-9999px';
|
|
105
|
+
textarea.style.fontSize = '12pt';
|
|
106
|
+
textarea.style.border = '0';
|
|
107
|
+
textarea.style.padding = '0';
|
|
108
|
+
textarea.style.margin = '0';
|
|
109
|
+
|
|
110
|
+
// 保存当前选区
|
|
111
|
+
const selection = document.getSelection();
|
|
112
|
+
let originalRange: Range | null = null;
|
|
113
|
+
if (selection && selection.rangeCount > 0) {
|
|
114
|
+
originalRange = selection.getRangeAt(0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 添加到 DOM 并选中
|
|
118
|
+
document.body.appendChild(textarea);
|
|
119
|
+
textarea.select();
|
|
120
|
+
textarea.selectionStart = 0;
|
|
121
|
+
textarea.selectionEnd = text.length;
|
|
122
|
+
|
|
123
|
+
// 执行复制
|
|
124
|
+
let success = false;
|
|
125
|
+
try {
|
|
126
|
+
success = document.execCommand('copy');
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.error('复制失败:', err);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 清理
|
|
132
|
+
textarea.remove();
|
|
133
|
+
|
|
134
|
+
// 恢复原选区
|
|
135
|
+
if (originalRange && selection) {
|
|
136
|
+
selection.removeAllRanges();
|
|
137
|
+
selection.addRange(originalRange);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 恢复焦点
|
|
141
|
+
if (activeElement) {
|
|
142
|
+
activeElement.focus();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return success;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* SVG 信息接口
|
|
150
|
+
*/
|
|
151
|
+
export interface SvgInfo {
|
|
152
|
+
width: number;
|
|
153
|
+
height: number;
|
|
154
|
+
body: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* 解析 SVG 字符串,提取宽度、高度和 body 内容
|
|
159
|
+
* @param svg - SVG 字符串
|
|
160
|
+
* @returns SVG 信息对象或原字符串
|
|
161
|
+
*/
|
|
162
|
+
export function getSvgInfo(svg: string): SvgInfo | string {
|
|
163
|
+
if (typeof window === 'undefined' || typeof DOMParser === 'undefined') {
|
|
164
|
+
return svg;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const parser = new DOMParser();
|
|
169
|
+
const doc = parser.parseFromString(svg, 'image/svg+xml');
|
|
170
|
+
const svgElement = doc.querySelector('svg');
|
|
171
|
+
|
|
172
|
+
if (!svgElement) {
|
|
173
|
+
return svg;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const viewBox = svgElement.getAttribute('viewBox');
|
|
177
|
+
if (!viewBox) {
|
|
178
|
+
throw new Error('Invalid SVG string: Missing viewBox attribute.');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const viewBoxParts = viewBox.split(' ');
|
|
182
|
+
const width = parseInt(viewBoxParts[2], 10);
|
|
183
|
+
const height = parseInt(viewBoxParts[3], 10);
|
|
184
|
+
|
|
185
|
+
const paths = Array.from(svgElement.querySelectorAll('path'));
|
|
186
|
+
const body = paths.map(path => path.outerHTML).join(' ');
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
width,
|
|
190
|
+
height,
|
|
191
|
+
body,
|
|
192
|
+
};
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error('解析 SVG 失败:', error);
|
|
195
|
+
return svg;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|