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