| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 | '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);        }    }    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) {                        // console.log(d[setting.pid]);                        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 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;        } else {            cur.dgn_qty1 = isXmj ? node.quantity || 0 : 0;            cur.dgn_qty2 = isXmj ? node.quantity2 || 0 : 0;            cur.quantity = isXmj ? 0 : node.quantity || 0;            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,};
 |