a2bei4-utils 1.0.6 → 1.0.8

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.
@@ -361,7 +361,111 @@ registerProcessor('audio-stream-resampler-processor', AudioStreamResamplerProces
361
361
  function getSearchParam(key) {
362
362
  const params = getAllSearchParams();
363
363
  return params[key];
364
- }
364
+ }
365
+
366
+ /**
367
+ * 全屏操作辅助工具对象
368
+ * @namespace fullscreenHelper
369
+ */
370
+ const fullscreenHelper = {
371
+ /**
372
+ * 请求进入全屏模式
373
+ * @param {Element} element - 要全屏显示的元素
374
+ * @returns {Promise<void> | undefined} 全屏请求 Promise(如支持)
375
+ */
376
+ requestFullscreen: (element) => {
377
+ if (!element) {
378
+ console.warn("未提供有效的 DOM 元素");
379
+ return;
380
+ }
381
+ if (element.requestFullscreen) {
382
+ return element.requestFullscreen();
383
+ } else if (element.mozRequestFullScreen) {
384
+ return element.mozRequestFullScreen();
385
+ } else if (element.webkitRequestFullscreen) {
386
+ return element.webkitRequestFullscreen();
387
+ } else if (element.msRequestFullscreen) {
388
+ return element.msRequestFullscreen();
389
+ } else {
390
+ console.warn("当前浏览器不支持全屏 API");
391
+ }
392
+ },
393
+
394
+ /**
395
+ * 退出全屏模式
396
+ * @returns {Promise<void> | undefined} 退出全屏请求 Promise(如支持)
397
+ */
398
+ exitFullscreen: () => {
399
+ if (document.exitFullscreen) {
400
+ return document.exitFullscreen();
401
+ } else if (document.mozCancelFullScreen) {
402
+ return document.mozCancelFullScreen();
403
+ } else if (document.webkitExitFullscreen) {
404
+ return document.webkitExitFullscreen();
405
+ } else if (document.msExitFullscreen) {
406
+ return document.msExitFullscreen();
407
+ }
408
+ },
409
+
410
+ /**
411
+ * 获取当前全屏元素
412
+ * @returns {Element | null} 当前处于全屏模式的元素,无则返回 null
413
+ */
414
+ getFullscreenElement: () => {
415
+ return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement || null;
416
+ },
417
+
418
+ /**
419
+ * 检测当前是否处于全屏模式
420
+ * @returns {boolean} 是否全屏中
421
+ */
422
+ isFullscreen: () => {
423
+ return !!(document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement);
424
+ },
425
+
426
+ /**
427
+ * 检测浏览器是否支持全屏 API
428
+ * @returns {boolean} 是否支持全屏
429
+ */
430
+ isFullscreenEnabled: () => {
431
+ return !!(document.fullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled);
432
+ },
433
+
434
+ /**
435
+ * 切换指定元素的全屏状态
436
+ * @param {Element} element - 要切换全屏的元素
437
+ * @returns {Promise<void> | undefined} 全屏操作 Promise
438
+ */
439
+ toggleFullscreen: (element) => {
440
+ if (fullscreenHelper.isFullscreen()) {
441
+ return fullscreenHelper.exitFullscreen();
442
+ } else {
443
+ return fullscreenHelper.requestFullscreen(element);
444
+ }
445
+ },
446
+
447
+ /**
448
+ * 监听全屏变化事件
449
+ * @param {Function} callback - 全屏状态变化时的回调函数,参数为 isFullscreen: boolean
450
+ * @returns {Function} 取消监听的函数
451
+ */
452
+ onFullscreenChange: (callback) => {
453
+ const handler = () => {
454
+ callback(fullscreenHelper.isFullscreen());
455
+ };
456
+ document.addEventListener("fullscreenchange", handler);
457
+ document.addEventListener("webkitfullscreenchange", handler);
458
+ document.addEventListener("mozfullscreenchange", handler);
459
+ document.addEventListener("msfullscreenchange", handler);
460
+
461
+ return () => {
462
+ document.removeEventListener("fullscreenchange", handler);
463
+ document.removeEventListener("webkitfullscreenchange", handler);
464
+ document.removeEventListener("mozfullscreenchange", handler);
465
+ document.removeEventListener("msfullscreenchange", handler);
466
+ };
467
+ }
468
+ };
365
469
 
366
470
  //#region 数据类型判断
367
471
 
@@ -1228,6 +1332,9 @@ registerProcessor('audio-stream-resampler-processor', AudioStreamResamplerProces
1228
1332
  const urlPathname = new URL(url).pathname;
1229
1333
  // 获取路径的最后一部分作为文件名,并移除可能的查询参数
1230
1334
  fileName = urlPathname.substring(urlPathname.lastIndexOf("/") + 1).split("?")[0];
1335
+ if (fileName) {
1336
+ fileName = decodeURIComponent(fileName);
1337
+ }
1231
1338
  } catch (e) {}
1232
1339
  // 如果提取后文件名为空(例如 URL 以 '/' 结尾),也使用时间戳
1233
1340
  if (!fileName) {
@@ -1903,6 +2010,64 @@ registerProcessor('audio-stream-resampler-processor', AudioStreamResamplerProces
1903
2010
  checked: [...checked],
1904
2011
  halfChecked: [...halfChecked]
1905
2012
  };
2013
+ }
2014
+
2015
+ /**
2016
+ * 在树形结构中查找目标节点的完整路径(从根节点到目标节点,含目标节点自身)。
2017
+ *
2018
+ * @template T extends Record<PropertyKey, any>
2019
+ * @param {T[]} nodes - 树形结构森林(支持多根)
2020
+ * @param {any} targetValue - 目标节点的 key 值
2021
+ * @param {string} [key='id'] - 节点唯一标识字段
2022
+ * @param {string} [parentKey='pid'] - 父节点标识字段(指向父节点的 key 值)
2023
+ * @param {string} [childrenKey='children'] - 子节点数组字段
2024
+ * @returns {T[]} 从根到目标节点的路径数组;未找到返回空数组
2025
+ *
2026
+ * @example
2027
+ * const tree = [
2028
+ * { id: 1, pid: null, children: [
2029
+ * { id: 2, pid: 1, children: [
2030
+ * { id: 3, pid: 2 }
2031
+ * ]}
2032
+ * ]}
2033
+ * ];
2034
+ * findTreeNodePath(tree, 3); // [{id:1, ...}, {id:2, ...}, {id:3, ...}]
2035
+ */
2036
+ function findTreeNodePath(nodes, targetValue, key = "id", parentKey = "pid", childrenKey = "children") {
2037
+ if (!Array.isArray(nodes) || nodes.length === 0 || targetValue == null) {
2038
+ return [];
2039
+ }
2040
+
2041
+ // 1. 建立节点索引
2042
+ const index = new Map();
2043
+ const stack = [...nodes];
2044
+
2045
+ while (stack.length) {
2046
+ const node = stack.pop();
2047
+ if (!node || typeof node !== "object") continue;
2048
+
2049
+ index.set(node[key], node);
2050
+
2051
+ const children = node[childrenKey];
2052
+ if (Array.isArray(children)) {
2053
+ stack.push(...children);
2054
+ }
2055
+ }
2056
+
2057
+ // 2. 回溯路径(防循环引用)
2058
+ const path = [];
2059
+ const visited = new Set();
2060
+ let cur = index.get(targetValue);
2061
+
2062
+ while (cur && !visited.has(cur[key])) {
2063
+ visited.add(cur[key]);
2064
+ path.push(cur);
2065
+
2066
+ const parentValue = cur[parentKey];
2067
+ cur = parentValue != null ? index.get(parentValue) : undefined;
2068
+ }
2069
+
2070
+ return path.reverse();
1906
2071
  }
1907
2072
 
1908
2073
  /**
@@ -2328,8 +2493,10 @@ registerProcessor('audio-stream-resampler-processor', AudioStreamResamplerProces
2328
2493
  exports.fetchOrDownloadByUrl = fetchOrDownloadByUrl;
2329
2494
  exports.findObjAttrValueById = findObjAttrValueById;
2330
2495
  exports.findTreeNodeById = findTreeNodeById;
2496
+ exports.findTreeNodePath = findTreeNodePath;
2331
2497
  exports.flatCompleteTree2NestedTree = flatCompleteTree2NestedTree;
2332
2498
  exports.formatTimeForLocale = formatTimeForLocale;
2499
+ exports.fullscreenHelper = fullscreenHelper;
2333
2500
  exports.getAllSearchParams = getAllSearchParams;
2334
2501
  exports.getDataType = getDataType;
2335
2502
  exports.getFunctionArgNames = getFunctionArgNames;