'use strict'; /** * * * @author Mai * @date * @version */ const itemsPre = 'id_'; class baseTree { /** * 构造函数 */ constructor (ctx, setting) { this.ctx = ctx; // 无索引 this.datas = []; // 以key为索引 this.items = {}; // 以排序为索引 this.nodes = []; // 根节点 this.children = []; // 树设置 this.setting = setting; } /** * 根据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]); }; /** * 查询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; }; /** * 获取节点的 index * @param node * @returns {number} */ getNodeSerialNo(node) { return this.nodes.indexOf(node); } /** * 树结构根据显示排序 */ 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]); } else { nodes[i].children.sort(function (a, b) { return a.order - b.order; }) } addSortNodes(nodes[i].children); } }; this.nodes = []; if (!isResort) { this.children = this.getChildren(); } else { this.children.sort(function (a, b) { return a.order - b.order; }) } addSortNodes(this.children); } /** * 加载数据(初始化), 并给数据添加部分树结构必须数据 * @param datas */ loadDatas (datas) { // 清空旧数据 this.items = {}; this.nodes = []; this.datas = []; this.children = []; // 加载全部数据 datas.sort(function (a, b) { return a.level - b.level; }); for (const data of datas) { const keyName = itemsPre + data[this.setting.id]; if (!this.items[keyName]) { const item = JSON.parse(JSON.stringify(data)); item.children = []; item.expanded = true; item.visible = true; this.items[keyName] = item; this.datas.push(item); if (item[this.setting.pid] === this.setting.rootId) { this.children.push(item); } else { const parent = this.getParent(item); if (parent) { parent.children.push(item); } } } } this.children.sort(function (a, b) { return a.order - b.order; }); this.sortTreeNode(true); } /** * 递归方式 查询node的已下载的全部后代 (兼容full_path不存在的情况) * @param node * @returns {*} * @private */ _recursiveGetPosterity (node) { let posterity = node.children; for (const c of node.children) { posterity = posterity.concat(this._recursiveGetPosterity(c)); } return posterity; }; /** * 查询node的已下载的全部后代 * @param {Object} node * @returns {Array} */ getPosterity (node) { const self = this; let posterity; if (node.full_path !== '') { const reg = new RegExp('^' + node.full_path + '-'); posterity = this.datas.filter(function (x) { return reg.test(x.full_path); }); } else { posterity = this._recursiveGetPosterity(node); } posterity.sort(function (x, y) { return self.getNodeSerialNo(x) - self.getNodeSerialNo(y); }); return posterity; }; /** * 根据 字段名称 获取数据 * @param fields * @returns {Array} */ getDatas (fields) { const datas = []; for (const node of this.nodes) { if (node.b_code && node.b_code !== '') node.chapter = this.ctx.helper.getChapterCode(node.b_code); node.is_leaf = !node.children || node.children.length === 0; const data = {}; for (const field of fields) { data[field] = node[field]; } datas.push(data); } return datas; } /** * 排除 某些字段 获取数据 * @param fields * @returns {Array} */ getDatasWithout (fields, filter) { const datas = []; for (const node of this.nodes) { if (filter && filter(node)) { continue; } if (node.b_code && node.b_code !== '') node.chapter = this.ctx.helper.getChapterCode(node.b_code); node.is_leaf = !node.children || node.children.length === 0; const data = {}; for (const field in node) { if (fields.indexOf(field) === -1) { data[field] = node[field]; } } datas.push(data); } return datas; } /** * 获取默认数据 剔除一些树结构需要的缓存数据 * @returns {Array} */ getDefaultDatas(filter) { return this.getDatasWithout(['expanded', 'visible', 'children', 'index'], filter); } _mapTreeNode () { let map = {}, maxLevel = 0; for (const node of this.nodes) { let levelArr = map[node.level]; if (!levelArr) { levelArr = []; map[node.level] = levelArr; } if (node.level > maxLevel) { maxLevel = node.level; } levelArr.push(node); } return [maxLevel, map]; } _calculateNode (node, fun) { const self = this; if (node.children && node.children.length > 0) { const gather = node.children.reduce(function (rst, x) { const result = {}; for (const cf of self.setting.calcFields) { result[cf] = self.ctx.helper.add(rst[cf], x[cf]); } return result; }); // 汇总子项 for (const cf of this.setting.calcFields) { if (gather[cf]) { node[cf] = gather[cf]; } else { node[cf] = null; } } } // 自身运算 if (fun) { fun(node); } else if (this.setting.calc) { this.setting.calc(node, this.ctx.helper); } } calculateAll(fun) { const [maxLevel, levelMap] = this._mapTreeNode(); for (let i = maxLevel; i >= 0; i--) { const levelNodes = levelMap[i]; if (levelNodes && levelNodes.length > 0) { for (const node of levelNodes) { this._calculateNode(node, fun); } } } } } class billsTree extends baseTree { /** * 检查节点是否是最底层项目节 * @param node * @returns {boolean} */ isLeafXmj(node) { if (node.b_code && node.b_code !== '') { return false; } for (const child of node.children) { if (!child.b_code || child.b_code === '') { return false; } } return true; } /** * 查询最底层项目节(本身或父项) * @param {Object} node - 查询节点 * @returns {Object} */ getLeafXmjParent(node) { let parent = node; while (parent) { if (this.isLeafXmj(parent)) { return parent; } else { parent = this.getParent(parent); } } return null; } } class filterTree extends baseTree { addData(data, fields) { const item = {}; for (const prop in data) { if (fields.indexOf(prop) >= 0) { item[prop] = data[prop]; } } const keyName = itemsPre + item[this.setting.id]; if (!this.items[keyName]) { item.children = []; item.is_leaf = true; item.expanded = true; item.visible = true; this.items[keyName] = item; this.datas.push(item); if (item[this.setting.pid] === this.setting.rootId) { this.children.push(item); } else { const parent = this.getParent(item); if (parent) { parent.is_leaf = false; parent.children.push(item); } } } else { return this.items[keyName]; } return item; } } class filterGatherTree extends baseTree { clearDatas() { this.items = {}; this.nodes = []; this.datas = []; this.children = []; } get newId() { if (!this._maxId) { this._maxId = 0; } this._maxId++; return this._maxId; } addNode(data, parent) { data[this.setting.pid] = parent ? parent[this.setting.id] : this.setting.rootId; let item = this.ctx.helper._.find(this.items, data); if (item) return item; item = data; item.drawing_code = []; item.memo = []; item.postil = []; item[this.setting.id] = this.newId; const keyName = itemsPre + item[this.setting.id]; item.children = []; item.is_leaf = true; item.expanded = true; item.visible = true; this.items[keyName] = item; this.datas.push(item); if (parent) { item[this.setting.fullPath] = parent[this.setting.fullPath] + '-' + item[this.setting.id]; item[this.setting.level] = parent[this.setting.level] + 1; item[this.setting.order] = parent.children.length + 1; parent.is_leaf = false; parent.children.push(item); } else { item[this.setting.fullPath] = '' + item[this.setting.id]; item[this.setting.level] = 1; item[this.setting.order] = this.children.length + 1; this.children.push(item); } return item; } generateSortNodes() { const self = this; const addSortNode = function (node) { self.nodes.push(node); for (const c of node.children) { addSortNode(c); } }; this.nodes = []; for (const n of this.children) { addSortNode(n); } } sortTreeNodeCustom(fun) { const sortNodes = function (nodes) { nodes.sort(fun); for (const [i, node] of nodes.entries()) { node.order = i + 1; } for (const node of nodes) { if (node.children && node.children.length > 1) { sortNodes(node.children); } } }; this.nodes = []; this.children = this.getChildren(null); sortNodes(this.children); this.generateSortNodes(); } } class gatherTree extends baseTree { constructor(ctx, setting) { super(ctx, setting); this._newId = 1; } get newId() { return this._newId++; } loadGatherNode(node, parent, loadFun) { const siblings = parent ? parent.children : this.children; let cur = siblings.find(function (x) { return node.b_code ? x.b_code === node.b_code && x.name === node.name && x.unit === node.unit && x.unit_price === node.unit_price : x.code === node.code && x.name === node.name; }); if (!cur) { const id = this.newId; cur = { id: id, pid: parent ? parent.id : this.setting.rootId, full_path: parent ? parent.full_path + '-' + id : '' + id, level: parent ? parent.level + 1 : 1, order: siblings.length + 1, children: [], code: node.code, b_code: node.b_code, name: node.name, unit: node.unit, unit_price: node.unit_price, }; siblings.push(cur); this.datas.push(cur); } loadFun(cur, node); for (const c of node.children) { this.loadGatherNode(c, cur, loadFun); } } generateSortNodes() { const self = this; const addSortNode = function (node) { self.nodes.push(node); for (const c of node.children) { addSortNode(c); } }; this.nodes = []; for (const n of this.children) { addSortNode(n); } } loadGatherTree(sourceTree, loadFun) { for (const c of sourceTree.children) { this.loadGatherNode(c, null, loadFun); } // todo load Pos Data; } calculateSum() { if (this.setting.calcSum) { for (const d of this.datas) { this.setting.calcSum(d, this.count); } } } } class pos { /** * 构造函数 * @param {id|Number, masterId|Number} setting */ constructor (setting) { // 无索引 this.datas = []; // 以key为索引 this.items = {}; // 以分类id为索引的有序 this.ledgerPos = {}; // pos设置 this.setting = setting; } /** * 加载部位明细数据 * @param datas */ loadDatas(datas) { this.datas = datas; this.items = {}; this.ledgerPos = {}; for (const data of this.datas) { const key = itemsPre + data[this.setting.id]; this.items[key] = data; const masterKey = itemsPre + data[this.setting.ledgerId]; if (!this.ledgerPos[masterKey]) { this.ledgerPos[masterKey] = []; } this.ledgerPos[masterKey].push(data); } for (const prop in this.ledgerPos) { this.resortLedgerPos(this.ledgerPos[prop]); } } getLedgerPos(mid) { return this.ledgerPos[itemsPre + mid]; } resortLedgerPos(ledgerPos) { if (ledgerPos instanceof Array) { ledgerPos.sort(function (a, b) { return a.porder - b.porder; }) } } /** * 计算全部 */ calculateAll(fun) { const calcFun = fun ? fun : this.setting.calc; if (!calcFun) return; for (const pos of this.datas) { calcFun(pos); } } getDatas () { return this.datas; } } module.exports = { billsTree, pos, filterTree, filterGatherTree, gatherTree, };