/** * 构建pathTree * 可动态加载子节点,要求子节点获取接口按/xxx/get-children定义 * @param {Object} setting - 设置 * @returns {PathTree} */ 'use strict'; const itemsPre = 'id_'; const createNewPathTree = function (type, setting) { class BaseTree { /** * 构造函数 */ constructor (setting) { // 无索引 this.datas = []; // 以key为索引 this.items = {}; // 以排序为索引 this.nodes = []; // 索引 this.children = {}; // 树设置 this.setting = JSON.parse(JSON.stringify(setting)); } /** * 树结构根据显示排序 */ sortTreeNode (isResort) { const self = this; const addSortNodes = function (nodes) { if (!nodes) { return } for (let i = 0; i < nodes.length; i++) { self.nodes.push(nodes[i]); nodes[i].index = self.nodes.length - 1; if (!isResort) { nodes[i].children = self.getChildren(nodes[i]); } addSortNodes(nodes[i].children); } }; self.nodes = []; addSortNodes(this.getChildren(null)); } /** * 加载数据(初始化), 并给数据添加部分树结构必须数据 * @param datas */ loadDatas (datas) { // 清空旧数据 this.items = {}; this.nodes = []; // 加载全部数据 for (const data of datas) { const keyName = itemsPre + data[this.setting.id]; this.items[keyName] = JSON.parse(JSON.stringify(data)); this.datas.push(this.items[keyName]); } this.sortTreeNode(); for (const node of this.nodes) { node.expanded = node.children.length > 0; node.visible = true; } } /** * 根据id获取树结构节点数据 * @param {Number} id * @returns {Object} */ getItems (id) { return this.items[itemsPre + id]; }; /** * 查找node的parent * @param {Object} node * @returns {Object} */ getParent (node) { return this.getItems(node[this.setting.pid]); }; /** * 根据path查找完整节点 * @param {Number} path */ getFullPathNodes (path) { const self = this, ids = path.split('.'); if (ids.length > 0) { return this.nodes.filter((x) => { return ids.indexOf('' + x[self.setting.id]) >= 0; }); } else { return []; } }; /** * 查询node的已下载子节点 * @param {Object} node * @returns {Array} */ getChildren (node) { const setting = this.setting; const pid = node ? node[setting.id] : setting.rootId; const children = this.datas.filter(function (x) { return x[setting.pid] === pid; }); children.sort(function (a, b) { return a.order - b.order; }); return children; }; /** * 查询node的已下载的全部后代 * @param {Object} node * @returns {Array} */ getPosterity (node) { const reg = new RegExp('^' + node.full_path + '.'); return this.datas.filter(function (x) { return reg.test(x.full_path); }) }; /** * 查询node是否是父节点的最后一个子节点 * @param {Object} node * @returns {boolean} */ isLastSibling (node) { const siblings = this.getChildren(this.getParent(node)); return node.order === siblings[siblings.length - 1].order; }; /** * 刷新子节点是否可见 * @param {Object} node * @private */ _refreshChildrenVisible (node) { if (!node.children) { node.children = this.getChildren(node); } if (node.children && node.children.length > 0) { for (const child of node.children) { child.visible = node.expanded && node.visible; this._refreshChildrenVisible(child); } } }; /** * 设置节点是否展开, 并控制子节点可见 * @param {Object} node * @param {Boolean} expanded */ setExpanded (node, expanded) { node.expanded = expanded; this._refreshChildrenVisible(node); }; /** * 提取节点key和索引数据 * @param {Object} node - 节点 * @returns {key} */ getNodeKeyData (node) { const data = {}; for (const key of this.setting.keys) { data[key] = node[key]; } return data; }; /** * 得到树结构构成id * @param node * @returns {*} */ getNodeKey (node) { return node[this.setting.id]; }; } class MeasureTree extends BaseTree { addData (datas) { const loadedData = []; for (const data of datas) { let node = this.getItems(data[this.setting.id]); if (node) { for (const prop in node) { if (data[prop] !== undefined) { node[prop] = data[prop]; } } loadedData.push(node); } else { const keyName = itemsPre + data[this.setting.id]; const node = JSON.parse(JSON.stringify(data)); this.items[keyName] = node; this.datas.push(node); node.expanded = false; node.visible = true; loadedData.push(node); } } this.sortTreeNode(); for (const node of loadedData) { const children = node.children; if (!node.expanded && children.length > 0) { node.expanded = true; this._refreshChildrenVisible(node); } } return loadedData; } removeData (datas) { datas.sort(function (a, b) { return b.level - a.level; }); console.log(datas); const removeArrayData = function (array, data) { const index = array.indexOf(data); array.splice(index, 1); }; for (const data of datas) { const node = this.getItems(data[this.setting.id]); if (node && this.getChildren(node).length === 0) { delete this.items[itemsPre + node[this.setting.id]]; if (node[this.setting.pid] !== this.setting.rootId) { const parent = this.items[itemsPre + node[this.setting.pid]]; removeArrayData(parent.children, node); } removeArrayData(this.datas, node); removeArrayData(this.nodes, node); } } }; loadLeafData (data) { const datas = data instanceof Array ? data : [data]; for (const d of datas) { let node = this.getItems(d[this.setting.id]); if (node && node.is_leaf) { for (const prop in node) { if (data[prop] !== undefined) { node[prop] = d[prop]; } } } } }; } class ActiveTree extends BaseTree { /** * 加载数据(动态),只加载不同部分 * @param {Array} datas * @return {Array} 加载到树的数据 * @privateA */ _loadData (datas) { const loadedData = []; for (const data of datas) { let node = this.getItems(data[this.setting.id]); if (node) { for (const prop in node) { if (data[prop] !== undefined) { node[prop] = data[prop]; } } loadedData.push(node); } else { const keyName = itemsPre + data[this.setting.id]; const node = JSON.parse(JSON.stringify(data)); this.items[keyName] = node; this.datas.push(node); node.expanded = false; node.visible = true; loadedData.push(node); } } this.sortTreeNode(); for (const node of loadedData) { const children = node.children; if (!node.expanded && children.length > 0) { node.expanded = true; this._refreshChildrenVisible(node); } } return loadedData; }; /** * 以下方法需等待响应, 通过callback刷新界面 */ /** * 加载子节点 * @param {Object} node * @param {function} callback */ loadChildren (node, callback) { const self = this; const url = this.setting.preUrl ? this.setting.preUrl + '/get-children' : 'get-children'; console.log(url); postData(url, this.getNodeKeyData(node), function (data) { self._loadData(data); callback(); }); }; } class LedgerTree extends BaseTree { /** * 加载数据(动态),只加载不同部分 * @param {Array} datas * @return {Array} 加载到树的数据 * @privateA */ _updateData (datas) { const loadedData = []; for (const data of datas) { let node = this.getItems(data[this.setting.id]); if (node) { for (const prop in node) { if (data[prop] !== undefined) { node[prop] = data[prop]; } } loadedData.push(node); } } for (const node of loadedData) { const children = this.getChildren(node); node.expanded = children.length > 0 && children[0].visible; } this.sortTreeNode(true); return loadedData; }; /** * 加载数据(动态),只加载不同部分 * @param {Array} datas * @return {Array} 加载到树的数据 * @privateA */ _loadData (datas) { const loadedData = [], resortData = []; for (const data of datas) { let node = this.getItems(data[this.setting.id]); if (node) { const parent = this.getItems(node[this.setting.pid]); for (const prop in node) { if (data[prop] !== undefined && data[prop] !== node[prop]) { node[prop] = data[prop]; if (parent && resortData.indexOf(parent) === -1) { resortData.push(parent); } } } loadedData.push(node); } else { const keyName = itemsPre + data[this.setting.id]; const node = JSON.parse(JSON.stringify(data)); this.items[keyName] = node; this.datas.push(node); node.expanded = false; node.visible = true; loadedData.push(node); if (resortData.indexOf(node) === -1) { resortData.push(node); } const parent = this.getItems(node[this.setting.pid]); if (parent && resortData.indexOf(parent) === -1) { resortData.push(parent); } } } for (const node of resortData) { node.children = this.getChildren(node); } this.sortTreeNode(true); for (const node of loadedData) { if (!node.expanded) { this.setExpanded(node, true); } } return loadedData; }; /** * 清理数据(动态) * @param datas * @private */ _freeData (datas) { const removeArrayData = function (array, data) { const index = array.indexOf(data); array.splice(index, 1); }; for (const data of datas) { const node = this.getItems(data[this.setting.id]); if (node) { delete this.items[itemsPre + node[this.setting.id]]; if (node[this.setting.pid] !== this.setting.rootId) { const parent = this.getItems(node[this.setting.pid]); if (parent) { removeArrayData(parent.children, node); } } removeArrayData(this.datas, node); removeArrayData(this.nodes, node); } } }; /** * 加载需展开的数据 * @param {Array} datas * @returns {Array} * @private */ _loadExpandData (datas) { const loadedData = [], existData = [], expandData = [], resortData = []; for (const data of datas) { let node = this.getItems(data[this.setting.id]); if (node) { existData.push(node); } else { const keyName = itemsPre + data[this.setting.id]; const node = JSON.parse(JSON.stringify(data)); this.items[keyName] = node; this.datas.push(node); node.expanded = false; node.visible = true; loadedData.push(node); if (resortData.indexOf(node) === -1) { resortData.push(node); } const parent = this.getItems(node[this.setting.pid]); if (parent && resortData.indexOf(parent) === -1) { resortData.push(parent); } } } for (const node of resortData) { node.children = this.getChildren(node); } this.sortTreeNode(true); for (const node of loadedData) { if (!node.expanded) { this.setExpanded(node, true); } } for (const node of existData) { const parent = this.getItems(node[this.setting.pid]); if (expandData.indexOf(parent) === -1) { expandData.push(parent); if (!parent.expanded) { this.setExpanded(parent, true); } } if (!node.expanded) { this.setExpanded(node, true); } } return [loadedData, expandData]; }; /** * 以下方法需等待响应, 通过callback刷新界面 */ /** * 加载子节点 * @param {Object} node * @param {function} callback */ loadChildren (node, callback) { const self = this; const url = this.setting.preUrl ? this.setting.preUrl + '/get-children' : 'get-children'; console.log(url); postData(url, this.getNodeKeyData(node), function (data) { self._loadData(data); callback(); }); }; /** * 树结构基本操作 * @param {String} url - 请求地址 * @param {Object} node - 操作节点 * @param {String} type - 操作类型 * @param {function} callback - 界面刷新 */ baseOperation (url, node, type, callback) { const self = this; const data = { id: node[this.setting.id], postType: type }; postData(url, data, function (datas) { const result = {}; if (datas.update) { result.update = self._updateData(datas.update); } if (datas.create) { result.create = self._loadData(datas.create); } if (datas.delete) { result.delete = self._freeData(datas.delete); } callback(result); }); }; /** * 节点数据编辑 * @param {String} url - 请求地址 * @param {Array|Object} updateData - 需更新的数据 * @param {function} callback - 界面刷新 */ update (url, updateData, callback) { const self = this; postData(url, updateData, function (datas) { const result = self._updateData(datas); callback(result); }, function () { if (updateData instanceof Array) { const result = []; for (const data of updateData) { result.push(self.getItems(data[self.setting.id])); } callback(result) } else { callback([self.getItems(updateData[self.setting.id])]); } }); }; /** * 复制粘贴整块(目前仅可粘贴为后项) * @param {String} url - 请求地址 * @param {Object} node - 操作节点 * @param {Array} block - 被复制整块的节点列表 * @param {function} callback - 界面刷新 */ pasteBlock (url, node, block, callback) { const self = this; const data = { id: node[self.setting.id], block: block }; postData(url, data, function (datas) { const result = {}; if (datas.update) { result.update = self._updateData(datas.update); } if (datas.create) { result.create = self._loadData(datas.create); } if (datas.delete) { result.delete = self._freeData(datas.delete); } callback(result); }); }; /** * 提交数据 * @param {String} url - 请求地址 * @param {Object} node - 当前选中节点 * @param {Object} data - 提交的数据 * @param {function} callback - 界面刷新 */ postData (url, node, data, callback) { const self = this; if (node) { data.id = node[self.setting.id]; } postData(url, data, function (datas) { const result = {}; console.log('childrenCount: ' + datas.expand.length); let time = new Date(); if (datas.update) { result.update = self._updateData(datas.update); } if (datas.create) { result.create = self._loadData(datas.create); } if (datas.delete) { result.delete = self._freeData(datas.delete); } if (datas.expand) { const [create, update] = self._loadExpandData(datas.expand); result.create = result.create ? result.create.concat(create) : create; result.expand = update; } time = new Date() - time; console.log('analysisData: ' + time); callback(result); }); }; batchInsert (url, node, data, callback) { const self = this; data.id = node[self.setting.id]; postData(url, data, function (datas) { const result = {}; if (datas.update) { result.update = self._updateData(datas.update); } if (datas.create) { result.create = self._loadData(datas.create); } if (datas.delete) { result.delete = self._freeData(datas.delete); } callback(result); }); }; } if (type === 'base') { return new BaseTree(setting); } else if (type === 'active') { return new ActiveTree(setting); } else if (type === 'ledger') { return new LedgerTree(setting); } else if (type === 'measure') { return new MeasureTree(setting); } }; const treeCalc = { getMaxLevel: function (tree) { return Math.max.apply(Math, tree.datas.map(function(o) {return o.level})); }, calculateNode: function (tree, node, calcFields) { const children = tree.getChildren(node); if (children.length > 0) { const gather = children.reduce(function (rst, x) { const result = {}; const fieldCalc = function (field) { if (rst[field]) { result[field] = x[field] ? rst[field] + x[field] : rst[field]; } else { result[field] = x[field] ? x[field] : undefined; } } for (const cf of calcFields) { fieldCalc(cf); } return result; }); for (const cf of calcFields) { if (gather[cf]) { node[cf] = gather[cf]; } } } }, calculateLevelNode: function (tree, level, calcFields) { const nodes = tree.datas.filter((n) => { return n.level === level }); for (const node of nodes) { this.calculateNode(tree, node, calcFields); } }, calculateAll: function (tree, calcFields) { for (let i = this.getMaxLevel(tree); i >= 0; i--) { this.calculateLevelNode(tree, i, calcFields); } }, calculateParent: function (tree, node, calcFields) { const nodes = tree.getFullPathNodes(node.full_path); nodes.sort((a, b) => { return b.level - a.level; }); for (const n of nodes) { this.calculateNode(tree, n, calcFields); } return nodes; } }