import type { TreeData, TreeNestedNode, TreeNode, TreeNodeStateEditable } from '#adata-ui/components/elements/tree-select/types' import highlight from '#adata-ui/composables/highlight' export const useTreeService = ( withoutId?: boolean ) => { const tree = ref | null>(null) const nestedTree = ref([]) const createdTreeBool = ref(false) const initTree = (data: TreeData[]): void => { if (!createdTreeBool.value) { tree.value = generateTree(data, 0) createdTreeBool.value = true updateNestedTree() } } const generateTree = ( data: TreeData[], level: number, parent: string | number | null = null ): Map => { const treeMap = new Map() data.forEach((item) => { const id = item.id const child: (string | number)[] = item.children ? item.children.map((child) => child.id) : [] treeMap.set(id, { id, name: item.name, level, expandable: child.length > 0, expandableClosed: true, child, parent, state: 'unchecked', hidden: false }) if (item.children && item.children.length > 0) { const childTreeMap = generateTree(item.children, level + 1, id) childTreeMap.forEach((value, key) => treeMap.set(key, value)) } }) return treeMap } const updateNestedTree = (searchText: string = '') => { if (!tree.value) return const nodesFromLevel0: TreeNode[] = getNodesByLevel(0) nestedTree.value = nodesFromLevel0.map((flatNode) => createNestedNode(flatNode)) highlightTree(nestedTree.value, searchText) } function highlightTree(nodes, searchText) { nodes.forEach((node) => { node.text = highlight(node.text, searchText); if (node.child.length) { highlightTree(node.child, searchText); } }); } const createNestedNode = (node: TreeNode): TreeNestedNode => { return { id: node.id, name: node.name, text: withoutId ? `${node.name}` : `${node.id} - ${node.name}`, state: node.state, level: node.level, expandable: node.expandable, expandableClosed: node.expandableClosed, hidden: node?.hidden ?? false, child: node.child .map((id) => { const childNode = getNodeById(id) return childNode ? createNestedNode(childNode) : null }) .filter((n) => n) as TreeNestedNode[] } } const getNodeById = (idNode: string | number): TreeNode | null => { if (!tree.value) return null return tree.value.get(idNode) || null } const getChildNodesFromNode = (idNode: string | number): TreeNode[] => { if (!tree.value) return [] const node = getNodeById(idNode) if (!node) return [] return node.child.map((id) => getNodeById(id)).filter((n) => n) as TreeNode[] } const getParentNode = (idNode: string | number): TreeNode | null => { if (!tree.value) return null const node = getNodeById(idNode) if (!node || !node.parent) return null return getNodeById(node.parent) } const getNodesByLevel = (level: number): TreeNode[] => { if (!tree.value) return [] const nodes: TreeNode[] = [] tree.value.forEach((node) => { if (node.level === level) { nodes.push(node) } }) return nodes } const setState = (idNode: string | number, state: TreeNodeStateEditable): void => { if (!tree.value) return const node = getNodeById(idNode) if (!node) return node.state = state tree.value.set(idNode, node) setStateChildNodes(node, state) setStateParentNodes(node) updateNestedTree() } const setStateChildNodes = (node: TreeNode, state: TreeNodeStateEditable): void => { if (!tree.value) return node.child.forEach((id) => { const childNode = getNodeById(id) if (childNode) { childNode.state = state tree.value.set(id, childNode) setStateChildNodes(childNode, state) } }) } const setStateParentNodes = (node: TreeNode): void => { if (!tree.value) return const parentNode = getParentNode(node.id) if (!parentNode) return const childNodes = getChildNodesFromNode(parentNode.id) const checkedCount = childNodes.filter((n) => n.state === 'checked').length const indeterminateCount = childNodes.filter((n) => n.state === 'indeterminate').length if (checkedCount === childNodes.length) { parentNode.state = 'checked' } else if (checkedCount > 0 || indeterminateCount > 0) { parentNode.state = 'indeterminate' } else { parentNode.state = 'unchecked' } tree.value.set(parentNode.id, parentNode) setStateParentNodes(parentNode) } const setExpandableClosed = (idNode: string | number, expandableClosed: boolean): void => { if (!tree.value) return const node = getNodeById(idNode) if (!node) return if (!expandableClosed && node.expandable && node.child.length === 0) { // Здесь можно добавить логику для ленивой загрузки дочерних узлов, если они не были загружены ранее. } node.expandableClosed = expandableClosed tree.value.set(node.id, node) updateNestedTree() } const reset = (): void => { if (!tree.value) return tree.value.forEach((node) => { node.state = 'unchecked' }) updateNestedTree() } const collectSelectedIds = (tree: Map): (string | number)[] => { const selectedIds: Set = new Set() // Функция для проверки, выбраны ли все дочерние узлы const areAllChildrenChecked = (node: TreeNode): boolean => { const childNodes = getChildNodesFromNode(node.id) return childNodes.length > 0 && childNodes.every((child) => child.state === 'checked') } tree.forEach((node) => { if (node.state === 'checked') { if (node.parent && getNodeById(node.parent)?.state === 'checked') { // Пропустить дочерний узел, если родительский узел уже выбран return } else if (areAllChildrenChecked(node)) { // Если все дочерние узлы выбраны, то включить только родительский узел selectedIds.add(node.id) } else { selectedIds.add(node.id) } } }) return Array.from(selectedIds) } function hideSearchedData(data: TreeData[], searchText: string) { data.forEach((node) => { const existingNode = tree.value.get(node.id) if (existingNode) { tree.value.set(node.id, { ...existingNode, hidden: false, expandableClosed: false }) } if (node.children?.length) { hideSearchedData(node.children) } }) } function searchedData(data: TreeData[], searchText: string) { if (!tree.value) return toggleHiddenNodes(true) hideSearchedData(data, searchText) updateNestedTree(searchText) } const toggleHiddenNodes = (hidden: boolean) => { if (!tree.value) return tree.value.forEach((node, key) => { tree.value.set(key, { ...node, hidden }) }) } const toggleExpandableNodes = (expandableClosed: boolean) => { if (!tree.value) return tree.value.forEach((node, key) => { tree.value.set(key, { ...node, expandableClosed }) }) } return { tree, nestedTree, initTree, setState, reset, setExpandableClosed, collectSelectedIds, updateNestedTree, searchedData, toggleHiddenNodes, toggleExpandableNodes, } }