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