a2bei4-utils 1.0.9 → 1.0.11

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.
Files changed (44) hide show
  1. package/dist/a2bei4.utils.cjs.js +193 -2
  2. package/dist/a2bei4.utils.cjs.js.map +1 -1
  3. package/dist/a2bei4.utils.cjs.min.js +1 -1
  4. package/dist/a2bei4.utils.cjs.min.js.map +1 -1
  5. package/dist/a2bei4.utils.esm.js +191 -2
  6. package/dist/a2bei4.utils.esm.js.map +1 -1
  7. package/dist/a2bei4.utils.esm.min.js +1 -1
  8. package/dist/a2bei4.utils.esm.min.js.map +1 -1
  9. package/dist/a2bei4.utils.umd.js +193 -2
  10. package/dist/a2bei4.utils.umd.js.map +1 -1
  11. package/dist/a2bei4.utils.umd.min.js +1 -1
  12. package/dist/a2bei4.utils.umd.min.js.map +1 -1
  13. package/dist/arr.cjs.map +1 -1
  14. package/dist/arr.js.map +1 -1
  15. package/dist/audio.cjs.map +1 -1
  16. package/dist/audio.js.map +1 -1
  17. package/dist/browser.cjs.map +1 -1
  18. package/dist/browser.js.map +1 -1
  19. package/dist/common.cjs.map +1 -1
  20. package/dist/common.js.map +1 -1
  21. package/dist/date.cjs.map +1 -1
  22. package/dist/date.js.map +1 -1
  23. package/dist/download.cjs.map +1 -1
  24. package/dist/download.js.map +1 -1
  25. package/dist/evt.cjs.map +1 -1
  26. package/dist/evt.js.map +1 -1
  27. package/dist/id.cjs.map +1 -1
  28. package/dist/id.js.map +1 -1
  29. package/dist/menu.cjs.map +1 -1
  30. package/dist/menu.js.map +1 -1
  31. package/dist/timer.cjs +14 -0
  32. package/dist/timer.cjs.map +1 -1
  33. package/dist/timer.js +14 -0
  34. package/dist/timer.js.map +1 -1
  35. package/dist/tree.cjs +177 -0
  36. package/dist/tree.cjs.map +1 -1
  37. package/dist/tree.js +176 -1
  38. package/dist/tree.js.map +1 -1
  39. package/dist/webSocket.cjs.map +1 -1
  40. package/dist/webSocket.js.map +1 -1
  41. package/package.json +5 -5
  42. package/types/index.d.ts +186 -2
  43. package/types/timer.d.ts +4 -0
  44. package/types/tree.d.ts +182 -1
package/dist/menu.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"menu.js","sources":["../src/source/menu.js"],"sourcesContent":["/**\r\n * 处理数据库菜单项,生成 UI 菜单树和路由配置。\r\n *\r\n * @param {Array<Object>} menuItems - 原始菜单项数组,树形结构\r\n * @param {Object} [options] - 配置选项\r\n * @param {string} [options.idKey='id'] - 数据源 ID 键名\r\n * @param {string} [options.codeKey='code'] - 数据源编码键名, 用于拼接路由名称和路由路径\r\n * @param {string} [options.labelKey='text'] - 数据源标签键名\r\n * @param {string} [options.childrenKey='children'] - 数据源子节点键名\r\n * @param {string} [options.extendKey='extend'] - 数据源扩展对象键名\r\n * @param {string} [options.menuIdKey='key'] - 输出菜单 ID 键名\r\n * @param {string} [options.menuLabelKey='label'] - 输出菜单标签键名\r\n * @param {string} [options.menuChildrenKey='children'] - 输出菜单子节点键名\r\n * @param {string} [options.menuExtendKey='extend'] - 输出菜单扩展对象键名\r\n * @param {string} [options.routeNameKey='name'] - 路由名称键名\r\n * @param {string} [options.routePathKey='path'] - 路由路径键名\r\n * @param {string} [options.routeMetaKey='meta'] - 路由元数据键名\r\n * @param {Function} [options.handleNodeItem] - 节点处理钩子,参数:(nodeSimple) => void\r\n * @param {Function} [options.handleMenuItem] - 菜单项处理钩子,参数:(uiMenuItem, nodeSimple) => void\r\n * @param {Function} [options.handleRouteItem] - 路由项处理钩子,参数:(routeItem, nodeSimple) => void\r\n *\r\n * @returns {Object} 处理结果\r\n * @returns {Array<Object>} returns.uiMenuItems - UI 菜单树形结构数组\r\n * @returns {Array<Object>} returns.routeItems - 扁平化路由配置数组\r\n * @returns {Map<string, Object>} returns.nodeMap - 节点 ID 到节点数据的映射表\r\n *\r\n * @example\r\n * const menuItems = [\r\n * { id: '1', code: 'system', text: '系统管理', children: [\r\n * { id: '1-1', code: 'user', text: '用户管理' }\r\n * ]}\r\n * ];\r\n *\r\n * const result = handleDbMenuItems(menuItems, {\r\n * handleRouteItem: (route, node) => {\r\n * route.component = () => import(`./views/${node.code}.vue`);\r\n * }\r\n * });\r\n *\r\n * // result.uiMenuItems: [{ key: '1', label: '系统管理', children: [...] }]\r\n * // result.routeItems: [{ name: 'system.user', path: '/system/user', meta: { id: '1-1' } }]\r\n * // result.nodeMap: Map { '1' => {...}, '1-1' => {...} }\r\n */\r\nexport function handleDbMenuItems(menuItems, options = {}) {\r\n // 1. 统一配置项,简化调用\r\n const {\r\n // 数据源键名\r\n idKey = \"id\",\r\n codeKey = \"code\",\r\n labelKey = \"text\",\r\n childrenKey = \"children\",\r\n extendKey = \"extend\",\r\n\r\n // 输出目标键名\r\n menuIdKey = \"key\",\r\n menuLabelKey = \"label\",\r\n menuChildrenKey = \"children\",\r\n menuExtendKey = \"extend\",\r\n\r\n routeNameKey = \"name\",\r\n routePathKey = \"path\",\r\n routeMetaKey = \"meta\",\r\n\r\n // 钩子函数\r\n handleNodeItem,\r\n handleMenuItem,\r\n handleRouteItem\r\n } = options;\r\n\r\n const uiMenuItems = [];\r\n const routeItems = [];\r\n const nodeMap = new Map(); // id -> node (纯净的节点数据)\r\n const nodePathMap = new Map(); // id -> [{ code, id }] (仅存储路径计算所需的轻量数据)\r\n\r\n /* ---------- 1. 建立索引:扁平化节点 & 构建路径索引 ---------- */\r\n // 使用栈进行深度优先遍历,记录路径\r\n const stack = [];\r\n if (Array.isArray(menuItems)) {\r\n menuItems.forEach((n) => stack.push({ node: n, parentPath: [] }));\r\n }\r\n\r\n while (stack.length) {\r\n const { node, parentPath } = stack.pop();\r\n const idValue = node[idKey];\r\n\r\n // 构建当前节点的路径片段\r\n const currentPathSegment = { [idKey]: idValue };\r\n const fullPath = [...parentPath, currentPathSegment];\r\n\r\n // 存储路径信息用于后续路由生成\r\n nodePathMap.set(idValue, fullPath);\r\n\r\n // 创建轻量级的节点对象存入 nodeMap\r\n // 注意:这里先创建基础结构,路由信息在第二步补充\r\n const nodeSimple = {\r\n ...node,\r\n [extendKey]: {}, // 初始化扩展对象\r\n [childrenKey]: null // 去除原始 children,避免引用污染\r\n };\r\n nodeMap.set(idValue, nodeSimple);\r\n\r\n // 处理子节点\r\n const childrenNodes = node[childrenKey];\r\n if (Array.isArray(childrenNodes)) {\r\n // 逆序压栈,保持原序\r\n for (let i = childrenNodes.length - 1; i >= 0; i--) {\r\n stack.push({ node: childrenNodes[i], parentPath: fullPath });\r\n }\r\n }\r\n }\r\n\r\n /* ---------- 2. 构建树形结构 & 生成路由 ---------- */\r\n const buildStack = [];\r\n if (Array.isArray(menuItems)) {\r\n for (let i = menuItems.length - 1; i >= 0; i--) {\r\n buildStack.push({ node: menuItems[i], parentUi: undefined });\r\n }\r\n }\r\n\r\n while (buildStack.length) {\r\n const { node, parentUi } = buildStack.pop();\r\n const idValue = node[idKey];\r\n\r\n // 从 Map 中取出之前创建好的节点对象\r\n const nodeSimple = nodeMap.get(idValue);\r\n\r\n // 2.1 构建 UI 菜单项\r\n const uiMenuItem = {\r\n [menuIdKey]: idValue,\r\n [menuLabelKey]: node[labelKey],\r\n [menuExtendKey]: {} // UI 菜单专用扩展\r\n };\r\n\r\n // 处理子节点\r\n const childrenNodes = node[childrenKey];\r\n const hasChildren = Array.isArray(childrenNodes) && childrenNodes.length > 0;\r\n\r\n if (hasChildren) {\r\n // 有子节点 -> 继续压栈,传递当前 uiMenuItem 作为父级\r\n for (let i = childrenNodes.length - 1; i >= 0; i--) {\r\n buildStack.push({ node: childrenNodes[i], parentUi: uiMenuItem });\r\n }\r\n } else {\r\n // 2.2 无子节点 -> 生成路由配置\r\n\r\n // 提取 code 和 id 数组\r\n const codePaths = [],\r\n keyPaths = [];\r\n nodePathMap.get(idValue).forEach((item) => {\r\n const idValue = item[idKey];\r\n const nodeSimple = nodeMap.get(idValue);\r\n keyPaths.push(idValue);\r\n codePaths.push(nodeSimple[codeKey]);\r\n });\r\n\r\n const routeName = codePaths.join(\".\");\r\n const routePath = \"/\" + codePaths.join(\"/\");\r\n\r\n const routeItem = {\r\n [routeNameKey]: routeName,\r\n [routePathKey]: routePath,\r\n [routeMetaKey]: { [idKey]: idValue }\r\n };\r\n\r\n // 执行路由钩子\r\n if (typeof handleRouteItem === \"function\") {\r\n handleRouteItem(routeItem, nodeSimple);\r\n }\r\n routeItems.push(routeItem);\r\n\r\n // 补充扩展信息\r\n uiMenuItem[menuExtendKey].routeName = routeName;\r\n uiMenuItem[menuExtendKey].keyPaths = keyPaths;\r\n\r\n nodeSimple[extendKey].routeName = routeName;\r\n nodeSimple[extendKey].keyPaths = keyPaths;\r\n }\r\n\r\n // 2.3 挂载到父级 UI 或根数组\r\n if (parentUi) {\r\n // 逻辑赋值,确保 children 数组存在\r\n (parentUi[menuChildrenKey] || (parentUi[menuChildrenKey] = [])).push(uiMenuItem);\r\n } else {\r\n if (typeof handleMenuItem === \"function\") {\r\n handleMenuItem(uiMenuItem, nodeSimple);\r\n }\r\n uiMenuItems.push(uiMenuItem);\r\n }\r\n\r\n // 执行节点钩子\r\n if (typeof handleNodeItem === \"function\") {\r\n handleNodeItem(nodeSimple);\r\n }\r\n }\r\n\r\n return { uiMenuItems, routeItems, nodeMap };\r\n}\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"menu.js","sources":["../src/source/menu.js"],"sourcesContent":["/**\r\n * 处理数据库菜单项,生成 UI 菜单树和路由配置。\r\n *\r\n * @param {Array<Object>} menuItems - 原始菜单项数组,树形结构\r\n * @param {Object} [options] - 配置选项\r\n * @param {string} [options.idKey='id'] - 数据源 ID 键名\r\n * @param {string} [options.codeKey='code'] - 数据源编码键名, 用于拼接路由名称和路由路径\r\n * @param {string} [options.labelKey='text'] - 数据源标签键名\r\n * @param {string} [options.childrenKey='children'] - 数据源子节点键名\r\n * @param {string} [options.extendKey='extend'] - 数据源扩展对象键名\r\n * @param {string} [options.menuIdKey='key'] - 输出菜单 ID 键名\r\n * @param {string} [options.menuLabelKey='label'] - 输出菜单标签键名\r\n * @param {string} [options.menuChildrenKey='children'] - 输出菜单子节点键名\r\n * @param {string} [options.menuExtendKey='extend'] - 输出菜单扩展对象键名\r\n * @param {string} [options.routeNameKey='name'] - 路由名称键名\r\n * @param {string} [options.routePathKey='path'] - 路由路径键名\r\n * @param {string} [options.routeMetaKey='meta'] - 路由元数据键名\r\n * @param {Function} [options.handleNodeItem] - 节点处理钩子,参数:(nodeSimple) => void\r\n * @param {Function} [options.handleMenuItem] - 菜单项处理钩子,参数:(uiMenuItem, nodeSimple) => void\r\n * @param {Function} [options.handleRouteItem] - 路由项处理钩子,参数:(routeItem, nodeSimple) => void\r\n *\r\n * @returns {Object} 处理结果\r\n * @returns {Array<Object>} returns.uiMenuItems - UI 菜单树形结构数组\r\n * @returns {Array<Object>} returns.routeItems - 扁平化路由配置数组\r\n * @returns {Map<string, Object>} returns.nodeMap - 节点 ID 到节点数据的映射表\r\n *\r\n * @example\r\n * const menuItems = [\r\n * { id: '1', code: 'system', text: '系统管理', children: [\r\n * { id: '1-1', code: 'user', text: '用户管理' }\r\n * ]}\r\n * ];\r\n *\r\n * const result = handleDbMenuItems(menuItems, {\r\n * handleRouteItem: (route, node) => {\r\n * route.component = () => import(`./views/${node.code}.vue`);\r\n * }\r\n * });\r\n *\r\n * // result.uiMenuItems: [{ key: '1', label: '系统管理', children: [...] }]\r\n * // result.routeItems: [{ name: 'system.user', path: '/system/user', meta: { id: '1-1' } }]\r\n * // result.nodeMap: Map { '1' => {...}, '1-1' => {...} }\r\n */\r\nexport function handleDbMenuItems(menuItems, options = {}) {\r\n // 1. 统一配置项,简化调用\r\n const {\r\n // 数据源键名\r\n idKey = \"id\",\r\n codeKey = \"code\",\r\n labelKey = \"text\",\r\n childrenKey = \"children\",\r\n extendKey = \"extend\",\r\n\r\n // 输出目标键名\r\n menuIdKey = \"key\",\r\n menuLabelKey = \"label\",\r\n menuChildrenKey = \"children\",\r\n menuExtendKey = \"extend\",\r\n\r\n routeNameKey = \"name\",\r\n routePathKey = \"path\",\r\n routeMetaKey = \"meta\",\r\n\r\n // 钩子函数\r\n handleNodeItem,\r\n handleMenuItem,\r\n handleRouteItem\r\n } = options;\r\n\r\n const uiMenuItems = [];\r\n const routeItems = [];\r\n const nodeMap = new Map(); // id -> node (纯净的节点数据)\r\n const nodePathMap = new Map(); // id -> [{ code, id }] (仅存储路径计算所需的轻量数据)\r\n\r\n /* ---------- 1. 建立索引:扁平化节点 & 构建路径索引 ---------- */\r\n // 使用栈进行深度优先遍历,记录路径\r\n const stack = [];\r\n if (Array.isArray(menuItems)) {\r\n menuItems.forEach((n) => stack.push({ node: n, parentPath: [] }));\r\n }\r\n\r\n while (stack.length) {\r\n const { node, parentPath } = stack.pop();\r\n const idValue = node[idKey];\r\n\r\n // 构建当前节点的路径片段\r\n const currentPathSegment = { [idKey]: idValue };\r\n const fullPath = [...parentPath, currentPathSegment];\r\n\r\n // 存储路径信息用于后续路由生成\r\n nodePathMap.set(idValue, fullPath);\r\n\r\n // 创建轻量级的节点对象存入 nodeMap\r\n // 注意:这里先创建基础结构,路由信息在第二步补充\r\n const nodeSimple = {\r\n ...node,\r\n [extendKey]: {}, // 初始化扩展对象\r\n [childrenKey]: null // 去除原始 children,避免引用污染\r\n };\r\n nodeMap.set(idValue, nodeSimple);\r\n\r\n // 处理子节点\r\n const childrenNodes = node[childrenKey];\r\n if (Array.isArray(childrenNodes)) {\r\n // 逆序压栈,保持原序\r\n for (let i = childrenNodes.length - 1; i >= 0; i--) {\r\n stack.push({ node: childrenNodes[i], parentPath: fullPath });\r\n }\r\n }\r\n }\r\n\r\n /* ---------- 2. 构建树形结构 & 生成路由 ---------- */\r\n const buildStack = [];\r\n if (Array.isArray(menuItems)) {\r\n for (let i = menuItems.length - 1; i >= 0; i--) {\r\n buildStack.push({ node: menuItems[i], parentUi: undefined });\r\n }\r\n }\r\n\r\n while (buildStack.length) {\r\n const { node, parentUi } = buildStack.pop();\r\n const idValue = node[idKey];\r\n\r\n // 从 Map 中取出之前创建好的节点对象\r\n const nodeSimple = nodeMap.get(idValue);\r\n\r\n // 2.1 构建 UI 菜单项\r\n const uiMenuItem = {\r\n [menuIdKey]: idValue,\r\n [menuLabelKey]: node[labelKey],\r\n [menuExtendKey]: {} // UI 菜单专用扩展\r\n };\r\n\r\n // 处理子节点\r\n const childrenNodes = node[childrenKey];\r\n const hasChildren = Array.isArray(childrenNodes) && childrenNodes.length > 0;\r\n\r\n if (hasChildren) {\r\n // 有子节点 -> 继续压栈,传递当前 uiMenuItem 作为父级\r\n for (let i = childrenNodes.length - 1; i >= 0; i--) {\r\n buildStack.push({ node: childrenNodes[i], parentUi: uiMenuItem });\r\n }\r\n } else {\r\n // 2.2 无子节点 -> 生成路由配置\r\n\r\n // 提取 code 和 id 数组\r\n const codePaths = [],\r\n keyPaths = [];\r\n nodePathMap.get(idValue).forEach((item) => {\r\n const idValue = item[idKey];\r\n const nodeSimple = nodeMap.get(idValue);\r\n keyPaths.push(idValue);\r\n codePaths.push(nodeSimple[codeKey]);\r\n });\r\n\r\n const routeName = codePaths.join(\".\");\r\n const routePath = \"/\" + codePaths.join(\"/\");\r\n\r\n const routeItem = {\r\n [routeNameKey]: routeName,\r\n [routePathKey]: routePath,\r\n [routeMetaKey]: { [idKey]: idValue }\r\n };\r\n\r\n // 执行路由钩子\r\n if (typeof handleRouteItem === \"function\") {\r\n handleRouteItem(routeItem, nodeSimple);\r\n }\r\n routeItems.push(routeItem);\r\n\r\n // 补充扩展信息\r\n uiMenuItem[menuExtendKey].routeName = routeName;\r\n uiMenuItem[menuExtendKey].keyPaths = keyPaths;\r\n\r\n nodeSimple[extendKey].routeName = routeName;\r\n nodeSimple[extendKey].keyPaths = keyPaths;\r\n }\r\n\r\n // 2.3 挂载到父级 UI 或根数组\r\n if (parentUi) {\r\n // 逻辑赋值,确保 children 数组存在\r\n (parentUi[menuChildrenKey] || (parentUi[menuChildrenKey] = [])).push(uiMenuItem);\r\n } else {\r\n if (typeof handleMenuItem === \"function\") {\r\n handleMenuItem(uiMenuItem, nodeSimple);\r\n }\r\n uiMenuItems.push(uiMenuItem);\r\n }\r\n\r\n // 执行节点钩子\r\n if (typeof handleNodeItem === \"function\") {\r\n handleNodeItem(nodeSimple);\r\n }\r\n }\r\n\r\n return { uiMenuItems, routeItems, nodeMap };\r\n}\r\n"],"names":[],"mappings":"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"}
package/dist/timer.cjs CHANGED
@@ -19,6 +19,10 @@ class IntervalTimer {
19
19
  this._fn = fn;
20
20
  this._ms = ms;
21
21
  this._timerId = null;
22
+ /**
23
+ * @type {symbol | null}
24
+ */
25
+ this._loopId = null;
22
26
  }
23
27
 
24
28
  /**
@@ -27,8 +31,17 @@ class IntervalTimer {
27
31
  */
28
32
  start() {
29
33
  this.stop();
34
+
35
+ const curLoopId = Symbol();
36
+ this._loopId = curLoopId;
37
+
30
38
  const loop = () => {
39
+ if (this._loopId !== curLoopId) return;
40
+
31
41
  this._fn(); // 执行业务
42
+
43
+ if (this._loopId !== curLoopId) return;
44
+
32
45
  this._timerId = setTimeout(loop, this._ms);
33
46
  };
34
47
  loop(); // 立即执行第一次
@@ -38,6 +51,7 @@ class IntervalTimer {
38
51
  * 停止定时器。
39
52
  */
40
53
  stop() {
54
+ this._loopId = null;
41
55
  if (this._timerId !== null) {
42
56
  clearTimeout(this._timerId);
43
57
  this._timerId = null;
@@ -1 +1 @@
1
- {"version":3,"file":"timer.cjs","sources":["../src/source/timer.js"],"sourcesContent":["/**\r\n * 基于 `setTimeout` 的“间隔循环”定时器。\r\n * 每次任务执行完成后才计算下一次间隔,避免任务堆积。\r\n */\r\nexport class IntervalTimer {\r\n \r\n /**\r\n * 创建定时器实例。\r\n * @param {() => void} fn - 每次间隔要执行的业务函数\r\n * @param {number} [ms=1000] - 间隔时间(毫秒)\r\n * @throws {TypeError} 当 `fn` 不是函数时抛出\r\n */\r\n constructor(fn, ms = 1000) {\r\n if (typeof fn !== \"function\") {\r\n throw new TypeError(\"IntervalTimer: 必须传入一个函数\");\r\n }\r\n this._fn = fn;\r\n this._ms = ms;\r\n this._timerId = null;\r\n }\r\n\r\n /**\r\n * 启动定时器;若已启动则先停止再重新启动。\r\n * 首次执行会立即触发。\r\n */\r\n start() {\r\n this.stop();\r\n const loop = () => {\r\n this._fn(); // 执行业务\r\n this._timerId = setTimeout(loop, this._ms);\r\n };\r\n loop(); // 立即执行第一次\r\n }\r\n\r\n /**\r\n * 停止定时器。\r\n */\r\n stop() {\r\n if (this._timerId !== null) {\r\n clearTimeout(this._timerId);\r\n this._timerId = null;\r\n }\r\n }\r\n\r\n /**\r\n * 查询定时器是否正在运行。\r\n * @returns {boolean}\r\n */\r\n isRunning() {\r\n return this._timerId !== null;\r\n }\r\n}\r\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACO,MAAM,aAAa,CAAC;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,WAAW,CAAC,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE;AAC/B,QAAQ,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE;AACtC,YAAY,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAC;AAC3D,QAAQ,CAAC;AACT,QAAQ,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;AACtB,QAAQ,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;AACtB,QAAQ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC7B,IAAI,CAAC;AACL;AACA;AACA;AACA;AACA;AACA,IAAI,KAAK,GAAG;AACZ,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;AACpB,QAAQ,MAAM,IAAI,GAAG,MAAM;AAC3B,YAAY,IAAI,CAAC,GAAG,EAAE,CAAC;AACvB,YAAY,IAAI,CAAC,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;AACvD,QAAQ,CAAC,CAAC;AACV,QAAQ,IAAI,EAAE,CAAC;AACf,IAAI,CAAC;AACL;AACA;AACA;AACA;AACA,IAAI,IAAI,GAAG;AACX,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE;AACpC,YAAY,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxC,YAAY,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;AACjC,QAAQ,CAAC;AACT,IAAI,CAAC;AACL;AACA;AACA;AACA;AACA;AACA,IAAI,SAAS,GAAG;AAChB,QAAQ,OAAO,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC;AACtC,IAAI,CAAC;AACL;;;;"}
1
+ {"version":3,"file":"timer.cjs","sources":["../src/source/timer.js"],"sourcesContent":["/**\r\n * 基于 `setTimeout` 的“间隔循环”定时器。\r\n * 每次任务执行完成后才计算下一次间隔,避免任务堆积。\r\n */\r\nexport class IntervalTimer {\r\n \r\n /**\r\n * 创建定时器实例。\r\n * @param {() => void} fn - 每次间隔要执行的业务函数\r\n * @param {number} [ms=1000] - 间隔时间(毫秒)\r\n * @throws {TypeError} 当 `fn` 不是函数时抛出\r\n */\r\n constructor(fn, ms = 1000) {\r\n if (typeof fn !== \"function\") {\r\n throw new TypeError(\"IntervalTimer: 必须传入一个函数\");\r\n }\r\n this._fn = fn;\r\n this._ms = ms;\r\n this._timerId = null;\r\n /**\r\n * @type {symbol | null}\r\n */\r\n this._loopId = null;\r\n }\r\n\r\n /**\r\n * 启动定时器;若已启动则先停止再重新启动。\r\n * 首次执行会立即触发。\r\n */\r\n start() {\r\n this.stop();\r\n\r\n const curLoopId = Symbol();\r\n this._loopId = curLoopId;\r\n\r\n const loop = () => {\r\n if (this._loopId !== curLoopId) return;\r\n\r\n this._fn(); // 执行业务\r\n\r\n if (this._loopId !== curLoopId) return;\r\n\r\n this._timerId = setTimeout(loop, this._ms);\r\n };\r\n loop(); // 立即执行第一次\r\n }\r\n\r\n /**\r\n * 停止定时器。\r\n */\r\n stop() {\r\n this._loopId = null;\r\n if (this._timerId !== null) {\r\n clearTimeout(this._timerId);\r\n this._timerId = null;\r\n }\r\n }\r\n\r\n /**\r\n * 查询定时器是否正在运行。\r\n * @returns {boolean}\r\n */\r\n isRunning() {\r\n return this._timerId !== null;\r\n }\r\n}\r\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACO,MAAM,aAAa,CAAC;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,WAAW,CAAC,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE;AAC/B,QAAQ,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE;AACtC,YAAY,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAC;AAC3D,QAAQ,CAAC;AACT,QAAQ,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;AACtB,QAAQ,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;AACtB,QAAQ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC7B;AACA;AACA;AACA,QAAQ,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;AAC5B,IAAI,CAAC;AACL;AACA;AACA;AACA;AACA;AACA,IAAI,KAAK,GAAG;AACZ,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;AACpB;AACA,QAAQ,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC;AACnC,QAAQ,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;AACjC;AACA,QAAQ,MAAM,IAAI,GAAG,MAAM;AAC3B,YAAY,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,OAAO;AACnD;AACA,YAAY,IAAI,CAAC,GAAG,EAAE,CAAC;AACvB;AACA,YAAY,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,OAAO;AACnD;AACA,YAAY,IAAI,CAAC,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;AACvD,QAAQ,CAAC,CAAC;AACV,QAAQ,IAAI,EAAE,CAAC;AACf,IAAI,CAAC;AACL;AACA;AACA;AACA;AACA,IAAI,IAAI,GAAG;AACX,QAAQ,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;AAC5B,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE;AACpC,YAAY,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxC,YAAY,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;AACjC,QAAQ,CAAC;AACT,IAAI,CAAC;AACL;AACA;AACA;AACA;AACA;AACA,IAAI,SAAS,GAAG;AAChB,QAAQ,OAAO,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC;AACtC,IAAI,CAAC;AACL;;"}
package/dist/timer.js CHANGED
@@ -17,6 +17,10 @@ class IntervalTimer {
17
17
  this._fn = fn;
18
18
  this._ms = ms;
19
19
  this._timerId = null;
20
+ /**
21
+ * @type {symbol | null}
22
+ */
23
+ this._loopId = null;
20
24
  }
21
25
 
22
26
  /**
@@ -25,8 +29,17 @@ class IntervalTimer {
25
29
  */
26
30
  start() {
27
31
  this.stop();
32
+
33
+ const curLoopId = Symbol();
34
+ this._loopId = curLoopId;
35
+
28
36
  const loop = () => {
37
+ if (this._loopId !== curLoopId) return;
38
+
29
39
  this._fn(); // 执行业务
40
+
41
+ if (this._loopId !== curLoopId) return;
42
+
30
43
  this._timerId = setTimeout(loop, this._ms);
31
44
  };
32
45
  loop(); // 立即执行第一次
@@ -36,6 +49,7 @@ class IntervalTimer {
36
49
  * 停止定时器。
37
50
  */
38
51
  stop() {
52
+ this._loopId = null;
39
53
  if (this._timerId !== null) {
40
54
  clearTimeout(this._timerId);
41
55
  this._timerId = null;
package/dist/timer.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"timer.js","sources":["../src/source/timer.js"],"sourcesContent":["/**\r\n * 基于 `setTimeout` 的“间隔循环”定时器。\r\n * 每次任务执行完成后才计算下一次间隔,避免任务堆积。\r\n */\r\nexport class IntervalTimer {\r\n \r\n /**\r\n * 创建定时器实例。\r\n * @param {() => void} fn - 每次间隔要执行的业务函数\r\n * @param {number} [ms=1000] - 间隔时间(毫秒)\r\n * @throws {TypeError} 当 `fn` 不是函数时抛出\r\n */\r\n constructor(fn, ms = 1000) {\r\n if (typeof fn !== \"function\") {\r\n throw new TypeError(\"IntervalTimer: 必须传入一个函数\");\r\n }\r\n this._fn = fn;\r\n this._ms = ms;\r\n this._timerId = null;\r\n }\r\n\r\n /**\r\n * 启动定时器;若已启动则先停止再重新启动。\r\n * 首次执行会立即触发。\r\n */\r\n start() {\r\n this.stop();\r\n const loop = () => {\r\n this._fn(); // 执行业务\r\n this._timerId = setTimeout(loop, this._ms);\r\n };\r\n loop(); // 立即执行第一次\r\n }\r\n\r\n /**\r\n * 停止定时器。\r\n */\r\n stop() {\r\n if (this._timerId !== null) {\r\n clearTimeout(this._timerId);\r\n this._timerId = null;\r\n }\r\n }\r\n\r\n /**\r\n * 查询定时器是否正在运行。\r\n * @returns {boolean}\r\n */\r\n isRunning() {\r\n return this._timerId !== null;\r\n }\r\n}\r\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACO,MAAM,aAAa,CAAC;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,WAAW,CAAC,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE;AAC/B,QAAQ,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE;AACtC,YAAY,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAC;AAC3D,QAAQ,CAAC;AACT,QAAQ,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;AACtB,QAAQ,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;AACtB,QAAQ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC7B,IAAI,CAAC;AACL;AACA;AACA;AACA;AACA;AACA,IAAI,KAAK,GAAG;AACZ,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;AACpB,QAAQ,MAAM,IAAI,GAAG,MAAM;AAC3B,YAAY,IAAI,CAAC,GAAG,EAAE,CAAC;AACvB,YAAY,IAAI,CAAC,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;AACvD,QAAQ,CAAC,CAAC;AACV,QAAQ,IAAI,EAAE,CAAC;AACf,IAAI,CAAC;AACL;AACA;AACA;AACA;AACA,IAAI,IAAI,GAAG;AACX,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE;AACpC,YAAY,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxC,YAAY,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;AACjC,QAAQ,CAAC;AACT,IAAI,CAAC;AACL;AACA;AACA;AACA;AACA;AACA,IAAI,SAAS,GAAG;AAChB,QAAQ,OAAO,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC;AACtC,IAAI,CAAC;AACL;;;;"}
1
+ {"version":3,"file":"timer.js","sources":["../src/source/timer.js"],"sourcesContent":["/**\r\n * 基于 `setTimeout` 的“间隔循环”定时器。\r\n * 每次任务执行完成后才计算下一次间隔,避免任务堆积。\r\n */\r\nexport class IntervalTimer {\r\n \r\n /**\r\n * 创建定时器实例。\r\n * @param {() => void} fn - 每次间隔要执行的业务函数\r\n * @param {number} [ms=1000] - 间隔时间(毫秒)\r\n * @throws {TypeError} 当 `fn` 不是函数时抛出\r\n */\r\n constructor(fn, ms = 1000) {\r\n if (typeof fn !== \"function\") {\r\n throw new TypeError(\"IntervalTimer: 必须传入一个函数\");\r\n }\r\n this._fn = fn;\r\n this._ms = ms;\r\n this._timerId = null;\r\n /**\r\n * @type {symbol | null}\r\n */\r\n this._loopId = null;\r\n }\r\n\r\n /**\r\n * 启动定时器;若已启动则先停止再重新启动。\r\n * 首次执行会立即触发。\r\n */\r\n start() {\r\n this.stop();\r\n\r\n const curLoopId = Symbol();\r\n this._loopId = curLoopId;\r\n\r\n const loop = () => {\r\n if (this._loopId !== curLoopId) return;\r\n\r\n this._fn(); // 执行业务\r\n\r\n if (this._loopId !== curLoopId) return;\r\n\r\n this._timerId = setTimeout(loop, this._ms);\r\n };\r\n loop(); // 立即执行第一次\r\n }\r\n\r\n /**\r\n * 停止定时器。\r\n */\r\n stop() {\r\n this._loopId = null;\r\n if (this._timerId !== null) {\r\n clearTimeout(this._timerId);\r\n this._timerId = null;\r\n }\r\n }\r\n\r\n /**\r\n * 查询定时器是否正在运行。\r\n * @returns {boolean}\r\n */\r\n isRunning() {\r\n return this._timerId !== null;\r\n }\r\n}\r\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACO,MAAM,aAAa,CAAC;AAC3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,WAAW,CAAC,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE;AAC/B,QAAQ,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE;AACtC,YAAY,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAC;AAC3D,QAAQ,CAAC;AACT,QAAQ,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;AACtB,QAAQ,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;AACtB,QAAQ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC7B;AACA;AACA;AACA,QAAQ,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;AAC5B,IAAI,CAAC;AACL;AACA;AACA;AACA;AACA;AACA,IAAI,KAAK,GAAG;AACZ,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;AACpB;AACA,QAAQ,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC;AACnC,QAAQ,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;AACjC;AACA,QAAQ,MAAM,IAAI,GAAG,MAAM;AAC3B,YAAY,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,OAAO;AACnD;AACA,YAAY,IAAI,CAAC,GAAG,EAAE,CAAC;AACvB;AACA,YAAY,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,OAAO;AACnD;AACA,YAAY,IAAI,CAAC,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;AACvD,QAAQ,CAAC,CAAC;AACV,QAAQ,IAAI,EAAE,CAAC;AACf,IAAI,CAAC;AACL;AACA;AACA;AACA;AACA,IAAI,IAAI,GAAG;AACX,QAAQ,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;AAC5B,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE;AACpC,YAAY,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxC,YAAY,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;AACjC,QAAQ,CAAC;AACT,IAAI,CAAC;AACL;AACA;AACA;AACA;AACA;AACA,IAAI,SAAS,GAAG;AAChB,QAAQ,OAAO,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC;AACtC,IAAI,CAAC;AACL;;"}
package/dist/tree.cjs CHANGED
@@ -237,12 +237,189 @@ function findTreeNodePath(nodes, targetValue, key = "id", parentKey = "pid", chi
237
237
  }
238
238
 
239
239
  return path.reverse();
240
+ }
241
+
242
+ /**
243
+ * 处理函数的返回值类型,用于控制节点是否添加。
244
+ * @typedef {Object} TransformTreeDataWithExtra_HandlerResult
245
+ * @property {boolean} [isAdd] - 是否将该节点添加到树中。如果为 false,则过滤掉该节点。
246
+ */
247
+
248
+ /**
249
+ * 最终输出的树形结构的字段配置。
250
+ * @typedef {Object} TransformTreeDataWithExtra_TreeDataOption
251
+ * @property {string} [idKey="key"] - 最终树节点的唯一标识字段名。
252
+ * @property {string} [titleKey="title"] - 最终树节点的标题显示字段名。
253
+ * @property {string} [childrenKey="children"] - 最终树节点的子节点列表字段名。
254
+ */
255
+
256
+ /**
257
+ * 主数据(父节点)的相关配置选项。
258
+ * @typedef {Object} TransformTreeDataWithExtra_NodeOption
259
+ * @property {string} [idPrefix] - 节点 ID 的前缀。
260
+ * @property {string} [idKey="id"] - 原始数据中作为唯一标识的字段名。
261
+ * @property {string} [titleKey="name"] - 原始数据中作为标题的字段名。
262
+ * @property {string} [childrenKey="children"] - 原始数据中子节点列表的字段名。
263
+ * @property {Record<string, any>} [assignData] - 需要额外合并到每个树节点上的静态属性。
264
+ * @property {string} [rawDataKey] - 如果提供,会将原始节点数据对象挂载到树节点的此字段下。
265
+ * @property {Object<string, any>} [nodeDataId2Obj] - (副作用) 引用传递的对象,用于建立 id -> 原始数据的映射。
266
+ * @property {function(object, object): TransformTreeDataWithExtra_HandlerResult | void} [nodeDataHandler] - 对每个主节点进行处理的回调函数。
267
+ */
268
+
269
+ /**
270
+ * 额外数据(子节点)的相关配置选项。
271
+ * @typedef {Object} TransformTreeDataWithExtra_ExtraOption
272
+ * @property {string} [idPrefix] - 额外节点 ID 的前缀。
273
+ * @property {string} [idKey="id"] - 额外数据中作为唯一标识的字段名。
274
+ * @property {string} [titleKey="name"] - 额外数据中作为标题的字段名。
275
+ * @property {string} [childrenKey="extraChildren"] - 在主节点上查找额外数据列表的字段名。
276
+ * @property {Record<string, any>} [assignData] - 需要额外合并到每个额外树节点上的静态属性。
277
+ * @property {string} [rawDataKey] - 如果提供,会将原始额外数据对象挂载到树节点的此字段下。
278
+ * @property {Object<string, any>} [extraDataId2Obj] - (副作用) 引用传递的对象,用于建立 id -> 额外数据的映射。
279
+ * @property {function(object, object, object): TransformTreeDataWithExtra_HandlerResult | void} [extraDataHandler] - 对每个额外节点进行处理的回调函数。
280
+ */
281
+
282
+ /**
283
+ * 将平铺或嵌套的原始数据转换为标准树形结构。
284
+ * 支持将外部“额外数据”节点作为子节点插入到主节点层级中。
285
+ * 支持自定义字段映射、ID 前缀、数据挂载以及通过回调函数过滤节点。
286
+ *
287
+ * @template T
288
+ * @param {Array<T>} data - 原始数据数组。
289
+ * @param {TransformTreeDataWithExtra_ExtraOption} [extraOption] - 额外数据(子节点)的相关配置选项。
290
+ * @param {TransformTreeDataWithExtra_NodeOption} [nodeOption] - 主数据(父节点)的相关配置选项。
291
+ * @param {TransformTreeDataWithExtra_TreeDataOption} [treeDataOption] - 最终输出的树形结构的字段配置选项。
292
+ * @returns {Array<object>} 转换后的树形数据数组。
293
+ */
294
+ function transformTreeDataWithExtra(data, extraOption = {}, nodeOption = {}, treeDataOption = {}) {
295
+ const { idPrefix: nodeIdPrefix, idKey: nodeIdKey = "id", titleKey: nodeTitleKey = "name", childrenKey: nodeChildrenKey = "children", assignData: nodeAssignData = {}, rawDataKey: nodeRawDataKey, nodeDataId2Obj, nodeDataHandler } = nodeOption;
296
+ const { idPrefix: extraIdPrefix, idKey: extraIdKey = "id", titleKey: extraTitleKey = "name", childrenKey: extraChildrenKey = "extraChildren", assignData: extraAssignData = {}, rawDataKey: extraRawDataKey, extraDataId2Obj, extraDataHandler } = extraOption;
297
+
298
+ const { idKey: treeIdKey = "key", titleKey: treeTitleKey = "title", childrenKey: treeChildrenKey = "children" } = treeDataOption;
299
+
300
+ const transformNode = (node) => {
301
+ const treeNode = {
302
+ [treeIdKey]: isNonEmptyString(nodeIdPrefix) ? nodeIdPrefix + node[nodeIdKey] : node[nodeIdKey],
303
+ [treeTitleKey]: node[nodeTitleKey],
304
+ [treeChildrenKey]: [],
305
+ ...nodeAssignData
306
+ };
307
+ if (isNonEmptyString(nodeRawDataKey)) {
308
+ treeNode[nodeRawDataKey] = node;
309
+ }
310
+
311
+ if (Array.isArray(node[nodeChildrenKey]) && node[nodeChildrenKey].length > 0) {
312
+ treeNode[treeChildrenKey] = node[nodeChildrenKey].map((child) => transformNode(child)).filter((child) => child !== null);
313
+ }
314
+
315
+ if (Array.isArray(node[extraChildrenKey]) && node[extraChildrenKey].length > 0) {
316
+ const isValidExtraDataId2Obj = typeof extraDataId2Obj === "object" && extraDataId2Obj !== null;
317
+ const extraNodes = [];
318
+ for (const extraItem of node[extraChildrenKey]) {
319
+ if (isValidExtraDataId2Obj) {
320
+ extraDataId2Obj[extraItem[extraIdKey]] = extraItem;
321
+ }
322
+ const extraNode = {
323
+ [treeIdKey]: isNonEmptyString(extraIdPrefix) ? extraIdPrefix + extraItem[extraIdKey] : extraItem[extraIdKey],
324
+ [treeTitleKey]: extraItem[extraTitleKey],
325
+ [treeChildrenKey]: [],
326
+ ...extraAssignData
327
+ };
328
+ if (isNonEmptyString(extraRawDataKey)) {
329
+ extraNode[extraRawDataKey] = extraItem;
330
+ }
331
+ if (typeof extraDataHandler === "function") {
332
+ const result = extraDataHandler(extraNode, extraItem, node);
333
+ if (result?.isAdd === false) continue;
334
+ }
335
+ extraNodes.push(extraNode);
336
+ }
337
+ treeNode[treeChildrenKey] = [...treeNode[treeChildrenKey], ...extraNodes];
338
+ }
339
+
340
+ if (typeof nodeDataId2Obj === "object" && nodeDataId2Obj !== null) {
341
+ nodeDataId2Obj[node[nodeIdKey]] = node;
342
+ }
343
+
344
+ if (typeof nodeDataHandler === "function") {
345
+ const result = nodeDataHandler(treeNode, node);
346
+ if (result?.isAdd === false) {
347
+ return null;
348
+ }
349
+ }
350
+
351
+ return treeNode;
352
+ };
353
+
354
+ return data.map((item) => transformNode(item)).filter((item) => item !== null);
355
+ }
356
+
357
+ /**
358
+ * 自定义过滤函数的类型定义。
359
+ * @callback FilterTreeData_FilterFn
360
+ * @param {object} node - 当前遍历到的树节点对象。
361
+ * @param {string | number} filterValue - 当前用于过滤的值。
362
+ * @param {Array<object>} [children] - 当前节点的子节点数组(原始数据)。
363
+ * @returns {boolean} 返回 true 表示保留该节点,false 表示过滤掉。
364
+ */
365
+
366
+ /**
367
+ * 递归过滤树形数据。
368
+ *
369
+ * 该函数会保留符合条件的节点。特别地,**即使当前节点本身不匹配条件,只要其子孙节点中有匹配项,该节点也会被保留**,
370
+ * 从而保证过滤后的树结构能展示出匹配节点的完整路径。
371
+ *
372
+ * @template T
373
+ * @param {Array<T>} treeData - 源树形数据数组。
374
+ * @param {string | number} filterValue - 用于过滤的关键字。
375
+ * @param {string} [filterKey="name"] - 节点对象中用于匹配的字段名。默认为 "name"。如果提供了 filterFn 则此项无效。
376
+ * @param {string} [childrenKey="children"] - 节点对象中存储子数组的字段名。默认为 "children"。
377
+ * @param {FilterTreeData_FilterFn} [filterFn] - 自定义过滤函数。如果提供,将忽略 filterKey,使用此函数的返回值判断是否匹配。
378
+ * @returns {Array<T>} 过滤后的新树形数据数组。注意:返回的是节点对象的浅拷贝。
379
+ */
380
+ function filterTreeData(treeData, filterValue, filterKey = "name", childrenKey = "children", filterFn) {
381
+ if (!Array.isArray(treeData) || treeData.length === 0) {
382
+ return [];
383
+ }
384
+
385
+ if (filterValue == null || filterValue === "") {
386
+ return treeData.map((node) => ({ ...node }));
387
+ }
388
+
389
+ const result = [];
390
+
391
+ for (const node of treeData) {
392
+ const newNode = { ...node };
393
+ const children = node[childrenKey];
394
+
395
+ // 后代节点
396
+ let isDescendantMatch = false;
397
+ if (Array.isArray(children)) {
398
+ const newChildren = filterTreeData(children, filterValue, filterKey, childrenKey, filterFn);
399
+ newNode[childrenKey] = newChildren;
400
+ isDescendantMatch = newChildren.length > 0;
401
+ }
402
+
403
+ if (isDescendantMatch) {
404
+ result.push(newNode);
405
+ } else {
406
+ // 当前节点
407
+ const isMatch = typeof filterFn === "function" ? filterFn(node, filterValue, children) : node[filterKey]?.toString().includes(filterValue);
408
+
409
+ if (isMatch) {
410
+ result.push(newNode);
411
+ }
412
+ }
413
+ }
414
+ return result;
240
415
  }
241
416
 
242
417
  exports.extractFullyCheckedKeys = extractFullyCheckedKeys;
418
+ exports.filterTreeData = filterTreeData;
243
419
  exports.findObjAttrValueById = findObjAttrValueById;
244
420
  exports.findTreeNodeById = findTreeNodeById;
245
421
  exports.findTreeNodePath = findTreeNodePath;
246
422
  exports.flatCompleteTree2NestedTree = flatCompleteTree2NestedTree;
247
423
  exports.nestedTree2IdMap = nestedTree2IdMap;
424
+ exports.transformTreeDataWithExtra = transformTreeDataWithExtra;
248
425
  //# sourceMappingURL=tree.cjs.map
package/dist/tree.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tree.cjs","sources":["../src/source/tree.js"],"sourcesContent":["/**\r\n * 把嵌套树拍平成 `{ [id]: node }` 映射,同时把原 `children` 置为 `null`。\r\n *\r\n * @template T extends Record<PropertyKey, any>\r\n * @param {T[]} data - 嵌套树森林\r\n * @param {string} [idKey='id'] - 主键字段\r\n * @param {string} [childrenKey='children'] - 子节点字段\r\n * @returns {Record<string, T & { [k in typeof childrenKey]: null }>} id→节点的映射表\r\n */\r\nexport function nestedTree2IdMap(data, idKey = \"id\", childrenKey = \"children\") {\r\n const retObj = {};\r\n function fn(nodes) {\r\n if (Array.isArray(nodes) && nodes.length > 0) {\r\n nodes.forEach((node) => {\r\n retObj[node[idKey]] = { ...node };\r\n retObj[node[idKey]][childrenKey] = null;\r\n\r\n fn(node[childrenKey]);\r\n });\r\n }\r\n }\r\n fn(data);\r\n return retObj;\r\n}\r\n\r\n/**\r\n * 把**已包含完整父子关系**的扁平节点列表还原成嵌套树(森林)。\r\n *\r\n * @template T extends Record<PropertyKey, any>\r\n * @param {T[]} nodes - 扁平节点列表(必须包含 id / parentId)\r\n * @param {number | string} [parentId=0] - 根节点标识值\r\n * @param {Object} [opts] - 字段映射配置\r\n * @param {string} [opts.idKey='id'] - 节点主键\r\n * @param {string} [opts.parentKey='parentId'] - 父节点外键\r\n * @param {string} [opts.childrenKey='children'] - 存放子节点的字段\r\n * @returns {(T & { [k in typeof childrenKey]: T[] })[]} 嵌套树森林\r\n */\r\nexport function flatCompleteTree2NestedTree(nodes, parentId = 0, { idKey = \"id\", parentKey = \"parentId\", childrenKey = \"children\" } = {}) {\r\n const map = new Map(); // id -> node\r\n const items = []; // 多根森林\r\n\r\n // 1. 初始化:保证每个节点都有 children,并存入 map\r\n for (const item of nodes) {\r\n const node = { ...item, [childrenKey]: [] };\r\n map.set(item[idKey], node);\r\n }\r\n\r\n // 2. 建立父子关系\r\n for (const item of nodes) {\r\n const node = map.get(item[idKey]);\r\n const parentIdVal = item[parentKey];\r\n\r\n if (parentIdVal === parentId) {\r\n // 根层\r\n items.push(node);\r\n } else {\r\n // 非根层:找到父节点,把自己挂上去\r\n const parent = map.get(parentIdVal);\r\n if (parent) parent[childrenKey].push(node);\r\n // 如果 parent 不存在,说明数据不完整,可自定义处理\r\n }\r\n }\r\n\r\n return items;\r\n}\r\n\r\n/**\r\n * 在嵌套树中按 `id` 递归查找节点\r\n *\r\n * @template T extends Record<PropertyKey, any>\r\n * @param {string | number} id - 要查找的 id\r\n * @param {T[]} arr - 嵌套树森林\r\n * @param {string} [idKey='id'] - 主键字段\r\n * @param {string} [childrenKey='children'] - 子节点字段\r\n * @returns {T | undefined} 找到的节点;未找到返回 `undefined`\r\n */\r\nexport function findTreeNodeById(id, arr, idKey = \"id\", childrenKey = \"children\") {\r\n if (Array.isArray(arr) && arr.length > 0) {\r\n for (let i = 0; i < arr.length; i++) {\r\n const item = arr[i];\r\n if (item[idKey]?.toString() === id?.toString()) {\r\n return item;\r\n } else if (Array.isArray(item[childrenKey]) && item[childrenKey].length > 0) {\r\n const result = findTreeNodeById(id, item[childrenKey], idKey, childrenKey);\r\n if (result) {\r\n return result;\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 在嵌套树中按 `id` 递归查找节点,并返回其指定属性值。\r\n *\r\n * @template T extends Record<PropertyKey, any>\r\n * @param {string | number} id - 要查找的 id\r\n * @param {T[]} arr - 嵌套树森林\r\n * @param {string} [resultKey='name'] - 需要返回的字段\r\n * @param {string} [idKey='id'] - 主键字段\r\n * @param {string} [childrenKey='children'] - 子节点字段\r\n * @returns {any} 找到的值;未找到返回 `undefined`\r\n */\r\nexport function findObjAttrValueById(id, arr, resultKey = \"name\", idKey = \"id\", childrenKey = \"children\") {\r\n return findTreeNodeById(id, arr, idKey, childrenKey)?.[resultKey];\r\n}\r\n\r\n/**\r\n * 从服务端返回的已选 id 数组里,提取出\r\n * 1. 叶子节点\r\n * 2. 所有子节点都被选中的父节点\r\n * 其余父节点一律丢弃(由前端 Tree 自动算半选)\r\n *\r\n * @param {Array} treeData 完整树\r\n * @param {Array} selectedKeys 后端给的选中 id 数组\r\n * @param {String} idKey 节点唯一字段\r\n * @param {String} childrenKey 子节点字段\r\n * @returns {{checked: string[], halfChecked: string[]}}\r\n */\r\nexport function extractFullyCheckedKeys(treeData, selectedKeys, idKey = \"id\", childrenKey = \"children\") {\r\n const selectedSet = new Set(selectedKeys);\r\n const checked = new Set();\r\n const halfChecked = new Set();\r\n\r\n /* 返回值含义\r\n 0 - 未选中\r\n 1 - 半选\r\n 2 - 全选\r\n */\r\n function dfs(node) {\r\n const nodeId = node[idKey];\r\n const children = node[childrenKey] || [];\r\n\r\n // 叶子\r\n if (!children.length) {\r\n if (selectedSet.has(nodeId)) {\r\n checked.add(nodeId);\r\n return 2;\r\n }\r\n return 0;\r\n }\r\n\r\n // 非叶子\r\n let allChecked = true;\r\n let someChecked = false;\r\n\r\n children.forEach((child) => {\r\n const childState = dfs(child);\r\n if (childState !== 2) allChecked = false;\r\n if (childState >= 1) someChecked = true;\r\n });\r\n\r\n // 当前节点本身在 selectedKeys 里,但子节点未全选 → 只能算半选\r\n if (selectedSet.has(nodeId)) {\r\n if (allChecked) {\r\n checked.add(nodeId);\r\n return 2;\r\n }\r\n halfChecked.add(nodeId);\r\n return 1;\r\n }\r\n\r\n // 当前节点不在 selectedKeys 里,看子节点\r\n if (allChecked) {\r\n checked.add(nodeId);\r\n return 2;\r\n }\r\n if (someChecked) {\r\n halfChecked.add(nodeId);\r\n return 1;\r\n }\r\n return 0;\r\n }\r\n\r\n treeData.forEach(dfs);\r\n return {\r\n checked: [...checked],\r\n halfChecked: [...halfChecked]\r\n };\r\n}\r\n\r\n/**\r\n * 在树形结构中查找目标节点的完整路径(从根节点到目标节点,含目标节点自身)。\r\n *\r\n * @template T extends Record<PropertyKey, any>\r\n * @param {T[]} nodes - 树形结构森林(支持多根)\r\n * @param {any} targetValue - 目标节点的 key 值\r\n * @param {string} [key='id'] - 节点唯一标识字段\r\n * @param {string} [parentKey='pid'] - 父节点标识字段(指向父节点的 key 值)\r\n * @param {string} [childrenKey='children'] - 子节点数组字段\r\n * @returns {T[]} 从根到目标节点的路径数组;未找到返回空数组\r\n *\r\n * @example\r\n * const tree = [\r\n * { id: 1, pid: null, children: [\r\n * { id: 2, pid: 1, children: [\r\n * { id: 3, pid: 2 }\r\n * ]}\r\n * ]}\r\n * ];\r\n * findTreeNodePath(tree, 3); // [{id:1, ...}, {id:2, ...}, {id:3, ...}]\r\n */\r\nexport function findTreeNodePath(nodes, targetValue, key = \"id\", parentKey = \"pid\", childrenKey = \"children\") {\r\n if (!Array.isArray(nodes) || nodes.length === 0 || targetValue == null) {\r\n return [];\r\n }\r\n\r\n // 1. 建立节点索引\r\n const index = new Map();\r\n const stack = [...nodes];\r\n\r\n while (stack.length) {\r\n const node = stack.pop();\r\n if (!node || typeof node !== \"object\") continue;\r\n\r\n index.set(node[key], node);\r\n\r\n const children = node[childrenKey];\r\n if (Array.isArray(children)) {\r\n stack.push(...children);\r\n }\r\n }\r\n\r\n // 2. 回溯路径(防循环引用)\r\n const path = [];\r\n const visited = new Set();\r\n let cur = index.get(targetValue);\r\n\r\n while (cur && !visited.has(cur[key])) {\r\n visited.add(cur[key]);\r\n path.push(cur);\r\n\r\n const parentValue = cur[parentKey];\r\n cur = parentValue != null ? index.get(parentValue) : undefined;\r\n }\r\n\r\n return path.reverse();\r\n}\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tree.cjs","sources":["../src/source/tree.js"],"sourcesContent":["/**\r\n * 把嵌套树拍平成 `{ [id]: node }` 映射,同时把原 `children` 置为 `null`。\r\n *\r\n * @template T extends Record<PropertyKey, any>\r\n * @param {T[]} data - 嵌套树森林\r\n * @param {string} [idKey='id'] - 主键字段\r\n * @param {string} [childrenKey='children'] - 子节点字段\r\n * @returns {Record<string, T & { [k in typeof childrenKey]: null }>} id→节点的映射表\r\n */\r\nexport function nestedTree2IdMap(data, idKey = \"id\", childrenKey = \"children\") {\r\n const retObj = {};\r\n function fn(nodes) {\r\n if (Array.isArray(nodes) && nodes.length > 0) {\r\n nodes.forEach((node) => {\r\n retObj[node[idKey]] = { ...node };\r\n retObj[node[idKey]][childrenKey] = null;\r\n\r\n fn(node[childrenKey]);\r\n });\r\n }\r\n }\r\n fn(data);\r\n return retObj;\r\n}\r\n\r\n/**\r\n * 把**已包含完整父子关系**的扁平节点列表还原成嵌套树(森林)。\r\n *\r\n * @template T extends Record<PropertyKey, any>\r\n * @param {T[]} nodes - 扁平节点列表(必须包含 id / parentId)\r\n * @param {number | string} [parentId=0] - 根节点标识值\r\n * @param {Object} [opts] - 字段映射配置\r\n * @param {string} [opts.idKey='id'] - 节点主键\r\n * @param {string} [opts.parentKey='parentId'] - 父节点外键\r\n * @param {string} [opts.childrenKey='children'] - 存放子节点的字段\r\n * @returns {(T & { [k in typeof childrenKey]: T[] })[]} 嵌套树森林\r\n */\r\nexport function flatCompleteTree2NestedTree(nodes, parentId = 0, { idKey = \"id\", parentKey = \"parentId\", childrenKey = \"children\" } = {}) {\r\n const map = new Map(); // id -> node\r\n const items = []; // 多根森林\r\n\r\n // 1. 初始化:保证每个节点都有 children,并存入 map\r\n for (const item of nodes) {\r\n const node = { ...item, [childrenKey]: [] };\r\n map.set(item[idKey], node);\r\n }\r\n\r\n // 2. 建立父子关系\r\n for (const item of nodes) {\r\n const node = map.get(item[idKey]);\r\n const parentIdVal = item[parentKey];\r\n\r\n if (parentIdVal === parentId) {\r\n // 根层\r\n items.push(node);\r\n } else {\r\n // 非根层:找到父节点,把自己挂上去\r\n const parent = map.get(parentIdVal);\r\n if (parent) parent[childrenKey].push(node);\r\n // 如果 parent 不存在,说明数据不完整,可自定义处理\r\n }\r\n }\r\n\r\n return items;\r\n}\r\n\r\n/**\r\n * 在嵌套树中按 `id` 递归查找节点\r\n *\r\n * @template T extends Record<PropertyKey, any>\r\n * @param {string | number} id - 要查找的 id\r\n * @param {T[]} arr - 嵌套树森林\r\n * @param {string} [idKey='id'] - 主键字段\r\n * @param {string} [childrenKey='children'] - 子节点字段\r\n * @returns {T | undefined} 找到的节点;未找到返回 `undefined`\r\n */\r\nexport function findTreeNodeById(id, arr, idKey = \"id\", childrenKey = \"children\") {\r\n if (Array.isArray(arr) && arr.length > 0) {\r\n for (let i = 0; i < arr.length; i++) {\r\n const item = arr[i];\r\n if (item[idKey]?.toString() === id?.toString()) {\r\n return item;\r\n } else if (Array.isArray(item[childrenKey]) && item[childrenKey].length > 0) {\r\n const result = findTreeNodeById(id, item[childrenKey], idKey, childrenKey);\r\n if (result) {\r\n return result;\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 在嵌套树中按 `id` 递归查找节点,并返回其指定属性值。\r\n *\r\n * @template T extends Record<PropertyKey, any>\r\n * @param {string | number} id - 要查找的 id\r\n * @param {T[]} arr - 嵌套树森林\r\n * @param {string} [resultKey='name'] - 需要返回的字段\r\n * @param {string} [idKey='id'] - 主键字段\r\n * @param {string} [childrenKey='children'] - 子节点字段\r\n * @returns {any} 找到的值;未找到返回 `undefined`\r\n */\r\nexport function findObjAttrValueById(id, arr, resultKey = \"name\", idKey = \"id\", childrenKey = \"children\") {\r\n return findTreeNodeById(id, arr, idKey, childrenKey)?.[resultKey];\r\n}\r\n\r\n/**\r\n * 从服务端返回的已选 id 数组里,提取出\r\n * 1. 叶子节点\r\n * 2. 所有子节点都被选中的父节点\r\n * 其余父节点一律丢弃(由前端 Tree 自动算半选)\r\n *\r\n * @param {Array} treeData 完整树\r\n * @param {Array} selectedKeys 后端给的选中 id 数组\r\n * @param {String} idKey 节点唯一字段\r\n * @param {String} childrenKey 子节点字段\r\n * @returns {{checked: string[], halfChecked: string[]}}\r\n */\r\nexport function extractFullyCheckedKeys(treeData, selectedKeys, idKey = \"id\", childrenKey = \"children\") {\r\n const selectedSet = new Set(selectedKeys);\r\n const checked = new Set();\r\n const halfChecked = new Set();\r\n\r\n /* 返回值含义\r\n 0 - 未选中\r\n 1 - 半选\r\n 2 - 全选\r\n */\r\n function dfs(node) {\r\n const nodeId = node[idKey];\r\n const children = node[childrenKey] || [];\r\n\r\n // 叶子\r\n if (!children.length) {\r\n if (selectedSet.has(nodeId)) {\r\n checked.add(nodeId);\r\n return 2;\r\n }\r\n return 0;\r\n }\r\n\r\n // 非叶子\r\n let allChecked = true;\r\n let someChecked = false;\r\n\r\n children.forEach((child) => {\r\n const childState = dfs(child);\r\n if (childState !== 2) allChecked = false;\r\n if (childState >= 1) someChecked = true;\r\n });\r\n\r\n // 当前节点本身在 selectedKeys 里,但子节点未全选 → 只能算半选\r\n if (selectedSet.has(nodeId)) {\r\n if (allChecked) {\r\n checked.add(nodeId);\r\n return 2;\r\n }\r\n halfChecked.add(nodeId);\r\n return 1;\r\n }\r\n\r\n // 当前节点不在 selectedKeys 里,看子节点\r\n if (allChecked) {\r\n checked.add(nodeId);\r\n return 2;\r\n }\r\n if (someChecked) {\r\n halfChecked.add(nodeId);\r\n return 1;\r\n }\r\n return 0;\r\n }\r\n\r\n treeData.forEach(dfs);\r\n return {\r\n checked: [...checked],\r\n halfChecked: [...halfChecked]\r\n };\r\n}\r\n\r\n/**\r\n * 在树形结构中查找目标节点的完整路径(从根节点到目标节点,含目标节点自身)。\r\n *\r\n * @template T extends Record<PropertyKey, any>\r\n * @param {T[]} nodes - 树形结构森林(支持多根)\r\n * @param {any} targetValue - 目标节点的 key 值\r\n * @param {string} [key='id'] - 节点唯一标识字段\r\n * @param {string} [parentKey='pid'] - 父节点标识字段(指向父节点的 key 值)\r\n * @param {string} [childrenKey='children'] - 子节点数组字段\r\n * @returns {T[]} 从根到目标节点的路径数组;未找到返回空数组\r\n *\r\n * @example\r\n * const tree = [\r\n * { id: 1, pid: null, children: [\r\n * { id: 2, pid: 1, children: [\r\n * { id: 3, pid: 2 }\r\n * ]}\r\n * ]}\r\n * ];\r\n * findTreeNodePath(tree, 3); // [{id:1, ...}, {id:2, ...}, {id:3, ...}]\r\n */\r\nexport function findTreeNodePath(nodes, targetValue, key = \"id\", parentKey = \"pid\", childrenKey = \"children\") {\r\n if (!Array.isArray(nodes) || nodes.length === 0 || targetValue == null) {\r\n return [];\r\n }\r\n\r\n // 1. 建立节点索引\r\n const index = new Map();\r\n const stack = [...nodes];\r\n\r\n while (stack.length) {\r\n const node = stack.pop();\r\n if (!node || typeof node !== \"object\") continue;\r\n\r\n index.set(node[key], node);\r\n\r\n const children = node[childrenKey];\r\n if (Array.isArray(children)) {\r\n stack.push(...children);\r\n }\r\n }\r\n\r\n // 2. 回溯路径(防循环引用)\r\n const path = [];\r\n const visited = new Set();\r\n let cur = index.get(targetValue);\r\n\r\n while (cur && !visited.has(cur[key])) {\r\n visited.add(cur[key]);\r\n path.push(cur);\r\n\r\n const parentValue = cur[parentKey];\r\n cur = parentValue != null ? index.get(parentValue) : undefined;\r\n }\r\n\r\n return path.reverse();\r\n}\r\n\r\n/**\r\n * 处理函数的返回值类型,用于控制节点是否添加。\r\n * @typedef {Object} TransformTreeDataWithExtra_HandlerResult\r\n * @property {boolean} [isAdd] - 是否将该节点添加到树中。如果为 false,则过滤掉该节点。\r\n */\r\n\r\n/**\r\n * 最终输出的树形结构的字段配置。\r\n * @typedef {Object} TransformTreeDataWithExtra_TreeDataOption\r\n * @property {string} [idKey=\"key\"] - 最终树节点的唯一标识字段名。\r\n * @property {string} [titleKey=\"title\"] - 最终树节点的标题显示字段名。\r\n * @property {string} [childrenKey=\"children\"] - 最终树节点的子节点列表字段名。\r\n */\r\n\r\n/**\r\n * 主数据(父节点)的相关配置选项。\r\n * @typedef {Object} TransformTreeDataWithExtra_NodeOption\r\n * @property {string} [idPrefix] - 节点 ID 的前缀。\r\n * @property {string} [idKey=\"id\"] - 原始数据中作为唯一标识的字段名。\r\n * @property {string} [titleKey=\"name\"] - 原始数据中作为标题的字段名。\r\n * @property {string} [childrenKey=\"children\"] - 原始数据中子节点列表的字段名。\r\n * @property {Record<string, any>} [assignData] - 需要额外合并到每个树节点上的静态属性。\r\n * @property {string} [rawDataKey] - 如果提供,会将原始节点数据对象挂载到树节点的此字段下。\r\n * @property {Object<string, any>} [nodeDataId2Obj] - (副作用) 引用传递的对象,用于建立 id -> 原始数据的映射。\r\n * @property {function(object, object): TransformTreeDataWithExtra_HandlerResult | void} [nodeDataHandler] - 对每个主节点进行处理的回调函数。\r\n */\r\n\r\n/**\r\n * 额外数据(子节点)的相关配置选项。\r\n * @typedef {Object} TransformTreeDataWithExtra_ExtraOption\r\n * @property {string} [idPrefix] - 额外节点 ID 的前缀。\r\n * @property {string} [idKey=\"id\"] - 额外数据中作为唯一标识的字段名。\r\n * @property {string} [titleKey=\"name\"] - 额外数据中作为标题的字段名。\r\n * @property {string} [childrenKey=\"extraChildren\"] - 在主节点上查找额外数据列表的字段名。\r\n * @property {Record<string, any>} [assignData] - 需要额外合并到每个额外树节点上的静态属性。\r\n * @property {string} [rawDataKey] - 如果提供,会将原始额外数据对象挂载到树节点的此字段下。\r\n * @property {Object<string, any>} [extraDataId2Obj] - (副作用) 引用传递的对象,用于建立 id -> 额外数据的映射。\r\n * @property {function(object, object, object): TransformTreeDataWithExtra_HandlerResult | void} [extraDataHandler] - 对每个额外节点进行处理的回调函数。\r\n */\r\n\r\n/**\r\n * 将平铺或嵌套的原始数据转换为标准树形结构。\r\n * 支持将外部“额外数据”节点作为子节点插入到主节点层级中。\r\n * 支持自定义字段映射、ID 前缀、数据挂载以及通过回调函数过滤节点。\r\n *\r\n * @template T\r\n * @param {Array<T>} data - 原始数据数组。\r\n * @param {TransformTreeDataWithExtra_ExtraOption} [extraOption] - 额外数据(子节点)的相关配置选项。\r\n * @param {TransformTreeDataWithExtra_NodeOption} [nodeOption] - 主数据(父节点)的相关配置选项。\r\n * @param {TransformTreeDataWithExtra_TreeDataOption} [treeDataOption] - 最终输出的树形结构的字段配置选项。\r\n * @returns {Array<object>} 转换后的树形数据数组。\r\n */\r\nexport function transformTreeDataWithExtra(data, extraOption = {}, nodeOption = {}, treeDataOption = {}) {\r\n const { idPrefix: nodeIdPrefix, idKey: nodeIdKey = \"id\", titleKey: nodeTitleKey = \"name\", childrenKey: nodeChildrenKey = \"children\", assignData: nodeAssignData = {}, rawDataKey: nodeRawDataKey, nodeDataId2Obj, nodeDataHandler } = nodeOption;\r\n const { idPrefix: extraIdPrefix, idKey: extraIdKey = \"id\", titleKey: extraTitleKey = \"name\", childrenKey: extraChildrenKey = \"extraChildren\", assignData: extraAssignData = {}, rawDataKey: extraRawDataKey, extraDataId2Obj, extraDataHandler } = extraOption;\r\n\r\n const { idKey: treeIdKey = \"key\", titleKey: treeTitleKey = \"title\", childrenKey: treeChildrenKey = \"children\" } = treeDataOption;\r\n\r\n const transformNode = (node) => {\r\n const treeNode = {\r\n [treeIdKey]: isNonEmptyString(nodeIdPrefix) ? nodeIdPrefix + node[nodeIdKey] : node[nodeIdKey],\r\n [treeTitleKey]: node[nodeTitleKey],\r\n [treeChildrenKey]: [],\r\n ...nodeAssignData\r\n };\r\n if (isNonEmptyString(nodeRawDataKey)) {\r\n treeNode[nodeRawDataKey] = node;\r\n }\r\n\r\n if (Array.isArray(node[nodeChildrenKey]) && node[nodeChildrenKey].length > 0) {\r\n treeNode[treeChildrenKey] = node[nodeChildrenKey].map((child) => transformNode(child)).filter((child) => child !== null);\r\n }\r\n\r\n if (Array.isArray(node[extraChildrenKey]) && node[extraChildrenKey].length > 0) {\r\n const isValidExtraDataId2Obj = typeof extraDataId2Obj === \"object\" && extraDataId2Obj !== null;\r\n const extraNodes = [];\r\n for (const extraItem of node[extraChildrenKey]) {\r\n if (isValidExtraDataId2Obj) {\r\n extraDataId2Obj[extraItem[extraIdKey]] = extraItem;\r\n }\r\n const extraNode = {\r\n [treeIdKey]: isNonEmptyString(extraIdPrefix) ? extraIdPrefix + extraItem[extraIdKey] : extraItem[extraIdKey],\r\n [treeTitleKey]: extraItem[extraTitleKey],\r\n [treeChildrenKey]: [],\r\n ...extraAssignData\r\n };\r\n if (isNonEmptyString(extraRawDataKey)) {\r\n extraNode[extraRawDataKey] = extraItem;\r\n }\r\n if (typeof extraDataHandler === \"function\") {\r\n const result = extraDataHandler(extraNode, extraItem, node);\r\n if (result?.isAdd === false) continue;\r\n }\r\n extraNodes.push(extraNode);\r\n }\r\n treeNode[treeChildrenKey] = [...treeNode[treeChildrenKey], ...extraNodes];\r\n }\r\n\r\n if (typeof nodeDataId2Obj === \"object\" && nodeDataId2Obj !== null) {\r\n nodeDataId2Obj[node[nodeIdKey]] = node;\r\n }\r\n\r\n if (typeof nodeDataHandler === \"function\") {\r\n const result = nodeDataHandler(treeNode, node);\r\n if (result?.isAdd === false) {\r\n return null;\r\n }\r\n }\r\n\r\n return treeNode;\r\n };\r\n\r\n return data.map((item) => transformNode(item)).filter((item) => item !== null);\r\n}\r\n\r\n/**\r\n * 自定义过滤函数的类型定义。\r\n * @callback FilterTreeData_FilterFn\r\n * @param {object} node - 当前遍历到的树节点对象。\r\n * @param {string | number} filterValue - 当前用于过滤的值。\r\n * @param {Array<object>} [children] - 当前节点的子节点数组(原始数据)。\r\n * @returns {boolean} 返回 true 表示保留该节点,false 表示过滤掉。\r\n */\r\n\r\n/**\r\n * 递归过滤树形数据。\r\n *\r\n * 该函数会保留符合条件的节点。特别地,**即使当前节点本身不匹配条件,只要其子孙节点中有匹配项,该节点也会被保留**,\r\n * 从而保证过滤后的树结构能展示出匹配节点的完整路径。\r\n *\r\n * @template T\r\n * @param {Array<T>} treeData - 源树形数据数组。\r\n * @param {string | number} filterValue - 用于过滤的关键字。\r\n * @param {string} [filterKey=\"name\"] - 节点对象中用于匹配的字段名。默认为 \"name\"。如果提供了 filterFn 则此项无效。\r\n * @param {string} [childrenKey=\"children\"] - 节点对象中存储子数组的字段名。默认为 \"children\"。\r\n * @param {FilterTreeData_FilterFn} [filterFn] - 自定义过滤函数。如果提供,将忽略 filterKey,使用此函数的返回值判断是否匹配。\r\n * @returns {Array<T>} 过滤后的新树形数据数组。注意:返回的是节点对象的浅拷贝。\r\n */\r\nexport function filterTreeData(treeData, filterValue, filterKey = \"name\", childrenKey = \"children\", filterFn) {\r\n if (!Array.isArray(treeData) || treeData.length === 0) {\r\n return [];\r\n }\r\n\r\n if (filterValue == null || filterValue === \"\") {\r\n return treeData.map((node) => ({ ...node }));\r\n }\r\n\r\n const result = [];\r\n\r\n for (const node of treeData) {\r\n const newNode = { ...node };\r\n const children = node[childrenKey];\r\n\r\n // 后代节点\r\n let isDescendantMatch = false;\r\n if (Array.isArray(children)) {\r\n const newChildren = filterTreeData(children, filterValue, filterKey, childrenKey, filterFn);\r\n newNode[childrenKey] = newChildren;\r\n isDescendantMatch = newChildren.length > 0;\r\n }\r\n\r\n if (isDescendantMatch) {\r\n result.push(newNode);\r\n } else {\r\n // 当前节点\r\n const isMatch = typeof filterFn === \"function\" ? filterFn(node, filterValue, children) : node[filterKey]?.toString().includes(filterValue);\r\n\r\n if (isMatch) {\r\n result.push(newNode);\r\n }\r\n }\r\n }\r\n return result;\r\n}\r\n"],"names":[],"mappings":"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"}