@hhfenpm/utils 1.0.2 → 1.0.3
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/README.md +59 -38
- package/dist/index.esm.js +806 -434
- package/dist/index.js +822 -449
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -2194,66 +2194,107 @@ function cleanEscapedString(input) {
|
|
|
2194
2194
|
return matched[1].replace(doubleQuoteRegExp, "'");
|
|
2195
2195
|
}
|
|
2196
2196
|
|
|
2197
|
-
|
|
2197
|
+
/**
|
|
2198
|
+
* 数据格式化工具
|
|
2199
|
+
*/
|
|
2198
2200
|
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
+
/**
|
|
2202
|
+
* 日期格式化
|
|
2203
|
+
* @param {number|string} timestamp - 时间戳
|
|
2204
|
+
* @param {string} formatStr - 格式化字符串,默认 "yyyy-MM-dd"
|
|
2205
|
+
* @returns {string} 格式化后的日期字符串
|
|
2206
|
+
*/
|
|
2207
|
+
function formatDate(timestamp, formatStr = 'yyyy-MM-dd') {
|
|
2201
2208
|
if (!timestamp) return ''
|
|
2202
2209
|
try {
|
|
2203
2210
|
const num = +timestamp;
|
|
2204
2211
|
if (num) {
|
|
2205
|
-
return format(num,
|
|
2212
|
+
return format(num, formatStr)
|
|
2206
2213
|
}
|
|
2207
|
-
return format(new Date(timestamp),
|
|
2214
|
+
return format(new Date(timestamp), formatStr)
|
|
2208
2215
|
} catch (error) {
|
|
2209
2216
|
return ''
|
|
2210
2217
|
}
|
|
2211
2218
|
}
|
|
2212
2219
|
|
|
2220
|
+
/**
|
|
2221
|
+
* 日期时间格式化(精确到分钟)
|
|
2222
|
+
* @param {number|string} timestamp - 时间戳
|
|
2223
|
+
* @returns {string} 格式化后的日期时间字符串 "yyyy-MM-dd HH:mm"
|
|
2224
|
+
*/
|
|
2213
2225
|
function formatDateMinute(timestamp) {
|
|
2214
2226
|
if (!timestamp) return ''
|
|
2215
2227
|
try {
|
|
2216
|
-
return format(+timestamp,
|
|
2228
|
+
return format(+timestamp, 'yyyy-MM-dd HH:mm')
|
|
2217
2229
|
} catch (error) {
|
|
2218
2230
|
return ''
|
|
2219
2231
|
}
|
|
2220
2232
|
}
|
|
2221
2233
|
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2234
|
+
/**
|
|
2235
|
+
* 小数转百分比(数字,会删除小数点后面的0)
|
|
2236
|
+
* @param {number} number - 小数
|
|
2237
|
+
* @param {number} decimalPlaces - 保留小数位数,默认2
|
|
2238
|
+
* @param {number} scale - 缩放比例,默认100
|
|
2239
|
+
* @returns {number} 百分比数字
|
|
2240
|
+
*/
|
|
2241
|
+
function formatDecimal(number = 0, decimalPlaces = 2, scale = 100) {
|
|
2242
|
+
return +(+number * scale).toFixed(decimalPlaces)
|
|
2225
2243
|
}
|
|
2226
2244
|
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2245
|
+
/**
|
|
2246
|
+
* 小数转百分比(字符串,不会删除小数点后面的0)
|
|
2247
|
+
* @param {number} number - 小数
|
|
2248
|
+
* @param {number} decimalPlaces - 保留小数位数,默认2
|
|
2249
|
+
* @param {number} scale - 缩放比例,默认100
|
|
2250
|
+
* @returns {string} 百分比字符串
|
|
2251
|
+
*/
|
|
2252
|
+
function formatDecimalString(
|
|
2253
|
+
number = 0,
|
|
2254
|
+
decimalPlaces = 2,
|
|
2255
|
+
scale = 100
|
|
2256
|
+
) {
|
|
2257
|
+
return (+number * scale).toFixed(decimalPlaces)
|
|
2230
2258
|
}
|
|
2231
2259
|
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2260
|
+
/**
|
|
2261
|
+
* 金额格式化
|
|
2262
|
+
* @param {number} number - 金额数字
|
|
2263
|
+
* @param {number} decimalPlaces - 保留小数位数,默认2
|
|
2264
|
+
* @param {number} scale - 缩放比例,默认100
|
|
2265
|
+
* @returns {string} 格式化后的金额字符串
|
|
2266
|
+
*/
|
|
2267
|
+
function formatMoney(number = 0, decimalPlaces = 2, scale = 100) {
|
|
2268
|
+
return (+number / scale).toFixed(decimalPlaces)
|
|
2235
2269
|
}
|
|
2236
2270
|
|
|
2237
|
-
|
|
2238
|
-
|
|
2271
|
+
/**
|
|
2272
|
+
* 创建唯一ID
|
|
2273
|
+
* @returns {string} 唯一ID
|
|
2274
|
+
*/
|
|
2275
|
+
function createGuid() {
|
|
2239
2276
|
return (
|
|
2240
2277
|
(Math.random() * 10000000).toString(16).substring(0, 4) +
|
|
2241
|
-
|
|
2278
|
+
'-' +
|
|
2242
2279
|
new Date().getTime() +
|
|
2243
|
-
|
|
2280
|
+
'-' +
|
|
2244
2281
|
Math.random().toString().substring(2, 7)
|
|
2245
2282
|
)
|
|
2246
2283
|
}
|
|
2247
2284
|
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2285
|
+
/**
|
|
2286
|
+
* 数字格式化(千分位)
|
|
2287
|
+
* @param {number} num - 数字
|
|
2288
|
+
* @returns {string} 格式化后的数字字符串
|
|
2289
|
+
*/
|
|
2290
|
+
function formatNumber(num) {
|
|
2291
|
+
if (!num) return '0'
|
|
2251
2292
|
let str = `${num}`;
|
|
2252
2293
|
let len = str.length;
|
|
2253
2294
|
if (len === 3) {
|
|
2254
2295
|
return str
|
|
2255
2296
|
}
|
|
2256
|
-
let str2 =
|
|
2297
|
+
let str2 = '';
|
|
2257
2298
|
let max = Math.floor(len / 3);
|
|
2258
2299
|
if (len % 3 === 0) {
|
|
2259
2300
|
max = max - 1;
|
|
@@ -2261,19 +2302,24 @@ function format_number(num) {
|
|
|
2261
2302
|
for (let i = 0; i < max; i++) {
|
|
2262
2303
|
let s = str.slice(len - 3, len);
|
|
2263
2304
|
str = str.substring(0, len - 3);
|
|
2264
|
-
str2 =
|
|
2305
|
+
str2 = ',' + s + str2;
|
|
2265
2306
|
len = str.length;
|
|
2266
2307
|
}
|
|
2267
2308
|
str += str2;
|
|
2268
2309
|
return str
|
|
2269
2310
|
}
|
|
2270
2311
|
|
|
2271
|
-
|
|
2272
|
-
|
|
2312
|
+
/**
|
|
2313
|
+
* 数组转标签对象
|
|
2314
|
+
* @param {Array} arr - 数组
|
|
2315
|
+
* @param {Object} props - 属性映射 {label: 'label', value: 'value', children: 'children', re: true}
|
|
2316
|
+
* @returns {Object} 标签对象
|
|
2317
|
+
*/
|
|
2318
|
+
function arrToLabel(arr, props) {
|
|
2273
2319
|
props = {
|
|
2274
|
-
label:
|
|
2275
|
-
value:
|
|
2276
|
-
children:
|
|
2320
|
+
label: 'label',
|
|
2321
|
+
value: 'value',
|
|
2322
|
+
children: 'children',
|
|
2277
2323
|
re: true,
|
|
2278
2324
|
...props,
|
|
2279
2325
|
};
|
|
@@ -2290,11 +2336,16 @@ function arr_label(arr, props) {
|
|
|
2290
2336
|
return obj
|
|
2291
2337
|
}
|
|
2292
2338
|
|
|
2293
|
-
|
|
2294
|
-
|
|
2339
|
+
/**
|
|
2340
|
+
* 数组转对象
|
|
2341
|
+
* @param {Array} arr - 数组
|
|
2342
|
+
* @param {Object} props - 属性映射 {value: 'value', children: 'children', re: true}
|
|
2343
|
+
* @returns {Object} 对象
|
|
2344
|
+
*/
|
|
2345
|
+
function arrToObj(arr, props) {
|
|
2295
2346
|
props = {
|
|
2296
|
-
value:
|
|
2297
|
-
children:
|
|
2347
|
+
value: 'value',
|
|
2348
|
+
children: 'children',
|
|
2298
2349
|
re: true,
|
|
2299
2350
|
...props,
|
|
2300
2351
|
};
|
|
@@ -2311,53 +2362,69 @@ function arr_obj(arr, props) {
|
|
|
2311
2362
|
return obj
|
|
2312
2363
|
}
|
|
2313
2364
|
|
|
2314
|
-
|
|
2315
|
-
|
|
2365
|
+
/**
|
|
2366
|
+
* 复制文本到剪贴板
|
|
2367
|
+
* @param {string} content - 要复制的内容
|
|
2368
|
+
* @returns {boolean} 是否成功
|
|
2369
|
+
*/
|
|
2370
|
+
function copyContent(content) {
|
|
2316
2371
|
if (typeof document === 'undefined') {
|
|
2317
|
-
console.warn('
|
|
2372
|
+
console.warn('copyContent需要在浏览器环境使用');
|
|
2318
2373
|
return false
|
|
2319
2374
|
}
|
|
2320
|
-
const input = document.createElement(
|
|
2321
|
-
input.setAttribute(
|
|
2375
|
+
const input = document.createElement('input');
|
|
2376
|
+
input.setAttribute('value', content);
|
|
2322
2377
|
document.body.appendChild(input);
|
|
2323
2378
|
input.select();
|
|
2324
|
-
const result = document.execCommand(
|
|
2379
|
+
const result = document.execCommand('copy');
|
|
2325
2380
|
document.body.removeChild(input);
|
|
2326
2381
|
return result
|
|
2327
2382
|
}
|
|
2328
2383
|
|
|
2329
|
-
|
|
2330
|
-
|
|
2384
|
+
/**
|
|
2385
|
+
* 计算高度(判断元素高度是否超过容器)
|
|
2386
|
+
* @param {string} itemId - 元素ID
|
|
2387
|
+
* @param {string} containerId - 容器ID
|
|
2388
|
+
* @returns {boolean} 元素高度是否超过容器
|
|
2389
|
+
*/
|
|
2390
|
+
function calculateHeight(itemId, containerId) {
|
|
2331
2391
|
if (typeof document === 'undefined') {
|
|
2332
2392
|
return false
|
|
2333
2393
|
}
|
|
2334
|
-
const dom = document.getElementById(
|
|
2335
|
-
const containerDom = document.getElementById(
|
|
2394
|
+
const dom = document.getElementById(itemId);
|
|
2395
|
+
const containerDom = document.getElementById(containerId);
|
|
2336
2396
|
if (dom && containerDom) {
|
|
2337
2397
|
const height = dom.offsetHeight;
|
|
2338
|
-
const
|
|
2339
|
-
return height >
|
|
2398
|
+
const maxHeight = containerDom.offsetHeight;
|
|
2399
|
+
return height > maxHeight
|
|
2340
2400
|
}
|
|
2341
2401
|
return false
|
|
2342
2402
|
}
|
|
2343
2403
|
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2404
|
+
/**
|
|
2405
|
+
* 文字截断(超过指定长度显示...)
|
|
2406
|
+
* @param {string} text - 文本
|
|
2407
|
+
* @param {number} maxLength - 最大长度,默认8
|
|
2408
|
+
* @returns {string} 截断后的文本
|
|
2409
|
+
*/
|
|
2410
|
+
function formatText(text, maxLength = 8) {
|
|
2411
|
+
if (!text) return ''
|
|
2412
|
+
let trimText = text.trim();
|
|
2413
|
+
if (trimText.length <= maxLength) {
|
|
2414
|
+
return trimText
|
|
2352
2415
|
}
|
|
2353
|
-
|
|
2354
|
-
return trimTetx.substring(0, 8) + "..."
|
|
2416
|
+
return trimText.substring(0, maxLength) + '...'
|
|
2355
2417
|
}
|
|
2356
2418
|
|
|
2357
|
-
|
|
2358
|
-
|
|
2419
|
+
/**
|
|
2420
|
+
* 设置光标位置(用于contenteditable元素)
|
|
2421
|
+
* @param {HTMLElement} dom - DOM元素
|
|
2422
|
+
* @param {number} start - 起始位置,默认0
|
|
2423
|
+
* @param {number} end - 结束位置,默认0
|
|
2424
|
+
*/
|
|
2425
|
+
function setCursor(dom, start = 0, end = 0) {
|
|
2359
2426
|
if (typeof window === 'undefined' || !dom) {
|
|
2360
|
-
console.warn('
|
|
2427
|
+
console.warn('setCursor需要在浏览器环境使用');
|
|
2361
2428
|
return
|
|
2362
2429
|
}
|
|
2363
2430
|
const selection = window.getSelection();
|
|
@@ -2369,11 +2436,19 @@ function set_cursor(dom, start = 0, end = 0) {
|
|
|
2369
2436
|
dom.focus();
|
|
2370
2437
|
}
|
|
2371
2438
|
|
|
2372
|
-
|
|
2439
|
+
/**
|
|
2440
|
+
* 同步setTimeout(Promise版本)
|
|
2441
|
+
* @param {number} time - 延迟时间(毫秒)
|
|
2442
|
+
* @returns {Promise} Promise对象
|
|
2443
|
+
*/
|
|
2373
2444
|
function sleep(time) {
|
|
2374
|
-
return new Promise(
|
|
2445
|
+
return new Promise(resolve => setTimeout(resolve, time))
|
|
2375
2446
|
}
|
|
2376
2447
|
|
|
2448
|
+
/**
|
|
2449
|
+
* 检测浏览器缩放比例
|
|
2450
|
+
* @returns {number} 缩放比例
|
|
2451
|
+
*/
|
|
2377
2452
|
function detectZoom() {
|
|
2378
2453
|
if (typeof window === 'undefined') {
|
|
2379
2454
|
return 1
|
|
@@ -2388,7 +2463,10 @@ function detectZoom() {
|
|
|
2388
2463
|
if (screen.deviceXDPI && screen.logicalXDPI) {
|
|
2389
2464
|
ratio = screen.deviceXDPI / screen.logicalXDPI;
|
|
2390
2465
|
}
|
|
2391
|
-
} else if (
|
|
2466
|
+
} else if (
|
|
2467
|
+
window.outerWidth !== undefined &&
|
|
2468
|
+
window.innerWidth !== undefined
|
|
2469
|
+
) {
|
|
2392
2470
|
ratio = window.outerWidth / window.innerWidth;
|
|
2393
2471
|
}
|
|
2394
2472
|
|
|
@@ -2397,25 +2475,28 @@ function detectZoom() {
|
|
|
2397
2475
|
|
|
2398
2476
|
/**
|
|
2399
2477
|
* 递归查找树节点
|
|
2400
|
-
* @param {Array} tree 树数据
|
|
2401
|
-
* @param {
|
|
2402
|
-
* @param {Object} keyMap 节点字段映射
|
|
2403
|
-
* @returns {Object|
|
|
2478
|
+
* @param {Array} tree - 树数据
|
|
2479
|
+
* @param {string} id - 节点id值
|
|
2480
|
+
* @param {Object} keyMap - 节点字段映射 {id: 'id', children: 'children'}
|
|
2481
|
+
* @returns {Object|null} 节点对象
|
|
2404
2482
|
*/
|
|
2405
2483
|
function findNodeOfTree(tree, id, keyMap = {}) {
|
|
2406
|
-
const
|
|
2407
|
-
id:
|
|
2408
|
-
children:
|
|
2484
|
+
const keyMapConfig = {
|
|
2485
|
+
id: 'id',
|
|
2486
|
+
children: 'children',
|
|
2409
2487
|
...keyMap,
|
|
2410
2488
|
};
|
|
2411
2489
|
|
|
2412
2490
|
function searchTree(nodes) {
|
|
2413
2491
|
for (let node of nodes) {
|
|
2414
|
-
if (node[
|
|
2492
|
+
if (node[keyMapConfig.id] === id) {
|
|
2415
2493
|
return node
|
|
2416
2494
|
}
|
|
2417
|
-
if (
|
|
2418
|
-
|
|
2495
|
+
if (
|
|
2496
|
+
node[keyMapConfig.children] &&
|
|
2497
|
+
node[keyMapConfig.children].length > 0
|
|
2498
|
+
) {
|
|
2499
|
+
const foundNode = searchTree(node[keyMapConfig.children]);
|
|
2419
2500
|
if (foundNode) {
|
|
2420
2501
|
return foundNode
|
|
2421
2502
|
}
|
|
@@ -2427,24 +2508,35 @@ function findNodeOfTree(tree, id, keyMap = {}) {
|
|
|
2427
2508
|
return searchTree(tree)
|
|
2428
2509
|
}
|
|
2429
2510
|
|
|
2430
|
-
|
|
2511
|
+
/**
|
|
2512
|
+
* 复制文本到剪贴板(兼容版)
|
|
2513
|
+
* @param {string} content - 要复制的内容
|
|
2514
|
+
* @param {string} type - 元素类型,'input' 或 'textarea',默认'input'
|
|
2515
|
+
* @returns {boolean} 是否成功
|
|
2516
|
+
*/
|
|
2517
|
+
function copyToClip(content, type = 'input') {
|
|
2431
2518
|
if (typeof document === 'undefined') {
|
|
2432
2519
|
console.warn('copyToClip需要在浏览器环境使用');
|
|
2433
2520
|
return false
|
|
2434
2521
|
}
|
|
2435
|
-
|
|
2522
|
+
const aux = document.createElement(type);
|
|
2436
2523
|
document.body.appendChild(aux);
|
|
2437
|
-
if (type ===
|
|
2438
|
-
aux.setAttribute(
|
|
2524
|
+
if (type === 'input') {
|
|
2525
|
+
aux.setAttribute('value', content);
|
|
2439
2526
|
} else {
|
|
2440
2527
|
aux.value = content;
|
|
2441
2528
|
}
|
|
2442
2529
|
aux.select();
|
|
2443
|
-
const result = document.execCommand(
|
|
2530
|
+
const result = document.execCommand('copy');
|
|
2444
2531
|
document.body.removeChild(aux);
|
|
2445
2532
|
return result
|
|
2446
2533
|
}
|
|
2447
2534
|
|
|
2535
|
+
/**
|
|
2536
|
+
* 手机号脱敏
|
|
2537
|
+
* @param {string|number} phone - 手机号
|
|
2538
|
+
* @returns {string} 脱敏后的手机号
|
|
2539
|
+
*/
|
|
2448
2540
|
function hidePhone(phone) {
|
|
2449
2541
|
if (!phone) return ''
|
|
2450
2542
|
phone = phone.toString();
|
|
@@ -2452,7 +2544,9 @@ function hidePhone(phone) {
|
|
|
2452
2544
|
}
|
|
2453
2545
|
|
|
2454
2546
|
/**
|
|
2455
|
-
*
|
|
2547
|
+
* 转义正则表达式特殊字符
|
|
2548
|
+
* @param {string} string - 要转义的字符串
|
|
2549
|
+
* @returns {string} 转义后的字符串
|
|
2456
2550
|
*/
|
|
2457
2551
|
function escapeRegExp(string) {
|
|
2458
2552
|
if (!string) {
|
|
@@ -2461,10 +2555,19 @@ function escapeRegExp(string) {
|
|
|
2461
2555
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
2462
2556
|
}
|
|
2463
2557
|
|
|
2464
|
-
|
|
2558
|
+
/**
|
|
2559
|
+
* 数据验证工具
|
|
2560
|
+
*/
|
|
2561
|
+
|
|
2562
|
+
/**
|
|
2563
|
+
* 基于Promise的表单字段验证(避免回调地狱)
|
|
2564
|
+
* @param {string} prop - 字段名
|
|
2565
|
+
* @param {Object} form - 表单实例
|
|
2566
|
+
* @returns {Promise} Promise对象,验证通过resolve,失败reject
|
|
2567
|
+
*/
|
|
2465
2568
|
function validateFieldAsync(prop, form) {
|
|
2466
2569
|
return new Promise((resolve, reject) => {
|
|
2467
|
-
form.validateField(prop,
|
|
2570
|
+
form.validateField(prop, valid => {
|
|
2468
2571
|
if (valid) {
|
|
2469
2572
|
reject(new Error(valid));
|
|
2470
2573
|
} else {
|
|
@@ -2474,19 +2577,28 @@ function validateFieldAsync(prop, form) {
|
|
|
2474
2577
|
})
|
|
2475
2578
|
}
|
|
2476
2579
|
|
|
2477
|
-
|
|
2580
|
+
/**
|
|
2581
|
+
* 手机号验证规则
|
|
2582
|
+
* @param {Object} rule - 验证规则
|
|
2583
|
+
* @param {string} value - 待验证的值
|
|
2584
|
+
* @param {Function} callback - 回调函数
|
|
2585
|
+
*/
|
|
2478
2586
|
function validatePhone(rule, value, callback) {
|
|
2479
2587
|
const reg = /^1[3456789]\d{9}$/;
|
|
2480
2588
|
if (!value) {
|
|
2481
|
-
return callback(new Error(
|
|
2589
|
+
return callback(new Error('请输入手机号'))
|
|
2482
2590
|
} else if (!reg.test(value)) {
|
|
2483
|
-
return callback(new Error(
|
|
2591
|
+
return callback(new Error('请输入正确的手机号'))
|
|
2484
2592
|
} else {
|
|
2485
2593
|
callback();
|
|
2486
2594
|
}
|
|
2487
2595
|
}
|
|
2488
2596
|
|
|
2489
|
-
|
|
2597
|
+
/**
|
|
2598
|
+
* 判断对象是否为空
|
|
2599
|
+
* @param {Object} obj - 待判断的对象
|
|
2600
|
+
* @returns {boolean} 是否为空对象
|
|
2601
|
+
*/
|
|
2490
2602
|
function isEmptyObj(obj) {
|
|
2491
2603
|
for (const name in obj) {
|
|
2492
2604
|
return false
|
|
@@ -2494,102 +2606,117 @@ function isEmptyObj(obj) {
|
|
|
2494
2606
|
return true
|
|
2495
2607
|
}
|
|
2496
2608
|
|
|
2497
|
-
|
|
2609
|
+
/**
|
|
2610
|
+
* 深度比较两个值是否相等(不包含引用比较)
|
|
2611
|
+
* @param {*} a - 第一个值
|
|
2612
|
+
* @param {*} b - 第二个值
|
|
2613
|
+
* @returns {boolean} 是否相等
|
|
2614
|
+
*/
|
|
2498
2615
|
function isEqual(a, b) {
|
|
2499
2616
|
const classNameA = Object.prototype.toString.call(a);
|
|
2500
2617
|
const classNameB = Object.prototype.toString.call(b);
|
|
2618
|
+
|
|
2501
2619
|
// 如果数据类型不相等,则返回false
|
|
2502
2620
|
if (classNameA !== classNameB) {
|
|
2503
2621
|
return false
|
|
2504
|
-
}
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2624
|
+
// 如果数据类型相等,再根据不同数据类型分别判断
|
|
2625
|
+
if (classNameA === '[object Object]') {
|
|
2626
|
+
for (let key in a) {
|
|
2627
|
+
if (!isEqual(a[key], b[key])) return false
|
|
2628
|
+
}
|
|
2629
|
+
for (let key in b) {
|
|
2630
|
+
if (!isEqual(a[key], b[key])) return false
|
|
2631
|
+
}
|
|
2632
|
+
return true
|
|
2633
|
+
} else if (classNameA === '[object Array]') {
|
|
2634
|
+
if (a.length !== b.length) {
|
|
2635
|
+
return false
|
|
2636
|
+
} else {
|
|
2637
|
+
for (let i = 0, len = a.length; i < len; i++) {
|
|
2638
|
+
if (!isEqual(a[i], b[i])) return false
|
|
2512
2639
|
}
|
|
2513
2640
|
return true
|
|
2514
|
-
} else if (classNameA === "[object Array]") {
|
|
2515
|
-
if (a.length !== b.length) {
|
|
2516
|
-
return false
|
|
2517
|
-
} else {
|
|
2518
|
-
for (let i = 0, len = a.length; i < len; i++) {
|
|
2519
|
-
if (!isEqual(a[i], b[i])) return false
|
|
2520
|
-
}
|
|
2521
|
-
return true
|
|
2522
|
-
}
|
|
2523
|
-
} else if (classNameA === "[object Function]") {
|
|
2524
|
-
return a.toString() === b.toString()
|
|
2525
|
-
} else {
|
|
2526
|
-
return Object.is(a, b)
|
|
2527
2641
|
}
|
|
2642
|
+
} else if (classNameA === '[object Function]') {
|
|
2643
|
+
return a.toString() === b.toString()
|
|
2644
|
+
} else {
|
|
2645
|
+
return Object.is(a, b)
|
|
2528
2646
|
}
|
|
2529
2647
|
}
|
|
2530
2648
|
|
|
2531
|
-
|
|
2649
|
+
/**
|
|
2650
|
+
* 判断值是否为空
|
|
2651
|
+
* @param {*} value - 待判断的值
|
|
2652
|
+
* @returns {boolean} 是否为空
|
|
2653
|
+
*/
|
|
2532
2654
|
function isEmpty(value) {
|
|
2533
|
-
if (value === undefined || value === null || value ===
|
|
2655
|
+
if (value === undefined || value === null || value === '') {
|
|
2534
2656
|
return true
|
|
2535
2657
|
}
|
|
2536
2658
|
return false
|
|
2537
2659
|
}
|
|
2538
2660
|
|
|
2661
|
+
/**
|
|
2662
|
+
* 获取浏览器信息
|
|
2663
|
+
* @returns {Object} {type: string, version: string}
|
|
2664
|
+
*/
|
|
2539
2665
|
function getBrowserInfo() {
|
|
2540
2666
|
if (typeof navigator === 'undefined') {
|
|
2541
2667
|
return {
|
|
2542
|
-
type:
|
|
2543
|
-
version:
|
|
2668
|
+
type: 'Unknown',
|
|
2669
|
+
version: 'Unknown',
|
|
2544
2670
|
}
|
|
2545
2671
|
}
|
|
2672
|
+
|
|
2546
2673
|
const userAgent = navigator.userAgent;
|
|
2547
|
-
let browserType =
|
|
2548
|
-
let browserVersion =
|
|
2674
|
+
let browserType = 'Unknown';
|
|
2675
|
+
let browserVersion = 'Unknown';
|
|
2549
2676
|
|
|
2550
2677
|
// Check for Firefox
|
|
2551
|
-
if (userAgent.indexOf(
|
|
2552
|
-
browserType =
|
|
2678
|
+
if (userAgent.indexOf('Firefox') > -1) {
|
|
2679
|
+
browserType = 'Mozilla Firefox';
|
|
2553
2680
|
const match = userAgent.match(/Firefox\/(\d+\.\d+)/);
|
|
2554
|
-
browserVersion = match ? match[1] :
|
|
2681
|
+
browserVersion = match ? match[1] : 'Unknown';
|
|
2555
2682
|
}
|
|
2556
2683
|
// Check for Samsung Browser
|
|
2557
|
-
else if (userAgent.indexOf(
|
|
2558
|
-
browserType =
|
|
2684
|
+
else if (userAgent.indexOf('SamsungBrowser') > -1) {
|
|
2685
|
+
browserType = 'Samsung Internet';
|
|
2559
2686
|
const match = userAgent.match(/SamsungBrowser\/(\d+\.\d+)/);
|
|
2560
|
-
browserVersion = match ? match[1] :
|
|
2687
|
+
browserVersion = match ? match[1] : 'Unknown';
|
|
2561
2688
|
}
|
|
2562
2689
|
// Check for Opera or OPR (Opera based on Chromium)
|
|
2563
|
-
else if (userAgent.indexOf(
|
|
2564
|
-
browserType =
|
|
2690
|
+
else if (userAgent.indexOf('Opera') > -1 || userAgent.indexOf('OPR') > -1) {
|
|
2691
|
+
browserType = 'Opera';
|
|
2565
2692
|
const match = userAgent.match(/(Opera|OPR)\/(\d+\.\d+)/);
|
|
2566
|
-
browserVersion = match ? match[2] :
|
|
2693
|
+
browserVersion = match ? match[2] : 'Unknown';
|
|
2567
2694
|
}
|
|
2568
2695
|
// Check for Internet Explorer
|
|
2569
|
-
else if (userAgent.indexOf(
|
|
2570
|
-
browserType =
|
|
2696
|
+
else if (userAgent.indexOf('Trident') > -1) {
|
|
2697
|
+
browserType = 'Microsoft Internet Explorer';
|
|
2571
2698
|
const versionMatch = userAgent.match(/rv:(\d+\.\d+)/);
|
|
2572
2699
|
if (versionMatch) {
|
|
2573
2700
|
browserVersion = versionMatch[1];
|
|
2574
2701
|
}
|
|
2575
2702
|
}
|
|
2576
2703
|
// Check for Microsoft Edge
|
|
2577
|
-
else if (userAgent.indexOf(
|
|
2578
|
-
browserType =
|
|
2704
|
+
else if (userAgent.indexOf('Edge') > -1) {
|
|
2705
|
+
browserType = 'Microsoft Edge';
|
|
2579
2706
|
const match = userAgent.match(/Edge\/(\d+\.\d+)/);
|
|
2580
|
-
browserVersion = match ? match[1] :
|
|
2707
|
+
browserVersion = match ? match[1] : 'Unknown';
|
|
2581
2708
|
}
|
|
2582
2709
|
// Check for Google Chrome
|
|
2583
|
-
else if (userAgent.indexOf(
|
|
2584
|
-
browserType =
|
|
2710
|
+
else if (userAgent.indexOf('Chrome') > -1) {
|
|
2711
|
+
browserType = 'Google Chrome';
|
|
2585
2712
|
const match = userAgent.match(/Chrome\/(\d+\.\d+)/);
|
|
2586
|
-
browserVersion = match ? match[1] :
|
|
2713
|
+
browserVersion = match ? match[1] : 'Unknown';
|
|
2587
2714
|
}
|
|
2588
2715
|
// Check for Apple Safari
|
|
2589
|
-
else if (userAgent.indexOf(
|
|
2590
|
-
browserType =
|
|
2716
|
+
else if (userAgent.indexOf('Safari') > -1) {
|
|
2717
|
+
browserType = 'Apple Safari';
|
|
2591
2718
|
const match = userAgent.match(/Version\/(\d+\.\d+)/);
|
|
2592
|
-
browserVersion = match ? match[1] :
|
|
2719
|
+
browserVersion = match ? match[1] : 'Unknown';
|
|
2593
2720
|
}
|
|
2594
2721
|
|
|
2595
2722
|
return {
|
|
@@ -2598,21 +2725,36 @@ function getBrowserInfo() {
|
|
|
2598
2725
|
}
|
|
2599
2726
|
}
|
|
2600
2727
|
|
|
2728
|
+
/**
|
|
2729
|
+
* 检测浏览器是否支持媒体设备(getUserMedia)
|
|
2730
|
+
* @returns {boolean} 是否支持
|
|
2731
|
+
*/
|
|
2601
2732
|
function isSupported() {
|
|
2602
2733
|
if (typeof navigator === 'undefined') {
|
|
2603
2734
|
return false
|
|
2604
2735
|
}
|
|
2736
|
+
|
|
2605
2737
|
const browserInfo = getBrowserInfo();
|
|
2606
|
-
|
|
2738
|
+
|
|
2739
|
+
// Firefox需要版本大于38
|
|
2740
|
+
if (browserInfo.type === 'Mozilla Firefox') {
|
|
2607
2741
|
return parseFloat(browserInfo.version) > 38
|
|
2608
2742
|
}
|
|
2743
|
+
|
|
2744
|
+
// 检测是否支持getUserMedia
|
|
2609
2745
|
return !!(
|
|
2610
2746
|
navigator.mediaDevices &&
|
|
2611
|
-
(navigator.mediaDevices.getUserMedia ||
|
|
2747
|
+
(navigator.mediaDevices.getUserMedia ||
|
|
2748
|
+
navigator.getUserMedia ||
|
|
2749
|
+
navigator.webkitGetUserMedia)
|
|
2612
2750
|
)
|
|
2613
2751
|
}
|
|
2614
2752
|
|
|
2615
|
-
|
|
2753
|
+
/**
|
|
2754
|
+
* 校验无意义连续符号
|
|
2755
|
+
* @param {string} input - 待校验的输入
|
|
2756
|
+
* @returns {Object} {isValid: boolean, message: string}
|
|
2757
|
+
*/
|
|
2616
2758
|
function validateMeaninglessConsecutiveSymbols(input) {
|
|
2617
2759
|
// 检查是否为空
|
|
2618
2760
|
if (input.trim() === '') {
|
|
@@ -2621,6 +2763,7 @@ function validateMeaninglessConsecutiveSymbols(input) {
|
|
|
2621
2763
|
message: '请输入内容',
|
|
2622
2764
|
}
|
|
2623
2765
|
}
|
|
2766
|
+
|
|
2624
2767
|
// 检查连续符号(中英文符号)
|
|
2625
2768
|
const symbolRegex =
|
|
2626
2769
|
/([\-\+\=\*\.\,\;\:\!\@\#\$\%\^\&\_\(\)\[\]\{\}\|\\\/\?\<\>\~\"\'\`\s]|[\u3000-\u303F]|[\uFF00-\uFFEF])\1{6,}/;
|
|
@@ -2630,6 +2773,7 @@ function validateMeaninglessConsecutiveSymbols(input) {
|
|
|
2630
2773
|
message: '输入无意义内容,请修改',
|
|
2631
2774
|
}
|
|
2632
2775
|
}
|
|
2776
|
+
|
|
2633
2777
|
// 检查连续数字
|
|
2634
2778
|
const digitRegex = /(\d)\1{6,}/;
|
|
2635
2779
|
if (digitRegex.test(input)) {
|
|
@@ -2638,6 +2782,7 @@ function validateMeaninglessConsecutiveSymbols(input) {
|
|
|
2638
2782
|
message: '输入无意义内容,请修改',
|
|
2639
2783
|
}
|
|
2640
2784
|
}
|
|
2785
|
+
|
|
2641
2786
|
// 检查连续相同文字(中英文)
|
|
2642
2787
|
const charRegex = /([\u4e00-\u9fa5a-zA-Z])\1{2,}/;
|
|
2643
2788
|
if (charRegex.test(input)) {
|
|
@@ -2646,188 +2791,318 @@ function validateMeaninglessConsecutiveSymbols(input) {
|
|
|
2646
2791
|
message: '输入无意义内容,请修改',
|
|
2647
2792
|
}
|
|
2648
2793
|
}
|
|
2794
|
+
|
|
2649
2795
|
return {
|
|
2650
2796
|
isValid: true,
|
|
2651
2797
|
message: '',
|
|
2652
2798
|
}
|
|
2653
2799
|
}
|
|
2654
2800
|
|
|
2655
|
-
|
|
2801
|
+
/**
|
|
2802
|
+
* 内存缓存工具
|
|
2803
|
+
*/
|
|
2804
|
+
|
|
2805
|
+
/**
|
|
2806
|
+
* 内存缓存对象
|
|
2807
|
+
*/
|
|
2656
2808
|
const cache = {
|
|
2657
2809
|
data: {},
|
|
2810
|
+
|
|
2811
|
+
/**
|
|
2812
|
+
* 设置缓存
|
|
2813
|
+
* @param {string} key - 键名
|
|
2814
|
+
* @param {*} data - 数据
|
|
2815
|
+
*/
|
|
2658
2816
|
set(key, data) {
|
|
2659
2817
|
this.data[key] = data;
|
|
2660
2818
|
},
|
|
2819
|
+
|
|
2820
|
+
/**
|
|
2821
|
+
* 获取缓存
|
|
2822
|
+
* @param {string} key - 键名
|
|
2823
|
+
* @returns {*} 缓存的数据
|
|
2824
|
+
*/
|
|
2661
2825
|
get(key) {
|
|
2662
2826
|
return this.data[key]
|
|
2663
2827
|
},
|
|
2828
|
+
|
|
2829
|
+
/**
|
|
2830
|
+
* 清除缓存
|
|
2831
|
+
* @param {string} key - 键名,如果不传则清除所有缓存
|
|
2832
|
+
*/
|
|
2664
2833
|
clear(key) {
|
|
2665
2834
|
if (key) {
|
|
2666
2835
|
delete this.data[key];
|
|
2667
2836
|
} else {
|
|
2668
2837
|
this.data = {};
|
|
2669
2838
|
}
|
|
2670
|
-
}
|
|
2839
|
+
},
|
|
2671
2840
|
};
|
|
2672
2841
|
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2842
|
+
/**
|
|
2843
|
+
* 构建唯一的URL key(用于请求缓存)
|
|
2844
|
+
* @param {string} url - 请求URL
|
|
2845
|
+
* @param {string} method - 请求方法
|
|
2846
|
+
* @param {Object} params - URL参数
|
|
2847
|
+
* @param {Object} data - 请求体数据
|
|
2848
|
+
* @returns {string} 唯一的URL key
|
|
2849
|
+
*/
|
|
2850
|
+
function buildUniqueUrl(url, method, params = {}, data = {}) {
|
|
2851
|
+
const paramStr = obj => {
|
|
2676
2852
|
if (Object.prototype.toString.call(obj) === '[object Object]') {
|
|
2677
|
-
return JSON.stringify(
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2853
|
+
return JSON.stringify(
|
|
2854
|
+
Object.keys(obj)
|
|
2855
|
+
.sort()
|
|
2856
|
+
.reduce((result, key) => {
|
|
2857
|
+
result[key] = obj[key];
|
|
2858
|
+
return result
|
|
2859
|
+
}, {})
|
|
2860
|
+
)
|
|
2681
2861
|
} else {
|
|
2682
2862
|
return JSON.stringify(obj)
|
|
2683
2863
|
}
|
|
2684
2864
|
};
|
|
2685
2865
|
url += `?${paramStr(params)}&${paramStr(data)}&${method}`;
|
|
2686
2866
|
return url
|
|
2687
|
-
}
|
|
2867
|
+
}
|
|
2688
2868
|
|
|
2689
|
-
|
|
2690
|
-
|
|
2869
|
+
/**
|
|
2870
|
+
* Session管理工具
|
|
2871
|
+
* 提供localStorage和sessionStorage的封装,支持系统标识前缀
|
|
2872
|
+
*/
|
|
2873
|
+
|
|
2874
|
+
let systemFlag = null;
|
|
2691
2875
|
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2876
|
+
/**
|
|
2877
|
+
* 设置系统标识
|
|
2878
|
+
* @param {string} flag - 系统标识
|
|
2879
|
+
* @returns {string} 系统标识
|
|
2880
|
+
*/
|
|
2881
|
+
function setSystemKey(flag) {
|
|
2882
|
+
systemFlag = flag;
|
|
2883
|
+
return systemFlag
|
|
2695
2884
|
}
|
|
2696
2885
|
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2886
|
+
/**
|
|
2887
|
+
* 获取带系统标识的key
|
|
2888
|
+
* @param {string} key - 原始key
|
|
2889
|
+
* @returns {string} 带系统标识的key
|
|
2890
|
+
*/
|
|
2891
|
+
function getSystemKey(key) {
|
|
2892
|
+
if (!systemFlag) {
|
|
2893
|
+
console.warn('系统标识未设置,请先调用 setSystemKey()');
|
|
2894
|
+
return key
|
|
2701
2895
|
}
|
|
2702
|
-
return `${
|
|
2896
|
+
return `${systemFlag}_${key}`
|
|
2703
2897
|
}
|
|
2704
2898
|
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2899
|
+
/**
|
|
2900
|
+
* 移除localStorage中的session
|
|
2901
|
+
* @param {string} key - 键名,默认'session'
|
|
2902
|
+
*/
|
|
2903
|
+
function removeSession(key = 'session') {
|
|
2904
|
+
if (typeof window === 'undefined' || !window.localStorage) {
|
|
2905
|
+
console.warn('localStorage不可用');
|
|
2906
|
+
return
|
|
2710
2907
|
}
|
|
2711
|
-
localStorage.removeItem(
|
|
2908
|
+
localStorage.removeItem(getSystemKey(key));
|
|
2712
2909
|
}
|
|
2713
2910
|
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2911
|
+
/**
|
|
2912
|
+
* 设置localStorage中的session
|
|
2913
|
+
* @param {*} session - 要存储的数据
|
|
2914
|
+
* @param {string} key - 键名,默认'session'
|
|
2915
|
+
*/
|
|
2916
|
+
function setSession(session, key = 'session') {
|
|
2917
|
+
if (typeof window === 'undefined' || !window.localStorage) {
|
|
2918
|
+
console.warn('localStorage不可用');
|
|
2919
|
+
return
|
|
2718
2920
|
}
|
|
2719
|
-
localStorage.setItem(
|
|
2921
|
+
localStorage.setItem(getSystemKey(key), JSON.stringify(session));
|
|
2720
2922
|
}
|
|
2721
2923
|
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2924
|
+
/**
|
|
2925
|
+
* 获取localStorage中的session
|
|
2926
|
+
* @param {string} key - 键名,默认'session'
|
|
2927
|
+
* @returns {*} 存储的数据
|
|
2928
|
+
*/
|
|
2929
|
+
function getSession(key = 'session') {
|
|
2930
|
+
if (typeof window === 'undefined' || !window.localStorage) {
|
|
2931
|
+
console.warn('localStorage不可用');
|
|
2932
|
+
return null
|
|
2726
2933
|
}
|
|
2727
|
-
const value = localStorage.getItem(
|
|
2728
|
-
if (value === null || value === undefined || value ===
|
|
2729
|
-
return value
|
|
2730
|
-
} else if (value ===
|
|
2731
|
-
return undefined
|
|
2934
|
+
const value = localStorage.getItem(getSystemKey(key));
|
|
2935
|
+
if (value === null || value === undefined || value === '') {
|
|
2936
|
+
return value
|
|
2937
|
+
} else if (value === 'undefined') {
|
|
2938
|
+
return undefined
|
|
2732
2939
|
} else {
|
|
2733
2940
|
try {
|
|
2734
|
-
return JSON.parse(value)
|
|
2941
|
+
return JSON.parse(value)
|
|
2735
2942
|
} catch (e) {
|
|
2736
|
-
console.error(
|
|
2737
|
-
return value
|
|
2943
|
+
console.error('解析session失败:', e);
|
|
2944
|
+
return value
|
|
2738
2945
|
}
|
|
2739
2946
|
}
|
|
2740
2947
|
}
|
|
2741
2948
|
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2949
|
+
/**
|
|
2950
|
+
* 扩展session中的部分属性,而不是全量替换
|
|
2951
|
+
* @param {Object} patch - 要扩展的属性
|
|
2952
|
+
* @param {string} key - 键名,默认'session'
|
|
2953
|
+
* @returns {*} 更新后的session
|
|
2954
|
+
*/
|
|
2955
|
+
function extendSession(patch, key = 'session') {
|
|
2956
|
+
if (typeof window === 'undefined' || !window.localStorage) {
|
|
2957
|
+
console.warn('localStorage不可用');
|
|
2958
|
+
return null
|
|
2747
2959
|
}
|
|
2748
2960
|
const prev = getSession(key);
|
|
2749
|
-
if (typeof prev ===
|
|
2961
|
+
if (typeof prev === 'object' && prev !== null) {
|
|
2750
2962
|
localStorage.setItem(
|
|
2751
|
-
|
|
2963
|
+
getSystemKey(key),
|
|
2752
2964
|
JSON.stringify({ ...prev, ...patch })
|
|
2753
2965
|
);
|
|
2754
2966
|
}
|
|
2755
|
-
return getSession(key)
|
|
2967
|
+
return getSession(key)
|
|
2756
2968
|
}
|
|
2757
2969
|
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2970
|
+
/**
|
|
2971
|
+
* 扩展session中的部分属性(向后兼容,拼写错误但保留)
|
|
2972
|
+
* @param {Object} patch - 要扩展的属性
|
|
2973
|
+
* @param {string} key - 键名,默认'session'
|
|
2974
|
+
* @returns {*} 更新后的session
|
|
2975
|
+
* @deprecated 请使用 extendSession,此函数保留仅为向后兼容
|
|
2976
|
+
*/
|
|
2977
|
+
const extentSession = extendSession;
|
|
2978
|
+
|
|
2979
|
+
/**
|
|
2980
|
+
* 设置sessionStorage
|
|
2981
|
+
* @param {*} session - 要存储的数据
|
|
2982
|
+
* @param {string} key - 键名,默认'collapse'
|
|
2983
|
+
*/
|
|
2984
|
+
function setSessionStorage(session, key = 'collapse') {
|
|
2985
|
+
if (typeof window === 'undefined' || !window.sessionStorage) {
|
|
2986
|
+
console.warn('sessionStorage不可用');
|
|
2987
|
+
return
|
|
2762
2988
|
}
|
|
2763
|
-
sessionStorage.setItem(
|
|
2989
|
+
sessionStorage.setItem(getSystemKey(key), JSON.stringify(session));
|
|
2764
2990
|
}
|
|
2765
2991
|
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2992
|
+
/**
|
|
2993
|
+
* 获取sessionStorage
|
|
2994
|
+
* @param {string} key - 键名,默认'collapse'
|
|
2995
|
+
* @returns {*} 存储的数据
|
|
2996
|
+
*/
|
|
2997
|
+
function getSessionStorage(key = 'collapse') {
|
|
2998
|
+
if (typeof window === 'undefined' || !window.sessionStorage) {
|
|
2999
|
+
console.warn('sessionStorage不可用');
|
|
3000
|
+
return null
|
|
2770
3001
|
}
|
|
2771
|
-
const value = sessionStorage.getItem(
|
|
2772
|
-
if (value === null || value === undefined || value ===
|
|
2773
|
-
return value
|
|
2774
|
-
} else if (value ===
|
|
2775
|
-
return undefined
|
|
3002
|
+
const value = sessionStorage.getItem(getSystemKey(key));
|
|
3003
|
+
if (value === null || value === undefined || value === '') {
|
|
3004
|
+
return value
|
|
3005
|
+
} else if (value === 'undefined') {
|
|
3006
|
+
return undefined
|
|
2776
3007
|
} else {
|
|
2777
3008
|
try {
|
|
2778
|
-
return JSON.parse(value)
|
|
3009
|
+
return JSON.parse(value)
|
|
2779
3010
|
} catch (e) {
|
|
2780
|
-
console.error(
|
|
2781
|
-
return value
|
|
3011
|
+
console.error('解析sessionStorage失败:', e);
|
|
3012
|
+
return value
|
|
2782
3013
|
}
|
|
2783
3014
|
}
|
|
2784
3015
|
}
|
|
2785
3016
|
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
3017
|
+
/**
|
|
3018
|
+
* 移除sessionStorage
|
|
3019
|
+
* @param {string} key - 键名,默认'collapse'
|
|
3020
|
+
*/
|
|
3021
|
+
function removeSessionStorage(key = 'collapse') {
|
|
3022
|
+
if (typeof window === 'undefined' || !window.sessionStorage) {
|
|
3023
|
+
console.warn('sessionStorage不可用');
|
|
3024
|
+
return
|
|
2790
3025
|
}
|
|
2791
|
-
sessionStorage.removeItem(
|
|
3026
|
+
sessionStorage.removeItem(getSystemKey(key));
|
|
2792
3027
|
}
|
|
2793
3028
|
|
|
2794
|
-
|
|
3029
|
+
/**
|
|
3030
|
+
* 图片URL处理工具
|
|
3031
|
+
* 注意:需要配置 GLOBAL_CONFIG.backend_server
|
|
3032
|
+
*/
|
|
3033
|
+
|
|
3034
|
+
/**
|
|
3035
|
+
* 获取图片URL
|
|
3036
|
+
* @param {string} url - 图片路径
|
|
3037
|
+
* @returns {string} 完整的图片URL
|
|
3038
|
+
*/
|
|
2795
3039
|
function getImgURL(url) {
|
|
2796
3040
|
if (!url) return ''
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
3041
|
+
|
|
3042
|
+
// 如果已经是完整的URL或data URL,直接返回
|
|
3043
|
+
if (/^http/g.test(url) || /^data:image/g.test(url)) {
|
|
3044
|
+
return url
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3047
|
+
// 获取基础URL
|
|
3048
|
+
const backendServer =
|
|
3049
|
+
typeof window !== 'undefined' && window.GLOBAL_CONFIG
|
|
3050
|
+
? window.GLOBAL_CONFIG.backend_server
|
|
2800
3051
|
: '';
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
3052
|
+
|
|
3053
|
+
if (!backendServer) {
|
|
3054
|
+
console.warn('未配置 backend_server,返回原始URL');
|
|
3055
|
+
return url
|
|
2804
3056
|
}
|
|
2805
|
-
|
|
3057
|
+
|
|
3058
|
+
return `${backendServer}/_uploads/files/${url}`
|
|
2806
3059
|
}
|
|
2807
3060
|
|
|
2808
|
-
|
|
3061
|
+
/**
|
|
3062
|
+
* 获取量表图片URL
|
|
3063
|
+
* @param {string} url - 图片路径
|
|
3064
|
+
* @returns {string} 完整的图片URL
|
|
3065
|
+
*/
|
|
2809
3066
|
function getGaugeImgUrl(url) {
|
|
2810
3067
|
if (!url) return ''
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
3068
|
+
|
|
3069
|
+
// 如果已经是完整的URL或data URL,直接返回
|
|
3070
|
+
if (/^http/g.test(url) || /^data:image/g.test(url)) {
|
|
3071
|
+
return url
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
// 获取基础URL
|
|
3075
|
+
const backendServer =
|
|
3076
|
+
typeof window !== 'undefined' && window.GLOBAL_CONFIG
|
|
3077
|
+
? window.GLOBAL_CONFIG.backend_server
|
|
2814
3078
|
: '';
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
3079
|
+
|
|
3080
|
+
if (!backendServer) {
|
|
3081
|
+
console.warn('未配置 backend_server,返回原始URL');
|
|
3082
|
+
return url
|
|
2818
3083
|
}
|
|
2819
|
-
|
|
3084
|
+
|
|
3085
|
+
return `${backendServer}/api/v1/ma/mobile/resource/local/files?file=${url}`
|
|
2820
3086
|
}
|
|
2821
3087
|
|
|
2822
|
-
|
|
2823
|
-
|
|
3088
|
+
/**
|
|
3089
|
+
* 获取医生头像URL
|
|
3090
|
+
* @param {string} url - 图片路径
|
|
3091
|
+
* @param {string} defaultUrl - 默认头像URL
|
|
3092
|
+
* @returns {string} 完整的图片URL
|
|
3093
|
+
*/
|
|
3094
|
+
function doctorHeadImg(url, defaultUrl = './img/doc_defalut_male.jpg') {
|
|
2824
3095
|
if (!url) {
|
|
2825
|
-
return
|
|
3096
|
+
return defaultUrl
|
|
2826
3097
|
}
|
|
2827
|
-
|
|
2828
|
-
|
|
3098
|
+
|
|
3099
|
+
// 如果已经是完整的URL或data URL,直接返回
|
|
3100
|
+
if (/^http/g.test(url) || /^data:image/g.test(url)) {
|
|
3101
|
+
return url
|
|
2829
3102
|
}
|
|
2830
|
-
|
|
3103
|
+
|
|
3104
|
+
// 使用OSS基础URL
|
|
3105
|
+
return `https://annetinfo1.oss-cn-shenzhen.aliyuncs.com/${url}`
|
|
2831
3106
|
}
|
|
2832
3107
|
|
|
2833
3108
|
/**
|
|
@@ -2848,19 +3123,21 @@ function isTablet() {
|
|
|
2848
3123
|
|
|
2849
3124
|
// 检测常见的平板设备标识
|
|
2850
3125
|
const tabletPatterns = [
|
|
2851
|
-
/ipad/,
|
|
2852
|
-
/android.*tablet/,
|
|
2853
|
-
/kindle/,
|
|
2854
|
-
/silk/,
|
|
2855
|
-
/playbook/,
|
|
2856
|
-
/bb10/,
|
|
2857
|
-
/rim tablet/,
|
|
2858
|
-
/windows.*touch/,
|
|
2859
|
-
/tablet/,
|
|
3126
|
+
/ipad/, // iPad
|
|
3127
|
+
/android.*tablet/, // Android 平板
|
|
3128
|
+
/kindle/, // Kindle
|
|
3129
|
+
/silk/, // Amazon Silk
|
|
3130
|
+
/playbook/, // BlackBerry PlayBook
|
|
3131
|
+
/bb10/, // BlackBerry 10
|
|
3132
|
+
/rim tablet/, // BlackBerry Tablet OS
|
|
3133
|
+
/windows.*touch/, // Windows 平板
|
|
3134
|
+
/tablet/, // 通用平板标识
|
|
2860
3135
|
];
|
|
2861
3136
|
|
|
2862
3137
|
// 检查用户代理是否匹配平板模式
|
|
2863
|
-
const isTabletByUserAgent = tabletPatterns.some(pattern =>
|
|
3138
|
+
const isTabletByUserAgent = tabletPatterns.some(pattern =>
|
|
3139
|
+
pattern.test(userAgent)
|
|
3140
|
+
);
|
|
2864
3141
|
|
|
2865
3142
|
// 检测屏幕尺寸和触摸支持
|
|
2866
3143
|
const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
@@ -2870,19 +3147,21 @@ function isTablet() {
|
|
|
2870
3147
|
const maxDimension = Math.max(screenWidth, screenHeight);
|
|
2871
3148
|
|
|
2872
3149
|
// 平板设备的典型屏幕尺寸范围
|
|
2873
|
-
const isTabletByScreenSize =
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
3150
|
+
const isTabletByScreenSize =
|
|
3151
|
+
minDimension >= 600 &&
|
|
3152
|
+
minDimension <= 1200 &&
|
|
3153
|
+
maxDimension >= 800 &&
|
|
3154
|
+
maxDimension <= 1600;
|
|
2877
3155
|
|
|
2878
3156
|
// 检测是否为移动设备但屏幕较大(可能是平板)
|
|
2879
|
-
const isMobile =
|
|
3157
|
+
const isMobile =
|
|
3158
|
+
/android|webos|iphone|ipod|blackberry|iemobile|opera mini/i.test(userAgent);
|
|
2880
3159
|
const isLargeMobile = isMobile && minDimension >= 600;
|
|
2881
3160
|
|
|
2882
3161
|
// 综合判断:用户代理匹配 或 (屏幕尺寸符合平板特征 且 支持触摸) 或 (大屏移动设备)
|
|
2883
|
-
return
|
|
2884
|
-
|
|
2885
|
-
|
|
3162
|
+
return (
|
|
3163
|
+
isTabletByUserAgent || (isTabletByScreenSize && hasTouch) || isLargeMobile
|
|
3164
|
+
)
|
|
2886
3165
|
}
|
|
2887
3166
|
|
|
2888
3167
|
/**
|
|
@@ -2899,7 +3178,8 @@ function getDeviceType() {
|
|
|
2899
3178
|
}
|
|
2900
3179
|
|
|
2901
3180
|
const userAgent = navigator.userAgent.toLowerCase();
|
|
2902
|
-
const isMobile =
|
|
3181
|
+
const isMobile =
|
|
3182
|
+
/android|webos|iphone|ipod|blackberry|iemobile|opera mini/i.test(userAgent);
|
|
2903
3183
|
|
|
2904
3184
|
return isMobile ? 'mobile' : 'desktop'
|
|
2905
3185
|
}
|
|
@@ -2929,7 +3209,7 @@ function getDeviceInfo() {
|
|
|
2929
3209
|
screenWidth: 0,
|
|
2930
3210
|
screenHeight: 0,
|
|
2931
3211
|
devicePixelRatio: 1,
|
|
2932
|
-
orientation: 'unknown'
|
|
3212
|
+
orientation: 'unknown',
|
|
2933
3213
|
}
|
|
2934
3214
|
}
|
|
2935
3215
|
|
|
@@ -2941,29 +3221,24 @@ function getDeviceInfo() {
|
|
|
2941
3221
|
screenWidth: window.screen.width,
|
|
2942
3222
|
screenHeight: window.screen.height,
|
|
2943
3223
|
devicePixelRatio: window.devicePixelRatio || 1,
|
|
2944
|
-
orientation: screen.orientation ? screen.orientation.type : 'unknown'
|
|
3224
|
+
orientation: screen.orientation ? screen.orientation.type : 'unknown',
|
|
2945
3225
|
}
|
|
2946
3226
|
}
|
|
2947
3227
|
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
let param = 'school=gongda&hobby=skating&number=3'
|
|
2954
|
-
let jsonObj = queryToObj(param)
|
|
3228
|
+
/**
|
|
3229
|
+
* URL参数处理工具
|
|
3230
|
+
* 注意:部分函数依赖浏览器环境(window.location)
|
|
3231
|
+
*/
|
|
2955
3232
|
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
}
|
|
2962
|
-
*/
|
|
3233
|
+
/**
|
|
3234
|
+
* 将查询字符串转换为对象
|
|
3235
|
+
* @param {string} str - 查询字符串,如 'a=1&b=2'
|
|
3236
|
+
* @returns {Object} 对象,如 {a: '1', b: '2'}
|
|
3237
|
+
*/
|
|
2963
3238
|
function queryToObj(str) {
|
|
2964
|
-
|
|
3239
|
+
const theRequest = {};
|
|
2965
3240
|
if (str) {
|
|
2966
|
-
|
|
3241
|
+
const strs = str.includes('&') ? str.split('&') : ('&' + str).split('&');
|
|
2967
3242
|
for (let i = 0; i < strs.length; i++) {
|
|
2968
3243
|
if (strs[i].includes('=')) {
|
|
2969
3244
|
const parts = strs[i].split('=');
|
|
@@ -2974,11 +3249,11 @@ function queryToObj(str) {
|
|
|
2974
3249
|
return theRequest
|
|
2975
3250
|
}
|
|
2976
3251
|
|
|
2977
|
-
|
|
2978
|
-
*
|
|
2979
|
-
*
|
|
2980
|
-
*
|
|
2981
|
-
|
|
3252
|
+
/**
|
|
3253
|
+
* 将对象转换为查询字符串
|
|
3254
|
+
* @param {Object} obj - 对象,如 {a: 1, b: 2}
|
|
3255
|
+
* @returns {string} 查询字符串,如 'a=1&b=2'
|
|
3256
|
+
*/
|
|
2982
3257
|
function toQueryPair(key, value) {
|
|
2983
3258
|
if (typeof value == 'undefined') {
|
|
2984
3259
|
return key
|
|
@@ -2987,37 +3262,31 @@ function toQueryPair(key, value) {
|
|
|
2987
3262
|
}
|
|
2988
3263
|
|
|
2989
3264
|
function toQueryString(obj) {
|
|
2990
|
-
|
|
2991
|
-
for (
|
|
2992
|
-
|
|
2993
|
-
|
|
3265
|
+
const ret = [];
|
|
3266
|
+
for (const key in obj) {
|
|
3267
|
+
const encodedKey = encodeURIComponent(key);
|
|
3268
|
+
const values = obj[key];
|
|
2994
3269
|
if (values && values.constructor == Array) {
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
for (
|
|
3270
|
+
// 数组
|
|
3271
|
+
const queryValues = [];
|
|
3272
|
+
for (let i = 0, len = values.length, value; i < len; i++) {
|
|
2998
3273
|
value = values[i];
|
|
2999
|
-
queryValues.push(toQueryPair(
|
|
3274
|
+
queryValues.push(toQueryPair(encodedKey, value));
|
|
3000
3275
|
}
|
|
3001
3276
|
ret = ret.concat(queryValues);
|
|
3002
3277
|
} else {
|
|
3003
|
-
|
|
3004
|
-
ret.push(toQueryPair(
|
|
3278
|
+
// 字符串
|
|
3279
|
+
ret.push(toQueryPair(encodedKey, values));
|
|
3005
3280
|
}
|
|
3006
3281
|
}
|
|
3007
3282
|
return ret.join('&')
|
|
3008
3283
|
}
|
|
3009
3284
|
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
用法2:
|
|
3017
|
-
let para = urlToJson('https://www.baidu.com?a=1&b=2')
|
|
3018
|
-
console.log(para)
|
|
3019
|
-
|
|
3020
|
-
* */
|
|
3285
|
+
/**
|
|
3286
|
+
* URL转JSON对象
|
|
3287
|
+
* @param {string} selfUrl - URL字符串,如果不传则使用当前页面URL
|
|
3288
|
+
* @returns {Object} {paramStr: string, paramJson: Object}
|
|
3289
|
+
*/
|
|
3021
3290
|
function urlToJson(selfUrl) {
|
|
3022
3291
|
if (typeof window === 'undefined') {
|
|
3023
3292
|
return { paramStr: '', paramJson: {} }
|
|
@@ -3028,47 +3297,40 @@ function urlToJson(selfUrl) {
|
|
|
3028
3297
|
const urlMatch = url.match(reg);
|
|
3029
3298
|
|
|
3030
3299
|
// 匹配去掉?的纯参数(正则精髓,贪婪永远匹配最后一个?后的参数)
|
|
3031
|
-
const param =
|
|
3300
|
+
const param =
|
|
3301
|
+
urlMatch && urlMatch.length ? urlMatch[0].replace(/^\?*.*\?/, '') : '';
|
|
3032
3302
|
|
|
3033
3303
|
const output = {
|
|
3034
3304
|
paramStr: param,
|
|
3035
|
-
paramJson: queryToObj(param)
|
|
3305
|
+
paramJson: queryToObj(param),
|
|
3036
3306
|
};
|
|
3037
3307
|
|
|
3038
3308
|
return output
|
|
3039
3309
|
}
|
|
3040
3310
|
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
* */
|
|
3311
|
+
/**
|
|
3312
|
+
* 获取URL中的指定参数
|
|
3313
|
+
* @param {string} name - 参数名
|
|
3314
|
+
* @param {string} url - URL字符串,如果不传则使用当前页面URL
|
|
3315
|
+
* @returns {string} 参数值
|
|
3316
|
+
*/
|
|
3048
3317
|
function getQueryString(name, url) {
|
|
3049
3318
|
if (typeof window === 'undefined') {
|
|
3050
3319
|
return ''
|
|
3051
3320
|
}
|
|
3052
3321
|
url = url || window.location.href;
|
|
3053
|
-
|
|
3322
|
+
const str = url.match(
|
|
3323
|
+
new RegExp('([?&#])' + name.replace('#', '') + '=([^#&?]*)', 'gi')
|
|
3324
|
+
);
|
|
3054
3325
|
return str ? decodeURIComponent(str[0].split('=')[1]) : ''
|
|
3055
3326
|
}
|
|
3056
3327
|
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
用法一:
|
|
3064
|
-
let resultUrl = setUrl({id: 1, name: '测试页面'})
|
|
3065
|
-
console.log(resultUrl) // 输出:https://********.html?id=1&name=测试页面
|
|
3066
|
-
|
|
3067
|
-
用法二:
|
|
3068
|
-
let resultUrl = setUrl({id: 3, name: '哈哈哈'}, 'https://www.baidu.com')
|
|
3069
|
-
console.log(resultUrl) // 输出:https://www.baidu.com?id=3&name=哈哈哈
|
|
3070
|
-
|
|
3071
|
-
* */
|
|
3328
|
+
/**
|
|
3329
|
+
* 设置URL参数,返回新的URL
|
|
3330
|
+
* @param {Object} json - 要设置的参数对象
|
|
3331
|
+
* @param {string} originUrl - 原始URL,如果不传则使用当前页面URL
|
|
3332
|
+
* @returns {string} 新的URL
|
|
3333
|
+
*/
|
|
3072
3334
|
function setUrl(json, originUrl = '') {
|
|
3073
3335
|
if (typeof window === 'undefined') {
|
|
3074
3336
|
return ''
|
|
@@ -3077,21 +3339,27 @@ function setUrl(json, originUrl = '') {
|
|
|
3077
3339
|
// 新的参数
|
|
3078
3340
|
let newJson = {
|
|
3079
3341
|
...paramJson,
|
|
3080
|
-
...json
|
|
3342
|
+
...json,
|
|
3081
3343
|
};
|
|
3082
3344
|
|
|
3083
3345
|
// 参数对象 =》get参数
|
|
3084
3346
|
let paramStr = toQueryString(newJson);
|
|
3085
3347
|
|
|
3086
3348
|
// url的origin + pathname
|
|
3087
|
-
let oPath = originUrl
|
|
3349
|
+
let oPath = originUrl
|
|
3350
|
+
? originUrl
|
|
3351
|
+
: window.location.origin + window.location.pathname;
|
|
3088
3352
|
|
|
3089
3353
|
let resultUrl = oPath + '?' + paramStr;
|
|
3090
3354
|
return resultUrl
|
|
3091
3355
|
}
|
|
3092
3356
|
|
|
3093
|
-
|
|
3094
|
-
|
|
3357
|
+
/**
|
|
3358
|
+
* 获取URL参数
|
|
3359
|
+
* @param {string} url - URL字符串,默认使用当前页面URL
|
|
3360
|
+
* @returns {Object} 参数对象
|
|
3361
|
+
*/
|
|
3362
|
+
function getUrlParams(url) {
|
|
3095
3363
|
if (typeof window === 'undefined') {
|
|
3096
3364
|
return {}
|
|
3097
3365
|
}
|
|
@@ -3099,12 +3367,28 @@ function get_url_params(url) {
|
|
|
3099
3367
|
return urlToJson(targetUrl).paramJson
|
|
3100
3368
|
}
|
|
3101
3369
|
|
|
3370
|
+
/**
|
|
3371
|
+
* 通用工具函数
|
|
3372
|
+
*/
|
|
3373
|
+
|
|
3374
|
+
/**
|
|
3375
|
+
* 将毫秒转换为时分秒
|
|
3376
|
+
* @param {number} ms - 毫秒数
|
|
3377
|
+
* @param {boolean} secondsTo2decimal - 秒数是否保留两位小数,默认false
|
|
3378
|
+
* @returns {Object} {hours: number, minutes: number, seconds: number}
|
|
3379
|
+
*/
|
|
3102
3380
|
function convertMilliseconds(ms, secondsTo2decimal = false) {
|
|
3103
|
-
if (!ms)
|
|
3381
|
+
if (!ms)
|
|
3382
|
+
return {
|
|
3383
|
+
hours: 0,
|
|
3384
|
+
minutes: 0,
|
|
3385
|
+
seconds: 0,
|
|
3386
|
+
}
|
|
3104
3387
|
|
|
3105
3388
|
const hours = Math.floor(ms / 3600000); // 计算小时
|
|
3106
3389
|
const minutes = Math.floor((ms % 3600000) / 60000); // 计算分钟
|
|
3107
3390
|
let seconds = 0;
|
|
3391
|
+
|
|
3108
3392
|
if (secondsTo2decimal) {
|
|
3109
3393
|
seconds = parseFloat(((ms % 60000) / 1000).toFixed(2));
|
|
3110
3394
|
} else {
|
|
@@ -3115,46 +3399,106 @@ function convertMilliseconds(ms, secondsTo2decimal = false) {
|
|
|
3115
3399
|
hours,
|
|
3116
3400
|
minutes,
|
|
3117
3401
|
seconds,
|
|
3118
|
-
}
|
|
3402
|
+
}
|
|
3119
3403
|
}
|
|
3120
3404
|
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3405
|
+
/**
|
|
3406
|
+
* 防抖函数
|
|
3407
|
+
* @param {Function} fn - 要防抖的函数
|
|
3408
|
+
* @param {number} delay - 延迟时间(毫秒),默认600
|
|
3409
|
+
* @returns {Function} 防抖后的函数
|
|
3410
|
+
*/
|
|
3411
|
+
function debounce(fn, delay = 600) {
|
|
3412
|
+
let timerId = null;
|
|
3413
|
+
return function (...args) {
|
|
3414
|
+
if (timerId) clearTimeout(timerId);
|
|
3125
3415
|
timerId = setTimeout(() => {
|
|
3126
|
-
fn && fn.call(this, ...
|
|
3127
|
-
},
|
|
3128
|
-
}
|
|
3129
|
-
}
|
|
3416
|
+
fn && fn.call(this, ...args);
|
|
3417
|
+
}, delay);
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
|
|
3421
|
+
/**
|
|
3422
|
+
* 防抖函数(别名,向后兼容)
|
|
3423
|
+
* @param {Function} fn - 要防抖的函数
|
|
3424
|
+
* @param {number} delay - 延迟时间(毫秒),默认600
|
|
3425
|
+
* @returns {Function} 防抖后的函数
|
|
3426
|
+
*/
|
|
3427
|
+
const myDebounce = debounce;
|
|
3428
|
+
|
|
3429
|
+
/**
|
|
3430
|
+
* 节流函数
|
|
3431
|
+
* @param {Function} fn - 要节流的函数
|
|
3432
|
+
* @param {number} delay - 延迟时间(毫秒),默认100
|
|
3433
|
+
* @returns {Function} 节流后的函数
|
|
3434
|
+
*/
|
|
3435
|
+
function throttle(fn, delay = 100) {
|
|
3436
|
+
let timer = null;
|
|
3437
|
+
let startTime = Date.now();
|
|
3438
|
+
|
|
3439
|
+
return function (...args) {
|
|
3440
|
+
const currentTime = Date.now();
|
|
3441
|
+
const diff = delay - (currentTime - startTime);
|
|
3130
3442
|
|
|
3131
|
-
|
|
3443
|
+
if (timer) clearTimeout(timer);
|
|
3444
|
+
|
|
3445
|
+
if (diff <= 0) {
|
|
3446
|
+
fn.apply(this, args);
|
|
3447
|
+
startTime = Date.now();
|
|
3448
|
+
} else {
|
|
3449
|
+
timer = setTimeout(() => {
|
|
3450
|
+
fn.apply(this, args);
|
|
3451
|
+
}, diff);
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
|
|
3456
|
+
/**
|
|
3457
|
+
* Map序列化的replacer函数(用于JSON.stringify)
|
|
3458
|
+
* @param {string} key - 键名
|
|
3459
|
+
* @param {*} value - 值
|
|
3460
|
+
* @returns {*} 处理后的值
|
|
3461
|
+
*/
|
|
3462
|
+
function mapStringifyReplacer(key, value) {
|
|
3132
3463
|
if (value instanceof Map) {
|
|
3133
3464
|
return {
|
|
3134
|
-
dataType:
|
|
3465
|
+
dataType: 'Map',
|
|
3135
3466
|
value: Array.from(value.entries()),
|
|
3136
|
-
}
|
|
3467
|
+
}
|
|
3137
3468
|
} else {
|
|
3138
|
-
return value
|
|
3469
|
+
return value
|
|
3139
3470
|
}
|
|
3140
|
-
}
|
|
3471
|
+
}
|
|
3141
3472
|
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3473
|
+
/**
|
|
3474
|
+
* Map反序列化的reviver函数(用于JSON.parse)
|
|
3475
|
+
* @param {string} key - 键名
|
|
3476
|
+
* @param {*} value - 值
|
|
3477
|
+
* @returns {*} 处理后的值
|
|
3478
|
+
*/
|
|
3479
|
+
function mapParseReviver(key, value) {
|
|
3480
|
+
if (typeof value === 'object' && value !== null) {
|
|
3481
|
+
if (value.dataType === 'Map') {
|
|
3482
|
+
return new Map(value.value)
|
|
3146
3483
|
}
|
|
3147
3484
|
}
|
|
3148
|
-
return value
|
|
3149
|
-
}
|
|
3485
|
+
return value
|
|
3486
|
+
}
|
|
3150
3487
|
|
|
3488
|
+
/**
|
|
3489
|
+
* 获取指定月数前的今天
|
|
3490
|
+
* @param {number} reduce - 减少的月数,默认12
|
|
3491
|
+
* @returns {number} 时间戳
|
|
3492
|
+
*/
|
|
3151
3493
|
function getHalfYearAgoToday(reduce = 12) {
|
|
3152
3494
|
const today = new Date().setHours(0, 0, 0, 0);
|
|
3153
3495
|
const currentMonth = new Date().getMonth();
|
|
3154
|
-
return new Date(today).setMonth(currentMonth - reduce)
|
|
3496
|
+
return new Date(today).setMonth(currentMonth - reduce)
|
|
3155
3497
|
}
|
|
3156
3498
|
|
|
3157
|
-
|
|
3499
|
+
/**
|
|
3500
|
+
* 文件下载助手
|
|
3501
|
+
*/
|
|
3158
3502
|
const fileDownloadHelper = {
|
|
3159
3503
|
/**
|
|
3160
3504
|
* 下载文件
|
|
@@ -3162,14 +3506,15 @@ const fileDownloadHelper = {
|
|
|
3162
3506
|
* @param {string} filename - 下载文件名
|
|
3163
3507
|
*/
|
|
3164
3508
|
download(blob, filename) {
|
|
3165
|
-
if (typeof window ===
|
|
3166
|
-
console.warn(
|
|
3167
|
-
return
|
|
3509
|
+
if (typeof window === 'undefined') {
|
|
3510
|
+
console.warn('fileDownloadHelper.download只能在浏览器环境使用');
|
|
3511
|
+
return
|
|
3168
3512
|
}
|
|
3513
|
+
|
|
3169
3514
|
const url = window.URL.createObjectURL(blob);
|
|
3170
|
-
const link = document.createElement(
|
|
3515
|
+
const link = document.createElement('a');
|
|
3171
3516
|
link.href = url;
|
|
3172
|
-
link.setAttribute(
|
|
3517
|
+
link.setAttribute('download', filename);
|
|
3173
3518
|
document.body.appendChild(link);
|
|
3174
3519
|
link.click();
|
|
3175
3520
|
window.URL.revokeObjectURL(url);
|
|
@@ -3183,93 +3528,69 @@ const fileDownloadHelper = {
|
|
|
3183
3528
|
* @returns {string} 文件名
|
|
3184
3529
|
*/
|
|
3185
3530
|
getFilenameFromHeaders(headers, defaultName) {
|
|
3186
|
-
const contentDisposition = headers[
|
|
3187
|
-
if (!contentDisposition) return defaultName
|
|
3531
|
+
const contentDisposition = headers['content-disposition'];
|
|
3532
|
+
if (!contentDisposition) return defaultName
|
|
3188
3533
|
|
|
3189
3534
|
const filenameMatch = contentDisposition.match(/filename=(.+)/);
|
|
3190
3535
|
return filenameMatch && filenameMatch.length > 1
|
|
3191
|
-
? decodeURIComponent(filenameMatch[1]).replace(/['"]/g,
|
|
3192
|
-
: defaultName
|
|
3536
|
+
? decodeURIComponent(filenameMatch[1]).replace(/['"]/g, '')
|
|
3537
|
+
: defaultName
|
|
3193
3538
|
},
|
|
3194
3539
|
};
|
|
3195
3540
|
|
|
3196
3541
|
/**
|
|
3197
|
-
*
|
|
3542
|
+
* 复制文本到剪贴板(Promise版本)
|
|
3198
3543
|
* @param {string} text - 要复制的文本
|
|
3199
|
-
* @returns {Promise<boolean>}
|
|
3544
|
+
* @returns {Promise<boolean>} 复制成功返回true,失败返回false
|
|
3200
3545
|
*/
|
|
3201
3546
|
async function copyText(text) {
|
|
3202
|
-
if (typeof window ===
|
|
3203
|
-
console.warn(
|
|
3204
|
-
return false
|
|
3547
|
+
if (typeof window === 'undefined') {
|
|
3548
|
+
console.warn('copyText只能在浏览器环境使用');
|
|
3549
|
+
return false
|
|
3205
3550
|
}
|
|
3206
3551
|
|
|
3207
3552
|
try {
|
|
3208
3553
|
// 优先使用现代的Clipboard API
|
|
3209
3554
|
if (navigator.clipboard && window.isSecureContext) {
|
|
3210
3555
|
await navigator.clipboard.writeText(text);
|
|
3211
|
-
return true
|
|
3556
|
+
return true
|
|
3212
3557
|
}
|
|
3213
3558
|
|
|
3214
3559
|
// 降级方案:使用传统的document.execCommand方法
|
|
3215
|
-
const textArea = document.createElement(
|
|
3560
|
+
const textArea = document.createElement('textarea');
|
|
3216
3561
|
textArea.value = text;
|
|
3217
3562
|
|
|
3218
3563
|
// 防止页面滚动
|
|
3219
|
-
textArea.style.position =
|
|
3220
|
-
textArea.style.left =
|
|
3221
|
-
textArea.style.top =
|
|
3564
|
+
textArea.style.position = 'fixed';
|
|
3565
|
+
textArea.style.left = '-999999px';
|
|
3566
|
+
textArea.style.top = '-999999px';
|
|
3222
3567
|
|
|
3223
3568
|
document.body.appendChild(textArea);
|
|
3224
3569
|
textArea.focus();
|
|
3225
3570
|
textArea.select();
|
|
3226
3571
|
|
|
3227
3572
|
try {
|
|
3228
|
-
const successful = document.execCommand(
|
|
3573
|
+
const successful = document.execCommand('copy');
|
|
3229
3574
|
document.body.removeChild(textArea);
|
|
3230
|
-
return successful
|
|
3575
|
+
return successful
|
|
3231
3576
|
} catch (err) {
|
|
3232
3577
|
document.body.removeChild(textArea);
|
|
3233
|
-
return false
|
|
3578
|
+
return false
|
|
3234
3579
|
}
|
|
3235
3580
|
} catch (err) {
|
|
3236
|
-
return false
|
|
3581
|
+
return false
|
|
3237
3582
|
}
|
|
3238
3583
|
}
|
|
3239
3584
|
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
return function () {
|
|
3244
|
-
let context = this;
|
|
3245
|
-
let args = arguments;
|
|
3246
|
-
clearTimeout(timeout);
|
|
3247
|
-
timeout = setTimeout(() => {
|
|
3248
|
-
fn.apply(context, args);
|
|
3249
|
-
}, delay);
|
|
3250
|
-
};
|
|
3251
|
-
}
|
|
3252
|
-
|
|
3253
|
-
// 节流
|
|
3254
|
-
const throttle = (fn, delay = 100) => {
|
|
3255
|
-
let timer = null;
|
|
3256
|
-
let start_time = Date.now();
|
|
3257
|
-
return function (...args) {
|
|
3258
|
-
const current_time = Date.now();
|
|
3259
|
-
const diff = delay - (current_time - start_time);
|
|
3260
|
-
if (timer) clearTimeout(timer);
|
|
3261
|
-
if (diff <= 0) {
|
|
3262
|
-
fn.apply(this, args);
|
|
3263
|
-
start_time = Date.now();
|
|
3264
|
-
} else {
|
|
3265
|
-
timer = setTimeout(() => {
|
|
3266
|
-
fn.apply(this, args);
|
|
3267
|
-
}, diff);
|
|
3268
|
-
}
|
|
3269
|
-
};
|
|
3270
|
-
};
|
|
3585
|
+
/**
|
|
3586
|
+
* UUID生成工具
|
|
3587
|
+
*/
|
|
3271
3588
|
|
|
3272
|
-
|
|
3589
|
+
/**
|
|
3590
|
+
* 生成短UUID
|
|
3591
|
+
* @returns {string} UUID字符串
|
|
3592
|
+
*/
|
|
3593
|
+
function uuid() {
|
|
3273
3594
|
let random;
|
|
3274
3595
|
|
|
3275
3596
|
try {
|
|
@@ -3286,24 +3607,46 @@ const uuid = function () {
|
|
|
3286
3607
|
}
|
|
3287
3608
|
|
|
3288
3609
|
return random.toString(36)
|
|
3289
|
-
}
|
|
3610
|
+
}
|
|
3290
3611
|
|
|
3291
|
-
|
|
3612
|
+
/**
|
|
3613
|
+
* 生成长UUID(由三个短UUID拼接)
|
|
3614
|
+
* @returns {string} 长UUID字符串
|
|
3615
|
+
*/
|
|
3616
|
+
function uuidLong() {
|
|
3292
3617
|
return `${uuid()}${uuid()}${uuid()}`
|
|
3293
|
-
}
|
|
3294
|
-
|
|
3295
|
-
function baseGet(object, path) {
|
|
3296
|
-
path = castPath(path);
|
|
3618
|
+
}
|
|
3297
3619
|
|
|
3298
|
-
|
|
3299
|
-
|
|
3620
|
+
/**
|
|
3621
|
+
* 对象属性获取工具
|
|
3622
|
+
* 类似lodash的get方法,支持路径字符串和数组
|
|
3623
|
+
*/
|
|
3300
3624
|
|
|
3301
|
-
|
|
3302
|
-
|
|
3625
|
+
/**
|
|
3626
|
+
* 将字符串路径转换为数组
|
|
3627
|
+
* @param {string} string - 路径字符串,如 'a.b[0].c'
|
|
3628
|
+
* @returns {Array} 路径数组
|
|
3629
|
+
*/
|
|
3630
|
+
function stringToPath(string) {
|
|
3631
|
+
const result = [];
|
|
3632
|
+
if (string.charCodeAt(0) === 46 /* . */) {
|
|
3633
|
+
result.push('');
|
|
3303
3634
|
}
|
|
3304
|
-
|
|
3635
|
+
string.replace(
|
|
3636
|
+
/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,
|
|
3637
|
+
function (match, number, quote, subString) {
|
|
3638
|
+
result.push(quote ? subString.replace(/\\(\\)?/g, '$1') : number || match);
|
|
3639
|
+
}
|
|
3640
|
+
);
|
|
3641
|
+
return result
|
|
3305
3642
|
}
|
|
3306
3643
|
|
|
3644
|
+
/**
|
|
3645
|
+
* 转换路径为数组格式
|
|
3646
|
+
* @param {string|Array} value - 路径字符串或数组
|
|
3647
|
+
* @param {Object} object - 对象(用于兼容性,未使用)
|
|
3648
|
+
* @returns {Array} 路径数组
|
|
3649
|
+
*/
|
|
3307
3650
|
function castPath(value, object) {
|
|
3308
3651
|
if (Array.isArray(value)) {
|
|
3309
3652
|
return value
|
|
@@ -3311,29 +3654,53 @@ function castPath(value, object) {
|
|
|
3311
3654
|
return stringToPath(String(value))
|
|
3312
3655
|
}
|
|
3313
3656
|
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3657
|
+
/**
|
|
3658
|
+
* 基础get方法
|
|
3659
|
+
* @param {Object} object - 对象
|
|
3660
|
+
* @param {string|Array} path - 路径
|
|
3661
|
+
* @returns {*} 获取到的值
|
|
3662
|
+
*/
|
|
3663
|
+
function baseGet(object, path) {
|
|
3664
|
+
path = castPath(path);
|
|
3665
|
+
|
|
3666
|
+
let index = 0;
|
|
3667
|
+
const length = path.length;
|
|
3668
|
+
|
|
3669
|
+
while (object != null && index < length) {
|
|
3670
|
+
object = object[path[index++]];
|
|
3318
3671
|
}
|
|
3319
|
-
|
|
3320
|
-
result.push(quote ? subString.replace(/\\(\\)?/g, '$1') : (number || match));
|
|
3321
|
-
});
|
|
3322
|
-
return result
|
|
3672
|
+
return index && index == length ? object : undefined
|
|
3323
3673
|
}
|
|
3324
3674
|
|
|
3675
|
+
/**
|
|
3676
|
+
* 获取对象属性值(支持路径)
|
|
3677
|
+
* @param {Object} object - 对象
|
|
3678
|
+
* @param {string|Array} path - 路径,如 'a.b[0].c' 或 ['a', 'b', 0, 'c']
|
|
3679
|
+
* @param {*} defaultValue - 默认值
|
|
3680
|
+
* @returns {*} 获取到的值,如果不存在则返回默认值
|
|
3681
|
+
*/
|
|
3325
3682
|
function get(object, path, defaultValue) {
|
|
3326
|
-
|
|
3683
|
+
const result = object == null ? undefined : baseGet(object, path);
|
|
3327
3684
|
return result === undefined ? defaultValue : result
|
|
3328
3685
|
}
|
|
3329
3686
|
|
|
3330
|
-
|
|
3687
|
+
/**
|
|
3688
|
+
* Axios 适配器 - 防止重复请求
|
|
3689
|
+
* @param {Object} options - 配置选项
|
|
3690
|
+
* @param {number} options.cacheTime - 缓存时间(毫秒),设置为0不清除缓存,默认0
|
|
3691
|
+
* @param {boolean} options.cache - 是否支持缓存,默认false
|
|
3692
|
+
* @param {boolean} options.noWarn - 是否禁用警告,默认false
|
|
3693
|
+
* @returns {Function} Axios 适配器函数
|
|
3694
|
+
*/
|
|
3331
3695
|
var index = (options = {}) =>
|
|
3332
|
-
async
|
|
3696
|
+
async config => {
|
|
3333
3697
|
const defaultOptions = {
|
|
3334
|
-
|
|
3698
|
+
cacheTime: 0, // 缓存时间,设置为0,不清除缓存
|
|
3699
|
+
cache: false, // 是否支持缓存
|
|
3700
|
+
noWarn: false, // 是否有提示
|
|
3335
3701
|
...options,
|
|
3336
3702
|
};
|
|
3703
|
+
|
|
3337
3704
|
const index = buildUniqueUrl(
|
|
3338
3705
|
config.url,
|
|
3339
3706
|
config.method,
|
|
@@ -3341,16 +3708,17 @@ var index = (options = {}) =>
|
|
|
3341
3708
|
config.data
|
|
3342
3709
|
);
|
|
3343
3710
|
let responsePromise = cache.get(index);
|
|
3344
|
-
|
|
3711
|
+
|
|
3712
|
+
if (!responsePromise || !defaultOptions.cache) {
|
|
3345
3713
|
responsePromise = (async () => {
|
|
3346
3714
|
try {
|
|
3347
|
-
// 需要确保axios
|
|
3715
|
+
// 需要确保axios可用
|
|
3348
3716
|
let axios = null;
|
|
3349
|
-
if (typeof window !==
|
|
3717
|
+
if (typeof window !== 'undefined' && window.axios) {
|
|
3350
3718
|
axios = window.axios;
|
|
3351
|
-
} else if (typeof require !==
|
|
3719
|
+
} else if (typeof require !== 'undefined') {
|
|
3352
3720
|
try {
|
|
3353
|
-
axios = require(
|
|
3721
|
+
axios = require('axios');
|
|
3354
3722
|
} catch (e) {
|
|
3355
3723
|
// ignore
|
|
3356
3724
|
}
|
|
@@ -3358,23 +3726,27 @@ var index = (options = {}) =>
|
|
|
3358
3726
|
|
|
3359
3727
|
if (axios && axios.defaults && axios.defaults.adapter) {
|
|
3360
3728
|
const response = await axios.defaults.adapter(config);
|
|
3361
|
-
return Promise.resolve(response)
|
|
3729
|
+
return Promise.resolve(response)
|
|
3362
3730
|
} else {
|
|
3363
|
-
throw new Error(
|
|
3731
|
+
throw new Error('axios未找到,请确保已安装axios')
|
|
3364
3732
|
}
|
|
3365
3733
|
} catch (reason) {
|
|
3366
3734
|
cache.clear(index);
|
|
3367
|
-
|
|
3735
|
+
if (defaultOptions.noWarn) reason.noWarn = true;
|
|
3736
|
+
return Promise.reject(reason)
|
|
3368
3737
|
}
|
|
3369
3738
|
})();
|
|
3739
|
+
|
|
3370
3740
|
cache.set(index, responsePromise);
|
|
3371
|
-
|
|
3741
|
+
|
|
3742
|
+
if (defaultOptions.cacheTime !== 0) {
|
|
3372
3743
|
setTimeout(() => {
|
|
3373
3744
|
cache.clear(index);
|
|
3374
|
-
}, defaultOptions.
|
|
3745
|
+
}, defaultOptions.cacheTime);
|
|
3375
3746
|
}
|
|
3376
3747
|
}
|
|
3377
|
-
|
|
3748
|
+
|
|
3749
|
+
return responsePromise.then(data => JSON.parse(JSON.stringify(data))) // 为防止数据源污染
|
|
3378
3750
|
};
|
|
3379
3751
|
|
|
3380
|
-
export {
|
|
3752
|
+
export { arrToLabel, arrToObj, buildUniqueUrl, cache, calculateHeight, convertMilliseconds, copyContent, copyText, copyToClip, createGuid, debounce, index as defaultAdapter, detectZoom, doctorHeadImg, escapeRegExp, extendSession, extentSession, fileDownloadHelper, findNodeOfTree, formatDate, formatDateMinute, formatDecimal, formatDecimalString, formatMoney, formatNumber, formatText, get, getBrowserInfo, getDeviceInfo, getDeviceType, getGaugeImgUrl, getHalfYearAgoToday, getImgURL, getQueryString, getSession, getSessionStorage, getSystemKey, getUrlParams, hidePhone, isEmpty, isEmptyObj, isEqual, isSupported, isTablet, isTouchDevice, mapParseReviver, mapStringifyReplacer, myDebounce, queryToObj, removeSession, removeSessionStorage, setCursor, setSession, setSessionStorage, setSystemKey, setUrl, sleep, throttle, toQueryString, urlToJson, uuid, uuidLong, validateFieldAsync, validateMeaninglessConsecutiveSymbols, validatePhone };
|