'use strict'; const YbpNodeKind = { dxfy: 1, // 大项费用 fb: 2, // 分部 fx: 3, // 分项 bill: 4, // 清单 bx: 5, // 补项 cs: 6, // 分类 dt: 7, // 费用明细 xmj: 8, // 项目节 }; const defaultMerge = [ YbpNodeKind.dxfy, YbpNodeKind.fb, YbpNodeKind.cs, YbpNodeKind.dt, YbpNodeKind.xmj ]; const YbpImportType = { flow: 0, merge: 1 }; class YbpTree { /** * * @param setting - {Object}配置 * setting中必须设置id,pid,order,rootId(id, 父id, 同层排序, 根节点id) * e.g.{id: 'ID', pid: 'parentID', order: 'seq'} * 目前仅用于载入建筑ybp文件中的清单部分,生成树结构以便汇总导入 */ constructor(setting) { this.setting = JSON.parse(JSON.stringify(setting)); this.clearDatas(); } clearDatas() { // 数据集合 this.datas = []; // id索引 this.items = {}; // 排序索引 this.nodes = []; // 首层节点 this.children = []; } sortChildren(children, recursive) { const setting = this.setting; children.sort((x, y) => { return x[setting.order] - y[setting.order]; }); if (!recursive) return; for (const c of children) { this.sortChildren(c.children, recursive); } } sort() { this.sortChildren(this.children, true); const self = this; const _loadNode = function(node) { self.nodes.push(node); for (const c of node.children) { _loadNode(c); } }; for (const child of this.children) { _loadNode(child); } } loadDatas(datas) { this.clearDatas(); const setting = this.setting; const self = this; const _loadData = function(d) { if (d[setting.pid] === setting.rootId) { self.children.push(d); } else { let parent = self.items[d[setting.pid]]; if (!parent) { parent = datas.find(x => { return x[setting.id] === d[setting.pid]; }); if (!parent) { return null; } parent = _loadData(parent); } if (!parent) return null; parent.children.push(d); } d.children = []; self.datas.push(d); self.items[d[setting.id]] = d; return d; }; for (const d of datas) { if (this.items[d[setting.id]]) continue; _loadData(d); } this.sort(); } } class YbpImportTree { /** * * @param {Object} setting - 树结构配置 * @param {YbpImportType} type - 导入类型 * @param {Object} helper - this.ctx.helper * * setting中必须设置id, pid, level, order, full_path, rootId(id, 父id, 层次, 同层排序, 完整路径, 根节点id) * */ constructor(setting, type, helper, decimal) { this.setting = setting; this.importType = type; this.helper = helper; this.decimal = decimal; this.clearDatas(); } set newId(num) { this._newId = num; } get newId() { this._newId++; return this._newId; } loadTemplate(template) { if (!template || template.length === 0) return; this.clearDatas(); const setting = this.setting; const self = this; const _loadData = function(d) { d.children = []; self.datas.push(d); self.items[d[setting.id]] = d; if (d[setting.pid] === setting.rootId) { self.children.push(d); } else { const parent = self.items[d[setting.pid]]; if (!parent) { const parent = self.datas.find(x => { return x[setting.id] === d[setting.pid]; }); if (!parent) throw '找不到父项'; _loadData(parent); } parent.children.push(d); } }; for (const t of template) { if (this.items[t[setting.id]]) continue; _loadData(t); } } clearDatas() { // 数据集合 this.datas = []; // id索引 this.items = {}; // 排序索引 this.nodes = []; // 首层节点 this.children = []; // 新增节点id this.newId = 100; } _findNode(data, parent) { const children = parent ? parent.children : this.children; return this.helper._.find(children, data); // return children.find(x => { // return x.kind === data.kind && // x.code === data.code && x.b_code === data.b_code && x.name === data.name && x.unit === data.unit && // x.unit_price === data.unit_price; // }); } _importNode(node, parent, loadRelaFun) { const setting = this.setting; const compareData = { kind: node.kind }; const hasUp = (!node.children || node.children.length === 0) && defaultMerge.indexOf(compareData.kind) < 0; const isXmj = defaultMerge.indexOf(compareData.kind) >= 0; compareData.code = isXmj ? node.code || '' : ''; compareData.b_code = isXmj ? '' : node.code || ''; compareData.name = node.name || ''; compareData.unit = node.unit || ''; compareData.unit_price = hasUp ? (node.fees && node.fees.marketCommon ? node.fees.marketCommon.unitPrice : 0) : 0; let cur = (this.importType === YbpImportType.merge || defaultMerge.indexOf(compareData.kind) >= 0) ? this._findNode(compareData, parent) : null; if (!cur) { cur = compareData; cur.children = []; cur.source = []; cur[setting.id] = this.newId; cur[setting.pid] = parent ? parent[setting.id] : setting.rootId; cur[setting.level] = parent ? parent[setting.level] + 1 : 1; cur[setting.full_path] = parent ? `${parent[setting.full_path]}-${cur[setting.id]}` : `${cur[setting.id]}`; if (parent) { parent.children.push(cur); } else { this.children.push(cur); } this.datas.push(cur); cur.dgn_qty1 = isXmj ? node.quantity || 0 : 0; cur.dgn_qty2 = isXmj ? node.quantity2 || 0 : 0; cur.quantity = isXmj ? 0 : node.quantity || 0; cur.total_fee = node.fees ? node.fees.marketCommon.totalFee : 0; cur.labour_fee = node.fees && node.fees.marketLabour ? node.fees.marketLabour.totalFee : 0; cur.remark = node.remark; } else { if (isXmj) { cur.dgn_qty1 = this.helper.add(cur.dgn_qty1, node.quantity || 0); cur.dgn_qty2 = this.helper.add(cur.dgn_qty2, node.quantity2 || 0); } else { cur.quantity = this.helper.add(cur.quantity, node.quantity || 0); } cur.total_fee = this.helper.add(cur.total_fee, node.fees ? node.fees.marketCommon.totalFee : 0); cur.labour_fee = this.helper.add(cur.labour_fee, node.fees && node.fees.marketLabour ? node.fees.marketLabour.totalFee : 0); } if (loadRelaFun) loadRelaFun(cur, node); cur.source.push(this.unitName); for (const c of node.children) { this._importNode(c, cur, loadRelaFun); } } importTree(tree, unitName, loadRelaFun) { this.unitName = unitName; for (const n of tree.children) { this._importNode(n, null, loadRelaFun); } } sortChildren(children, recursive, resort) { resort && children.sort((x, y) => { return x.kind === YbpNodeKind.bill ? (x.b_code ? (y.b_code ? x.b_code.localeCompare(y.b_code) : 1) : -1) : (x.code ? (y.code ? x.code.localeCompare(y.code) : -1) : 1); }); children.forEach((c, i) => { c.order = i + 1; }); if (!recursive) return; for (const c of children) { this.sortChildren(c.children, recursive); } } generateSortNodes() { const self = this; const _loadNode = function(node) { self.nodes.push(node); for (const c of node.children) { _loadNode(c); } }; for (const child of this.children) { _loadNode(child); } } sort(sortChildren) { this.sortChildren(this.children, true, sortChildren); this.generateSortNodes(); } _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.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, this.helper, this.decimal); } 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 || this.setting.calc); } } } } } module.exports = { YbpImportType, YbpTree, YbpImportTree, };