UNPKG

ivue-material-plus

Version:

A high quality UI components Library with Vue.js

631 lines (516 loc) • 16.2 kB
import { hasOwn, isObject } from '@vue/shared'; import Node from './node'; import { getNodeKey } from '../utils'; // type import type { TreeKey, TreeStoreOptions, TreeData, TreeStoreNodesMap, TreeOptionProps, LoadFunction, TreeNodeData, FilterValue, FilterNodeMethodFunction, } from '../types/tree'; // tree 存储数据对象 export default class TreeStore { // 当前节点的key currentNodeKey: TreeKey; // 当前节点 currentNode: Node; // 根节点数据 root: Node; // tree 数据 data: TreeData; // 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的 key: TreeKey; // 存储tree子节点 nodesMap: TreeStoreNodesMap; // 是否懒加载子节点,需与 load 方法结合使用 lazy: boolean; // tree 配置选项 props: TreeOptionProps; // 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法 checkBoxStrictly: boolean; // 加载子树数据的方法 load: LoadFunction; // 默认勾选的节点的 key 的数组 defaultCheckedKeys: TreeKey[]; // 默认展开的节点的 key 的数组 defaultExpandedKeys: TreeKey[]; // 展开子节点的时候是否自动展开父节点 autoExpandParent: boolean; // 是否默认展开所有节点 defaultExpandAll: boolean; // 对树节点进行筛选时执行的方法, 返回 false 则表示这个节点会被隐藏 filterNodeMethod: FilterNodeMethodFunction; constructor(options: TreeStoreOptions) { // 当前节点的key this.currentNodeKey = null; // 当前节点 this.currentNode = null; // 将外部值赋值给属性 for (const name in options) { // 检测是否属性是否拥有 if (hasOwn(options, name)) { // 赋值给当前 class 属性 this[name] = options[name]; } } // 存储tree子节点 this.nodesMap = {}; } // 初始化数据 initialize() { this.root = new Node({ data: this.data, store: this, }); // 初始化节点属性 this.root.initialize(); // 是懒加载节点 && 有加载子树数据的方法 if (this.lazy && this.load) { this.load(this.root, (data) => { this.root.createChildren(data); // 初始化默认勾选节点 this.initDefaultCheckedNodes(); }); } // 其他节点 else { // 初始化默认勾选节点 this.initDefaultCheckedNodes(); } } // 注册节点 registerNode(node: Node): void { const key = this.key; // 没有节点 || 没有节点数据 if (!node || !node.data) { return; } // 当前树没有唯一key if (!key) { // 用节点的id去存储节点 this.nodesMap[node.id] = node; } // 有定义key else { const nodeKey = node.key; if (nodeKey !== undefined) { // 存储tree子节点 this.nodesMap[node.key] = node; } } } // 点击当前节点 setCurrentNode(currentNode: Node): void { // 上一个点击的节点 const preCurrentNode = this.currentNode; // 初始化上一个节点数据 if (preCurrentNode) { preCurrentNode.isCurrent = false; } // 设置当前节点 this.currentNode = currentNode; this.currentNode.isCurrent = true; } // 获取选中的节点 getCheckedNodes( leafOnly = false, includeHalfChecked = false ): TreeNodeData[] { // 选中的节点数组 const checkedNodes: TreeNodeData[] = []; // 递归遍历子节点 const traverse = (node: TreeStore | Node) => { // 获取子节点数组 const childNodes = (node as TreeStore).root ? (node as TreeStore).root.childNodes : (node as Node).childNodes; childNodes.forEach((child) => { if ( // 选中 || 是不确定的选中状态 (child.checked || (includeHalfChecked && child.indeterminate)) && // 不是叶子节点 || 是叶子节点 (!leafOnly || (leafOnly && child.isLeaf)) ) { checkedNodes.push(child.data); } traverse(child); }); }; traverse(this); return checkedNodes; } // 获取选中节点的key getCheckedKeys(leafOnly = false): TreeKey[] { return this.getCheckedNodes(leafOnly).map((data) => (data || {})[this.key]); } // 获取半选中节点 getHalfCheckedNodes(): TreeNodeData[] { const halfCheckedNodes: TreeNodeData[] = []; // 递归遍历子节点 const traverse = (node: TreeStore | Node) => { // 获取根节点数据的子项 || 子节点数据的子项 const childNodes = (node as TreeStore).root ? (node as TreeStore).root.childNodes : (node as Node).childNodes; childNodes.forEach((child) => { // 有半选中状态 if (child.indeterminate) { halfCheckedNodes.push(child.data); } // 递归遍历子节点 traverse(child); }); }; // 递归遍历子节点 traverse(this); return halfCheckedNodes; } // 获取不确定选中节点key getHalfCheckedKeys(): TreeKey[] { return this.getHalfCheckedNodes().map((data) => (data || {})[this.key]); } // 初始化默认勾选节点 initDefaultCheckedNodes(): void { const defaultCheckedKeys = this.defaultCheckedKeys || []; const nodesMap = this.nodesMap; defaultCheckedKeys.forEach((checkedKey) => { // 获取默认勾选节点 const node = nodesMap[checkedKey]; // 设置勾选 if (node) { node.setChecked(true, !this.checkBoxStrictly); } }); } // 初始化默认勾选节点 initDefaultCheckedNode(node: Node): void { const defaultCheckedKeys = this.defaultCheckedKeys || []; // 激活当前节点 if (defaultCheckedKeys.includes(node.key)) { node.setChecked(true, !this.checkBoxStrictly); } } // 设置目前勾选的节点,使用此方法必须提前设置 node-key 属性 setCheckedNodes(array: Node[], leafOnly = false): void { const key = this.key; const checkedKeys = {}; // 创建多选框key array.forEach((item) => { checkedKeys[(item || {})[key]] = true; }); // 设置多选框key this._setCheckedKeys(key, leafOnly, checkedKeys); } // 设置目前选中的节点 setCheckedKeys(keys: TreeKey[], leafOnly = false): void { const key = this.key; const checkedKeys = {}; // 默认勾选的节点的 this.defaultCheckedKeys = keys; keys.forEach((key) => { checkedKeys[key] = true; }); // 设置多选框key this._setCheckedKeys(key, leafOnly, checkedKeys); } // 设置多选框key _setCheckedKeys( key: TreeKey, leafOnly = false, checkedKeys: { [key: string]: boolean } ): void { const allNodes = this.getAllNodes().sort((a, b) => b.level - a.level); // 父级数据的key const parentKeys = {}; // 外部传入的key const keys = Object.keys(checkedKeys); // 先取消所有多选框选中 allNodes.forEach((node) => { node.setChecked(false, false); }); for (let i = 0; i < allNodes.length; i++) { // 节点 const node = allNodes[i]; // 节点key const nodeKey = node.data[key].toString(); // 匹配外部传入是否选中 const checked = keys.includes(nodeKey); // 取消选中 if (!checked) { // 激活了 && 不是父级数据的子节点 if (node.checked && !parentKeys[nodeKey]) { node.setChecked(false, false); } continue; } let parent = node.parent; // 父级层级 > 0 while (parent && parent.level > 0) { // 父级数据的key parentKeys[parent.data[key]] = true; parent = parent.parent; } // 叶子节点 || 是否严格的遵循父子不互相关联的做法 if (node.isLeaf || this.checkBoxStrictly) { // 选中 node.setChecked(true, false); continue; } // 选中 node.setChecked(true, true); // 叶子节点 if (leafOnly) { // 取消选中 node.setChecked(false, false); // 遍历子节点 const traverse = (node: Node): void => { const childNodes = node.childNodes; childNodes.forEach((child) => { // 不是叶子节点 取消选中 if (!child.isLeaf) { child.setChecked(false, false); } traverse(child); }); }; traverse(node); } } } // 获取所有节点 getAllNodes(): Node[] { const allNodes: Node[] = []; const nodesMap = this.nodesMap; for (const nodeKey in nodesMap) { // 检测是否属性是否拥有 if (hasOwn(nodesMap, nodeKey)) { allNodes.push(nodesMap[nodeKey]); } } return allNodes; } // 设置数据 setData(value: TreeData): void { // 数据不一样 const instanceChanged = value !== this.root.data; if (instanceChanged) { this.root.setData(value); // 初始化默认勾选节点 this.initDefaultCheckedNodes(); } else { // 更新子节点 this.root.updateChildren(); } } // 更新子节点 updateChildren(key: TreeKey, data: TreeData): void { // 获取对应的节点 const node = this.nodesMap[key]; if (!node) { return; } const childNodes = node.childNodes; // 删除节点 -> 从后向前找 for (let i = childNodes.length - 1; i >= 0; i--) { const child = childNodes[i]; this.remove(child.data); } // 添加节点 for (let i = 0, j = data.length; i < j; i++) { const child = data[i]; this.append(child, node.data); } } // 删除节点 remove(data: TreeNodeData | Node): void { // 获取节点 const node = this.getNode(data); // 有节点 && 有父节点 if (node && node.parent) { // 清除当前节点 if (node === this.currentNode) { this.currentNode = null; } node.parent.removeChild(node); } } // 添加节点 append(data: TreeNodeData, parentData: TreeNodeData | TreeKey | Node): void { // 是否有父节点 const parentNode = parentData ? this.getNode(parentData) : this.root; if (parentNode) { parentNode.insertChild({ data }); } } // 获取节点 getNode(data: TreeKey | TreeNodeData): Node { if (data instanceof Node) { return data; } // 获取节点的key const key = isObject(data) ? getNodeKey(this.key, data) : data; // 存储的树节点 return this.nodesMap[key] || null; } // 注销节点 deregisterNode(node: Node): void { const key = this.key; if (!key || !node || !node.data) { return; } // 递归调用 node.childNodes.forEach((child) => { this.deregisterNode(child); }); // 删除存储的树节点 delete this.nodesMap[node.key]; } // 设置默认勾选的节点的 key 数组 setDefaultCheckedKey(value: TreeKey[]): void { if (value !== this.defaultCheckedKeys) { this.defaultCheckedKeys = value; // 初始化默认勾选节点 this.initDefaultCheckedNodes(); } } // 设置默认展开的节点的 key 的数组 setDefaultExpandedKeys(keys: TreeKey[]) { keys = keys || []; this.defaultExpandedKeys = keys; keys.forEach((key) => { // 获取节点 const node = this.getNode(key); // 设置展开 if (node) { node.expand(null, this.autoExpandParent); } }); } // 设置当前激活的key setCurrentNodeKey( // 待被选节点的 key, 如果为 null, 取消当前选中的节点 key?: TreeKey, // 是否自动展开父节点 shouldAutoExpandParent = true ): void { // 没有key if (key === null || key === undefined) { // 重置当前选中的节点 this.currentNode && (this.currentNode.isCurrent = false); this.currentNode = null; return; } // 获取节点 const node = this.getNode(key); // 有节点 if (node) { // 点击当前节点 this.setCurrentNode(node); // 有自动展开 && 节点层级 > 1 if (shouldAutoExpandParent && this.currentNode.level > 1) { // 展开 this.currentNode.parent.expand(null, true); } } } // 设置节点为选中状态 setUserCurrentNode( // 待被选中的节点 node: Node, // 是否自动展开父节点 shouldAutoExpandParent = true ): void { // 传入节点的key const key = node[this.key]; // 获取到当前节点 const currNode = this.nodesMap[key]; // 点击当前节点 this.setCurrentNode(currNode); // 有自动展开 && 节点层级 > 1 if (shouldAutoExpandParent && this.currentNode.level > 1) { this.currentNode.parent.expand(null, true); } } // 过滤所有树节点,过滤后的节点将被隐藏 filter(value: FilterValue): void { const filterNodeMethod = this.filterNodeMethod; const lazy = this.lazy; const traverse = (node: TreeStore | Node) => { // 子节点 const childNodes = (node as TreeStore).root ? (node as TreeStore).root.childNodes : (node as Node).childNodes; // 循环子节点 childNodes.forEach((child) => { child.visible = filterNodeMethod.call(child, value, child.data, child); // 继续循环获取子节点 traverse(child); }); // 没有隐藏 && 有子节点 if (!(node as Node).visible && childNodes.length) { let allHidden = true; // 是否符合隐藏全部条件 allHidden = !childNodes.some((child) => child.visible); // 跟节点 if ((node as TreeStore).root) { // 隐藏根节点 (node as TreeStore).root.visible = allHidden === false; } // 隐藏子节点 else { (node as Node).visible = allHidden === false; } } // 没有输入 if (!value) { return; } // 隐藏了子节点 && 不是叶子节点 && 不是懒加载节点 if ((node as Node).visible && !(node as Node).isLeaf && !lazy) { // 展开节点 (node as Node).expand(); } }; // 遍历子节点 traverse(this); } // 插入到节点前面 insertBefore(data: TreeNodeData, refData: TreeKey | TreeNodeData): void { // 获取节点 const refNode = this.getNode(refData); // 插入到节点 refNode.parent.insertBefore({ data }, refNode); } // 插入到节点后面 insertAfter(data: TreeNodeData, refData: TreeKey | TreeNodeData): void { // 获取节点 const refNode = this.getNode(refData); // 插入到节点 refNode.parent.insertAfter({ data }, refNode); } // 设置节点是否被选中, 使用此方法必须设置 node-key 属性 setChecked( // 要选中的节点的 key 或者数据 data: TreeKey | TreeNodeData, // 一个布尔类型参数表明是否选中 checked: boolean, // 一个布尔类型参数表明是否递归选中子节点 deep: boolean ): void { // 获取节点 const node = this.getNode(data); // 设置选中 if (node) { node.setChecked(!!checked, deep); } } // 返回当前被选中节点的数据 getCurrentNode(): Node { return this.currentNode; } }