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