|  | @@ -45,6 +45,18 @@ const aeUtils = {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +const mainReg = /^(GD*)?G?(\d\d)*\d$/i;
 | 
	
		
			
				|  |  | +const subReg = /^(GD*)?G?[A-Z]{2}(\d\d)+$/i;
 | 
	
		
			
				|  |  | +const gdXmjPartReg = /^(GD*)?G?/;
 | 
	
		
			
				|  |  | +const specCode106 = {
 | 
	
		
			
				|  |  | +    code: ['102', '103', '104'],
 | 
	
		
			
				|  |  | +    reg: /^(GD*)?G?106/i
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +const specCode109 = {
 | 
	
		
			
				|  |  | +    code: ['102', '103', '104', '105', '106', '107', '108'],
 | 
	
		
			
				|  |  | +    reg: /^(GD*)?G?109/i
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  class ImportBaseTree {
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  |       * 构造函数
 | 
	
	
		
			
				|  | @@ -61,6 +73,8 @@ class ImportBaseTree {
 | 
	
		
			
				|  |  |          this.roots = [];
 | 
	
		
			
				|  |  |          this.pos = [];
 | 
	
		
			
				|  |  |          this.tempData = [];
 | 
	
		
			
				|  |  | +        // 以id为索引
 | 
	
		
			
				|  |  | +        this.nodes = {};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          // 缓存
 | 
	
		
			
				|  |  |          this.finalNode = null;
 | 
	
	
		
			
				|  | @@ -97,6 +111,7 @@ class ImportBaseTree {
 | 
	
		
			
				|  |  |                  this.keyNodeId = node.ledger_id + 1;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              this.tempData.push(node);
 | 
	
		
			
				|  |  | +            this.nodes[node.ledger_id] = node;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          for (const node of this.items) {
 | 
	
		
			
				|  |  |              node.tender_id = this.ctx.tender.id;
 | 
	
	
		
			
				|  | @@ -182,6 +197,7 @@ class ImportBaseTree {
 | 
	
		
			
				|  |  |       * @param {Object} node - 当前添加的节点
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  |      defineCacheData(node) {
 | 
	
		
			
				|  |  | +        this.nodes[node.ledger_id] = node;
 | 
	
		
			
				|  |  |          this.finalNode = node;
 | 
	
		
			
				|  |  |          this.finalPrecision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, node.unit);
 | 
	
		
			
				|  |  |          if (node.code) {
 | 
	
	
		
			
				|  | @@ -297,6 +313,128 @@ class ImportBaseTree {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +class ImportStd18Tree extends ImportBaseTree {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 检查是否是父项
 | 
	
		
			
				|  |  | +     * @param parent
 | 
	
		
			
				|  |  | +     * @param code
 | 
	
		
			
				|  |  | +     * @returns {boolean}
 | 
	
		
			
				|  |  | +     * @private
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    _checkParent(parent, code) {
 | 
	
		
			
				|  |  | +        if (!parent.code) return false;
 | 
	
		
			
				|  |  | +        const numberPart = parent.code.replace(gdXmjPartReg, '');
 | 
	
		
			
				|  |  | +        if (!numberPart) return false;
 | 
	
		
			
				|  |  | +        return code.indexOf(numberPart) === 0 ||
 | 
	
		
			
				|  |  | +            code.indexOf('G' + numberPart) === 0 ||
 | 
	
		
			
				|  |  | +            code.indexOf('GD' + numberPart) === 0;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 查找主表项目节父项
 | 
	
		
			
				|  |  | +     * @param code
 | 
	
		
			
				|  |  | +     * @returns {*}
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    findMainXmjParent(code) {
 | 
	
		
			
				|  |  | +        const numberPart = code.replace(gdXmjPartReg, '');
 | 
	
		
			
				|  |  | +        if (numberPart.length <= 1) throw '首层项目节模板中未定义,不支持导入';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        let parent = this.cacheMainXmjNode;
 | 
	
		
			
				|  |  | +        while (parent) {
 | 
	
		
			
				|  |  | +            if (this._checkParent(parent, code)) return parent;
 | 
	
		
			
				|  |  | +            parent = this.nodes[parent.ledger_pid];
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return null;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 查找分表项目节父项
 | 
	
		
			
				|  |  | +     * @param code
 | 
	
		
			
				|  |  | +     * @returns {*}
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    findSubXmjParent(code) {
 | 
	
		
			
				|  |  | +        let parent = this.cacheSubXmjNode;
 | 
	
		
			
				|  |  | +        while (parent && parent.code.match(subReg)) {
 | 
	
		
			
				|  |  | +            if (this._checkParent(parent, code)) return parent;
 | 
	
		
			
				|  |  | +            parent = this.nodes[parent.ledger_pid];
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return this.cacheMainXmjNode;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 根据 编号 查找 父项项目节
 | 
	
		
			
				|  |  | +     * @param {String} code - 子项编号
 | 
	
		
			
				|  |  | +     * @returns {*}
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    findXmjParent(code) {
 | 
	
		
			
				|  |  | +        if (code.match(mainReg)) {
 | 
	
		
			
				|  |  | +            if (!this.cacheMainXmjNode) throw '主表项目节找不到父项';
 | 
	
		
			
				|  |  | +            return this.findMainXmjParent(code);
 | 
	
		
			
				|  |  | +        } else if (code.match(subReg)) {
 | 
	
		
			
				|  |  | +            if (!this.cacheMainXmjNode) throw '分表项目节找不到所属主表项目节';
 | 
	
		
			
				|  |  | +            return this.findSubXmjParent(code);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 定义缓存节点(添加工程量清单、部位明细需使用缓存定位)
 | 
	
		
			
				|  |  | +     * @param {Object} node - 当前添加的节点
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    defineCacheData(node) {
 | 
	
		
			
				|  |  | +        super.defineCacheData(node);
 | 
	
		
			
				|  |  | +        if (node.code) {
 | 
	
		
			
				|  |  | +            if (node.code.match(mainReg)) {
 | 
	
		
			
				|  |  | +                this.cacheMainXmjNode = node;
 | 
	
		
			
				|  |  | +                this.cacheSubXmjNode = null;
 | 
	
		
			
				|  |  | +            } else if (node.code.match(subReg)) {
 | 
	
		
			
				|  |  | +                this.cacheSubXmjNode = node;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            if (node.code.match(specCode106.reg)) {
 | 
	
		
			
				|  |  | +                if (this.cacheSpecMainXmj1 && this.cacheSpecMainXmj1.code.match(specCode109.reg)) {
 | 
	
		
			
				|  |  | +                    this.cacheSpecMainXmj2 = node;
 | 
	
		
			
				|  |  | +                } else {
 | 
	
		
			
				|  |  | +                    this.cacheSpecMainXmj1 = node;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            } else if (node.code.match(specCode109.reg)) {
 | 
	
		
			
				|  |  | +                this.cacheSpecMainXmj1 = node;
 | 
	
		
			
				|  |  | +                this.cacheSpecMainXmj2 = null;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 添加 项目节
 | 
	
		
			
				|  |  | +     * @param {Object} node - 项目节
 | 
	
		
			
				|  |  | +     * @returns {*}
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    addXmjNode(node) {
 | 
	
		
			
				|  |  | +        if (!node.code || (!node.code.match(mainReg) && !node.code.match(subReg))) return null;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        node.id = this.ctx.app.uuid.v4();
 | 
	
		
			
				|  |  | +        node.tender_id = this.ctx.tender.id;
 | 
	
		
			
				|  |  | +        node.children = [];
 | 
	
		
			
				|  |  | +        if ((specCode106.code.indexOf(node.code) >= 0)) {
 | 
	
		
			
				|  |  | +            if (this.cacheSpecMainXmj2 && this.cacheSpecMainXmj2.code.match(specCode106.reg))
 | 
	
		
			
				|  |  | +                return this.addNodeWithParent(node, this.cacheSpecMainXmj2);
 | 
	
		
			
				|  |  | +            if (this.cacheSpecMainXmj1 && this.cacheSpecMainXmj1.code.match(specCode106.reg))
 | 
	
		
			
				|  |  | +                return this.addNodeWithParent(node, this.cacheSpecMainXmj1);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if ((specCode109.code.indexOf(node.code) >= 0) &&
 | 
	
		
			
				|  |  | +            (this.cacheSpecMainXmj1 && this.cacheSpecMainXmj1.code.match(specCode109.reg))) {
 | 
	
		
			
				|  |  | +            return this.addNodeWithParent(node, this.cacheSpecMainXmj1)
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        const temp = this.findTempData(node);
 | 
	
		
			
				|  |  | +        if (temp) {
 | 
	
		
			
				|  |  | +            this.defineCacheData(temp);
 | 
	
		
			
				|  |  | +            return temp;
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +            const parent = this.findXmjParent(node.code);
 | 
	
		
			
				|  |  | +            return this.addNodeWithParent(node, parent);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  class AnalysisExcelTree {
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  |       * 构造函数
 | 
	
	
		
			
				|  | @@ -319,6 +457,29 @@ class AnalysisExcelTree {
 | 
	
		
			
				|  |  |          };
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    _isMatch11(tempData) {
 | 
	
		
			
				|  |  | +        return _.find(tempData, x => {
 | 
	
		
			
				|  |  | +            return x.code.indexOf('-') > 0;
 | 
	
		
			
				|  |  | +        })
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    _isMatch18(tempData) {
 | 
	
		
			
				|  |  | +        return _.every(tempData, x => {
 | 
	
		
			
				|  |  | +            return !x.code || !!x.code.match(mainReg);
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    _getNewCacheTree(tempData) {
 | 
	
		
			
				|  |  | +        // 模板符合11编办规则,使用11编办树
 | 
	
		
			
				|  |  | +        if (this._isMatch18(tempData)) {
 | 
	
		
			
				|  |  | +            return new ImportStd18Tree(tempData, this.ctx);
 | 
	
		
			
				|  |  | +        // 反之使用11编办(未校验模板是否符合,替换注释部分即可实现)
 | 
	
		
			
				|  |  | +        // } else if (this._isMatch11(tempData)){
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +            return new ImportBaseTree(tempData, this.ctx);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  |       * 读取项目节节点
 | 
	
		
			
				|  |  |       * @param {Array} row - excel行数据
 | 
	
	
		
			
				|  | @@ -326,23 +487,38 @@ class AnalysisExcelTree {
 | 
	
		
			
				|  |  |       * @private
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  |      _loadXmjNode(row) {
 | 
	
		
			
				|  |  | -        const node = {};
 | 
	
		
			
				|  |  | -        node.code = this.ctx.helper.replaceReturn(row[this.colsDef.code]);
 | 
	
		
			
				|  |  | -        node.name = this.ctx.helper.replaceReturn(row[this.colsDef.name]);
 | 
	
		
			
				|  |  | -        node.unit = this.ctx.helper.replaceReturn(row[this.colsDef.unit]);
 | 
	
		
			
				|  |  | -        const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, node.unit);
 | 
	
		
			
				|  |  | -        node.quantity = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.quantity]), precision.value);
 | 
	
		
			
				|  |  | -        node.dgn_qty1 = aeUtils.toNumber(row[this.colsDef.dgn_qty1]);
 | 
	
		
			
				|  |  | -        node.dgn_qty2 = aeUtils.toNumber(row[this.colsDef.dgn_qty2]);
 | 
	
		
			
				|  |  | -        node.unit_price = aeUtils.toNumber(row[this.colsDef.unit_price]);
 | 
	
		
			
				|  |  | -        node.drawing_code = this.ctx.helper.replaceReturn(row[this.colsDef.drawing_code]);
 | 
	
		
			
				|  |  | -        node.memo = this.ctx.helper.replaceReturn(row[this.colsDef.memo]);
 | 
	
		
			
				|  |  | -        if (node.quantity && node.unit_price) {
 | 
	
		
			
				|  |  | -            node.total_price = this.ctx.helper.mul(node.quantity, node.unit_price, this.ctx.tender.info.decimal.tp);
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -            node.total_price = null;
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +            const node = {};
 | 
	
		
			
				|  |  | +            node.code = this.ctx.helper.replaceReturn(row[this.colsDef.code]);
 | 
	
		
			
				|  |  | +            node.name = this.ctx.helper.replaceReturn(row[this.colsDef.name]);
 | 
	
		
			
				|  |  | +            node.unit = this.ctx.helper.replaceReturn(row[this.colsDef.unit]);
 | 
	
		
			
				|  |  | +            const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, node.unit);
 | 
	
		
			
				|  |  | +            node.quantity = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.quantity]), precision.value);
 | 
	
		
			
				|  |  | +            node.dgn_qty1 = aeUtils.toNumber(row[this.colsDef.dgn_qty1]);
 | 
	
		
			
				|  |  | +            node.dgn_qty2 = aeUtils.toNumber(row[this.colsDef.dgn_qty2]);
 | 
	
		
			
				|  |  | +            node.unit_price = aeUtils.toNumber(row[this.colsDef.unit_price]);
 | 
	
		
			
				|  |  | +            node.drawing_code = this.ctx.helper.replaceReturn(row[this.colsDef.drawing_code]);
 | 
	
		
			
				|  |  | +            node.memo = this.ctx.helper.replaceReturn(row[this.colsDef.memo]);
 | 
	
		
			
				|  |  | +            if (node.quantity && node.unit_price) {
 | 
	
		
			
				|  |  | +                node.total_price = this.ctx.helper.mul(node.quantity, node.unit_price, this.ctx.tender.info.decimal.tp);
 | 
	
		
			
				|  |  | +            } else {
 | 
	
		
			
				|  |  | +                node.total_price = null;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            return this.cacheTree.addXmjNode(node);
 | 
	
		
			
				|  |  | +        } catch (error) {
 | 
	
		
			
				|  |  | +            console.log(error);
 | 
	
		
			
				|  |  | +            if (error.stack) {
 | 
	
		
			
				|  |  | +                this.ctx.logger.error(error);
 | 
	
		
			
				|  |  | +            } else {
 | 
	
		
			
				|  |  | +                this.ctx.getLogger('fail').info(JSON.stringify({
 | 
	
		
			
				|  |  | +                    error,
 | 
	
		
			
				|  |  | +                    project: this.ctx.session.sessionProject,
 | 
	
		
			
				|  |  | +                    user: this.ctx.session.sessionUser,
 | 
	
		
			
				|  |  | +                    body: row,
 | 
	
		
			
				|  |  | +                }));
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            return null;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        return this.cacheTree.addXmjNode(node);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  |       * 读取工程量清单数据
 | 
	
	
		
			
				|  | @@ -431,7 +607,7 @@ class AnalysisExcelTree {
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  |      analysisData(sheet, tempData) {
 | 
	
		
			
				|  |  |          this.colsDef = null;
 | 
	
		
			
				|  |  | -        this.cacheTree = new ImportBaseTree(tempData, this.ctx);
 | 
	
		
			
				|  |  | +        this.cacheTree = this._getNewCacheTree(tempData);
 | 
	
		
			
				|  |  |          this.errorData = [];
 | 
	
		
			
				|  |  |          this.loadEnd = false;
 | 
	
		
			
				|  |  |  
 |