/** * 构建pathTree * 可动态加载子节点,要求子节点获取接口按/xxx/get-children定义 * @param {Object} setting - 设置 * @returns {PathTree} */ 'use strict'; class PosData { /** * 构造函数 * @param {id|Number, masterId|Number} setting */ constructor (setting) { // 无索引 this.datas = null; // 以key为索引 this.items = {}; // 以分类id为索引的有序 this.masterRange = {}; // pos设置 this.setting = JSON.parse(JSON.stringify(setting)); } /** * 加载部位明细数据 * @param datas */ loadDatas(datas) { this.datas = datas; for (const data of this.datas) { const key = itemsPre + data[this.setting.id]; this.items[key] = data; const masterKey = itemsPre + data[this.setting.masterId]; if (!this.masterRange[masterKey]) { this.masterRange[masterKey] = []; } this.masterRange[masterKey].push(data); } } /** * 更新数据 * @param datas */ updateDatas(data) { const datas = data instanceof Array ? data : [data]; for (const d of datas) { const key = itemsPre + d[this.setting.id]; if (!this.items[key]) { this.datas.push(d); this.items[key] = d; const masterKey = itemsPre + d[this.setting.masterId]; if (!this.masterRange[masterKey]) { this.masterRange[masterKey] = []; } this.masterRange[masterKey].push(d); } else { const pos = this.items[key]; for (const prop in d) { pos[prop] = d[prop]; } } } } /** * 移除数据 * @param datas */ removeDatas(data) { const datas = data instanceof Array ? data : [data]; for (let i = datas.length - 1; i >= 0; i--) { const d = datas[i]; this.datas.splice(this.datas.indexOf(d), 1); const key = itemsPre + d[this.setting.id]; delete this.items[key]; const masterKey = itemsPre + d[this.setting.masterId]; const range = this.masterRange[masterKey]; range.splice(range.indexOf(d), 1); if (range.length === 0) { delete this.masterRange[masterKey]; } } } /** * 移除数据 - 根据分类id * @param mid */ removeDatasByMasterId (mid) { const masterKey = itemsPre + mid; const range = this.masterRange(mid); delete this.masterRange[masterKey]; for (const r of range) { this.datas.splice(this.datas.indexOf(r), 1); const key = itemsPre + r[this.setting.id]; delete this.items[key]; } } getPos(id) { return this.items[itemsPre + id]; } getMasterRange(mid) { return this.masterRange[itemsPre + mid]; } } const itemsPre = 'id_'; const createNewPathTree = function (type, setting) { class BaseTree { /** * 构造函数 */ constructor (setting) { // 无索引 this.datas = []; // 以key为索引 this.items = {}; // 以排序为索引 this.nodes = []; // 索引 this.children = {}; // 树设置 this.setting = 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) { datas = datas instanceof Array ? datas : [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) { datas = datas instanceof Array ? datas : [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) { datas = datas instanceof Array ? datas : [datas]; const freeDatas = []; 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) { freeDatas.push(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); } } return freeDatas; }; /** * 加载需展开的数据 * @param {Array} datas * @returns {Array} * @private */ _loadExpandData (datas) { datas = datas instanceof Array ? datas : [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]; }; /** * * @param parent * @param node * @private */ _getNodesParents(parents, nodes) { for (const node of nodes) { const parent = this.getParent(node); if (parent) { const paths = this.getFullPathNodes(parent.full_path); for (const p of paths) { if (parents.indexOf(p) === -1) { parents.push(p); } } } if (node.children.length > 0) { parents.push(node); } } } isLeafXmj(node) { for (const child of node.children) { if (child.code !== '') { return false; } } return true; } /** * 因为提交其他数据,引起的树结构数据更新,调用该方法 * * @param data - 更新的数据 {update, create, delete} * @param {function} callback - 界面刷新 */ loadPostData(data, callback) { const result = {}, parents = []; if (data.update) { result.update = this._updateData(data.update); this._getNodesParents(parents, result.update); } if (data.create) { result.create = this._loadData(data.create); this._getNodesParents(parents, result.create); } if (data.delete) { result.delete = this._freeData(data.delete); this._getNodesParents(parents, result.delete); } parents.sort((a, b) => { return b.level - a.level; }); for (const parent of parents) { treeCalc.calculateNode(this, parent, this.setting.calcFields, this.setting.calcFun); } result.update = result.update ? result.update.concat(parents) : parents; callback(result); } /** * 以下方法需等待响应, 通过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) { self.loadPostData(datas, callback); }); }; /** * 节点数据编辑 * @param {String} url - 请求地址 * @param {Array|Object} updateData - 需更新的数据 * @param {function} callback - 界面刷新 */ update (url, updateData, callback) { const self = this; postData(url, updateData, function (datas) { self.loadPostData(datas, callback); }, 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) { self.loadPostData(datas, callback); }); }; /** * 提交数据 * @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 = {}; 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; } 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, calcFun) { 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]; } } } if (calcFun) { calcFun(node); } }, calculateLevelNode: function (tree, level, calcFields, calcFun) { const nodes = tree.datas.filter((n) => { return n.level === level }); for (const node of nodes) { this.calculateNode(tree, node, calcFields, calcFun); } }, calculateAll: function (tree, calcFields, calcFun) { for (let i = this.getMaxLevel(tree); i >= 0; i--) { this.calculateLevelNode(tree, i, calcFields, calcFun); } }, calculateParent: function (tree, node, calcFields, calcFun) { 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, calcFun); } return nodes; } };