|  | @@ -510,20 +510,23 @@ class CalcProgram {
 | 
	
		
			
				|  |  |          project.registerModule(ModuleNames.calc_program, me);
 | 
	
		
			
				|  |  |      };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    // 兼容Project框架方法
 | 
	
		
			
				|  |  |      getSourceType () {
 | 
	
		
			
				|  |  |          return ModuleNames.calc_program;
 | 
	
		
			
				|  |  | -    };    // 兼容Project框架方法
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    // 兼容Project框架方法
 | 
	
		
			
				|  |  |      loadData (datas) {
 | 
	
		
			
				|  |  |          this.datas = datas;
 | 
	
		
			
				|  |  |          this.compileAllTemps();
 | 
	
		
			
				|  |  | -    };   // 兼容Project框架方法
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    // 兼容Project框架方法
 | 
	
		
			
				|  |  |      doAfterUpdate (err, data) {
 | 
	
		
			
				|  |  |          if(!err){
 | 
	
		
			
				|  |  |              $.bootstrapLoading.end();
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -    };  // 兼容Project框架方法
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      // 经测试,全部编译一次耗时0.003~0.004秒。耗时基本忽略不计。
 | 
	
		
			
				|  |  |      compileAllTemps(){
 | 
	
	
		
			
				|  | @@ -697,8 +700,56 @@ class CalcProgram {
 | 
	
		
			
				|  |  |          };
 | 
	
		
			
				|  |  |      };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  // 仅内部调用。注意:外部不能直接使用,因为这里传入的树节点必须有一定的初始化。
 | 
	
		
			
				|  |  | -    innerCalc(treeNode){
 | 
	
		
			
				|  |  | +    // 存储、刷新零散的多个结点。
 | 
	
		
			
				|  |  | +    saveNodes(treeNodes){
 | 
	
		
			
				|  |  | +        if (treeNodes.length < 1) return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        let me = this;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        me.project.beginUpdate('');
 | 
	
		
			
				|  |  | +        for (let node of treeNodes){
 | 
	
		
			
				|  |  | +            if (node.changed){
 | 
	
		
			
				|  |  | +                let data = {
 | 
	
		
			
				|  |  | +                    ID: node.data.ID,
 | 
	
		
			
				|  |  | +                    projectID: me.project.ID(),
 | 
	
		
			
				|  |  | +                    /*  subType、quantity、calcBase、programID、marketUnitFee等等字段较为特殊,它们的改变一定会触发计算并导致计算
 | 
	
		
			
				|  |  | +                    结果的变化,从而引发保存动作。将这些字段放在该位置跟计算结果一起保存,可减少前端跟后端的通讯频率。              */
 | 
	
		
			
				|  |  | +                    subType: node.data.subType,
 | 
	
		
			
				|  |  | +                    quantity: node.data.quantity,
 | 
	
		
			
				|  |  | +                    calcBase: node.data.calcBase,
 | 
	
		
			
				|  |  | +                    calcBaseValue: node.data.calcBaseValue,
 | 
	
		
			
				|  |  | +                    programID: node.data.programID,
 | 
	
		
			
				|  |  | +                    marketUnitFee: node.data.marketUnitFee,
 | 
	
		
			
				|  |  | +                    marketTotalFee: node.data.marketTotalFee,
 | 
	
		
			
				|  |  | +                    fees: node.data.fees,
 | 
	
		
			
				|  |  | +                    isFromDetail:node.data.isFromDetail,
 | 
	
		
			
				|  |  | +                    feeRate: node.data.feeRate,
 | 
	
		
			
				|  |  | +                    feeRateID: node.data.feeRateID,
 | 
	
		
			
				|  |  | +                    contain:node.data.contain,
 | 
	
		
			
				|  |  | +                    quantityEXP:node.data.quantityEXP
 | 
	
		
			
				|  |  | +                };
 | 
	
		
			
				|  |  | +                if(node.sourceType==ModuleNames.ration && node.data.type==rationType.gljRation){//定额类型的工料机做特殊处理
 | 
	
		
			
				|  |  | +                    data.code=node.data.code;
 | 
	
		
			
				|  |  | +                    data.projectGLJID = node.data.projectGLJID;
 | 
	
		
			
				|  |  | +                    delete data.marketUnitFee;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                let newData = {'updateType': 'ut_update', 'updateData': data};
 | 
	
		
			
				|  |  | +                me.project.push(node.sourceType, [newData]);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +        me.project.endUpdate();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        for (let node of treeNodes){delete node.changed};
 | 
	
		
			
				|  |  | +        projectObj.mainController.refreshTreeNode(treeNodes);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (activeSubSheetIs(subSheetIndex.ssiCalcProgram)) {
 | 
	
		
			
				|  |  | +            calcProgramObj.showData(me.project.mainTree.selected, false);
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 只计算treeNode自身。changedArr: 外部传来的一个数组,专门存储发生变动的节点。
 | 
	
		
			
				|  |  | +    innerCalc(treeNode, changedArr){
 | 
	
		
			
				|  |  |          let me = this;
 | 
	
		
			
				|  |  |          // 仅用作树节点显示的工料机不能参与计算。
 | 
	
		
			
				|  |  |          if (treeNode.sourceType === me.project.ration_glj.getSourceType()) return;
 | 
	
	
		
			
				|  | @@ -923,126 +974,34 @@ class CalcProgram {
 | 
	
		
			
				|  |  |                  };
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          };
 | 
	
		
			
				|  |  | -    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /*// 计算公式结点(叶子清单的计算基数计算)。仅内部调用。注意:外部不能直接使用,因为这里传入的树节点必须有一定的初始化。
 | 
	
		
			
				|  |  | -    innerCalcFormula(treeNode){
 | 
	
		
			
				|  |  | -        treeNode.calcType = treeNodeTools.getCalcType(treeNode);
 | 
	
		
			
				|  |  | -        if (treeNode.calcType != treeNodeCalcType.ctCalcBaseValue) return;
 | 
	
		
			
				|  |  | +        if (treeNode.changed && !changedArr.includes(treeNode)) changedArr.push(treeNode);
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        let me = this;
 | 
	
		
			
				|  |  | -        delete treeNode.data.gljList;
 | 
	
		
			
				|  |  | -        if (treeNode.data.programID) treeNode.data.programID = null;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let f = treeNode.data.feeRate ? treeNode.data.feeRate : 100;
 | 
	
		
			
				|  |  | -        if (!treeNode.data.quantity) treeNode.data.quantity = 1;
 | 
	
		
			
				|  |  | -        let q = treeNode.data.quantity;
 | 
	
		
			
				|  |  | -        let b = treeNode.data.calcBaseValue ? treeNode.data.calcBaseValue : 0;
 | 
	
		
			
				|  |  | -        let uf = (b * f * q / 100).toDecimal(decimalObj.bills.unitPrice);
 | 
	
		
			
				|  |  | -        let tuf = uf;
 | 
	
		
			
				|  |  | -        let tf = (me.project.property.billsCalcMode === leafBillGetFeeType.rationPrice) ? (b * f / 100).toDecimal(decimalObj.bills.totalPrice) : (uf * q).toDecimal(decimalObj.bills.totalPrice);
 | 
	
		
			
				|  |  | -        let ttf = tf;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        delete treeNode.data.fees;    // 直接删掉再新增,不用一个个费判断更新,效率更高。
 | 
	
		
			
				|  |  | -        delete treeNode.data.feesIndex;
 | 
	
		
			
				|  |  | -        treeNodeTools.initFeeField(treeNode, 'common');
 | 
	
		
			
				|  |  | -        treeNode.data.feesIndex.common.unitFee = uf;
 | 
	
		
			
				|  |  | -        treeNode.data.feesIndex.common.totalFee = tf;
 | 
	
		
			
				|  |  | -        treeNode.data.feesIndex.common.tenderUnitFee = tuf;
 | 
	
		
			
				|  |  | -        treeNode.data.feesIndex.common.tenderTotalFee = ttf;
 | 
	
		
			
				|  |  | -        treeNode.changed = true;
 | 
	
		
			
				|  |  | -        treeNode.data.calcTemplate = {"calcItems": []};
 | 
	
		
			
				|  |  | -    };*/
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // 计算本节点(默认同时递归计算所有父节点,可选)
 | 
	
		
			
				|  |  | +    // 计算本节点、所有父节点(默认,可选)、公式引用节点。
 | 
	
		
			
				|  |  |      calculate(treeNode, calcParents = true){
 | 
	
		
			
				|  |  |          let me = this;
 | 
	
		
			
				|  |  |          let changedNodes = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        me.innerCalc(treeNode);
 | 
	
		
			
				|  |  | +        me.innerCalc(treeNode, changedNodes);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          if (treeNode.changed) {
 | 
	
		
			
				|  |  | -            if (!changedNodes.includes(treeNode)) changedNodes.push(treeNode);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |              // 计算父结点
 | 
	
		
			
				|  |  |              if (calcParents){
 | 
	
		
			
				|  |  |                  let curNode = treeNode.parent;
 | 
	
		
			
				|  |  |                  while (curNode){
 | 
	
		
			
				|  |  | -                    me.innerCalc(curNode);
 | 
	
		
			
				|  |  | -                    if (curNode.changed && !changedNodes.includes(curNode)) changedNodes.push(curNode);
 | 
	
		
			
				|  |  | +                    me.innerCalc(curNode, changedNodes);
 | 
	
		
			
				|  |  |                      curNode = curNode.parent;
 | 
	
		
			
				|  |  |                  };
 | 
	
		
			
				|  |  |              };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            // 父结点算完,再算所有的公式结点(必须先算完父结点,再算公式结点)
 | 
	
		
			
				|  |  | +            // 父结点算完,再计算所有的公式结点(必须先算完父结点,再算公式结点)
 | 
	
		
			
				|  |  |              me.calcFormulaNodes(changedNodes);
 | 
	
		
			
				|  |  |          };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          return changedNodes;
 | 
	
		
			
				|  |  |      };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // 存储、刷新本节点(默认存储刷新所有父节点,可选)
 | 
	
		
			
				|  |  | -    saveNode(treeNode, saveParents = true) {
 | 
	
		
			
				|  |  | -        if (!treeNode.changed) return;
 | 
	
		
			
				|  |  | -        let me = this;
 | 
	
		
			
				|  |  | -        let nodesArr = [];
 | 
	
		
			
				|  |  | -        let curNode = treeNode;
 | 
	
		
			
				|  |  | -        while (curNode) {
 | 
	
		
			
				|  |  | -            if (curNode.changed){nodesArr.push(curNode)};
 | 
	
		
			
				|  |  | -            if (saveParents) curNode = curNode.parent
 | 
	
		
			
				|  |  | -            else break;
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -        me.saveNodes(nodesArr);
 | 
	
		
			
				|  |  | -    };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    // 存储、刷新零散的多个树结点
 | 
	
		
			
				|  |  | -    saveNodes(treeNodes){
 | 
	
		
			
				|  |  | -        if (treeNodes.length < 1) return;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let me = this;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        me.project.beginUpdate('');
 | 
	
		
			
				|  |  | -        for (let node of treeNodes){
 | 
	
		
			
				|  |  | -            if (node.changed){
 | 
	
		
			
				|  |  | -                let data = {
 | 
	
		
			
				|  |  | -                    ID: node.data.ID,
 | 
	
		
			
				|  |  | -                    projectID: me.project.ID(),
 | 
	
		
			
				|  |  | -                    /*  subType、quantity、calcBase、programID、marketUnitFee等等字段较为特殊,它们的改变一定会触发计算并导致计算
 | 
	
		
			
				|  |  | -                    结果的变化,从而引发保存动作。将这些字段放在该位置跟计算结果一起保存,可减少前端跟后端的通讯频率。              */
 | 
	
		
			
				|  |  | -                    subType: node.data.subType,
 | 
	
		
			
				|  |  | -                    quantity: node.data.quantity,
 | 
	
		
			
				|  |  | -                    calcBase: node.data.calcBase,
 | 
	
		
			
				|  |  | -                    calcBaseValue: node.data.calcBaseValue,
 | 
	
		
			
				|  |  | -                    programID: node.data.programID,
 | 
	
		
			
				|  |  | -                    marketUnitFee: node.data.marketUnitFee,
 | 
	
		
			
				|  |  | -                    marketTotalFee: node.data.marketTotalFee,
 | 
	
		
			
				|  |  | -                    fees: node.data.fees,
 | 
	
		
			
				|  |  | -                    isFromDetail:node.data.isFromDetail,
 | 
	
		
			
				|  |  | -                    feeRate: node.data.feeRate,
 | 
	
		
			
				|  |  | -                    feeRateID: node.data.feeRateID,
 | 
	
		
			
				|  |  | -                    contain:node.data.contain,
 | 
	
		
			
				|  |  | -                    quantityEXP:node.data.quantityEXP
 | 
	
		
			
				|  |  | -                };
 | 
	
		
			
				|  |  | -                if(node.sourceType==ModuleNames.ration && node.data.type==rationType.gljRation){//定额类型的工料机做特殊处理
 | 
	
		
			
				|  |  | -                    data.code=node.data.code;
 | 
	
		
			
				|  |  | -                    data.projectGLJID = node.data.projectGLJID;
 | 
	
		
			
				|  |  | -                    delete data.marketUnitFee;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                let newData = {'updateType': 'ut_update', 'updateData': data};
 | 
	
		
			
				|  |  | -                me.project.push(node.sourceType, [newData]);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -        me.project.endUpdate();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        for (let node of treeNodes){delete node.changed};
 | 
	
		
			
				|  |  | -        projectObj.mainController.refreshTreeNode(treeNodes);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if (activeSubSheetIs(subSheetIndex.ssiCalcProgram)) {
 | 
	
		
			
				|  |  | -            calcProgramObj.showData(me.project.mainTree.selected, false);
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -    };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |      /* 计算所有树结点(分3种情况),并返回发生变动的零散的多个树结点。
 | 
	
		
			
				|  |  |      参数取值如下:
 | 
	
		
			
				|  |  |      calcAllType.catAll       计算所有树结点 (不指定参数时的默认值)
 | 
	
	
		
			
				|  | @@ -1071,7 +1030,7 @@ class CalcProgram {
 | 
	
		
			
				|  |  |      };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      // 计算全部公式项。 (参数意义:将通过本方法后发生改变的节点存入changedNodesArr中)
 | 
	
		
			
				|  |  | -    calcFormulaNodes(changedNodesArr){
 | 
	
		
			
				|  |  | +    calcFormulaNodes(changedArr){
 | 
	
		
			
				|  |  |          let me = this;
 | 
	
		
			
				|  |  |          let formulaNodes = treeNodeTools.getFormulaNodes();
 | 
	
		
			
				|  |  |          if (formulaNodes.length == 0) return;
 | 
	
	
		
			
				|  | @@ -1080,15 +1039,13 @@ class CalcProgram {
 | 
	
		
			
				|  |  |              projectObj.project.calcBase.calculate(formulaNode, true);
 | 
	
		
			
				|  |  |              if (projectObj.project.calcBase.success){
 | 
	
		
			
				|  |  |                  // 计算公式结点
 | 
	
		
			
				|  |  | -                me.innerCalc(formulaNode);
 | 
	
		
			
				|  |  | -                if (formulaNode.changed && !changedNodesArr.includes(formulaNode)) changedNodesArr.push(formulaNode);
 | 
	
		
			
				|  |  | +                me.innerCalc(formulaNode, changedArr);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  // 计算父结点
 | 
	
		
			
				|  |  |                  if (formulaNode.changed){
 | 
	
		
			
				|  |  |                      let curNode = formulaNode.parent;
 | 
	
		
			
				|  |  |                      while (curNode){
 | 
	
		
			
				|  |  | -                        me.innerCalc(curNode);
 | 
	
		
			
				|  |  | -                        if (curNode.changed && !changedNodesArr.includes(curNode)) changedNodesArr.push(curNode);
 | 
	
		
			
				|  |  | +                        me.innerCalc(curNode, changedArr);
 | 
	
		
			
				|  |  |                          curNode = curNode.parent;
 | 
	
		
			
				|  |  |                      };
 | 
	
		
			
				|  |  |                  };
 | 
	
	
		
			
				|  | @@ -1122,26 +1079,21 @@ class CalcProgram {
 | 
	
		
			
				|  |  |      calcRationsAndSave(rationNodes){
 | 
	
		
			
				|  |  |          let me = this, leafBills = [], changedNodes = [];
 | 
	
		
			
				|  |  |          for (let node of rationNodes) {
 | 
	
		
			
				|  |  | -            me.calculate(node, false);
 | 
	
		
			
				|  |  | -            if (node.changed) changedNodes.push(node);
 | 
	
		
			
				|  |  | +            me.innerCalc(node, changedNodes);
 | 
	
		
			
				|  |  |              let leafBill = node.parent;
 | 
	
		
			
				|  |  | -            if (leafBill && leafBills.indexOf(leafBill) < 0) leafBills.push(leafBill);      // 多条定额同属一条叶子清单时,避免叶子清单重复计算
 | 
	
		
			
				|  |  | +            // 多条定额同属一条叶子清单时,避免叶子清单重复计算
 | 
	
		
			
				|  |  | +            if (leafBill && leafBills.indexOf(leafBill) < 0) leafBills.push(leafBill);
 | 
	
		
			
				|  |  |          };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          for (let node of leafBills){
 | 
	
		
			
				|  |  |              me.calculate(node);
 | 
	
		
			
				|  |  | -            let cur = node;
 | 
	
		
			
				|  |  | -            while (cur) {
 | 
	
		
			
				|  |  | -                if (cur.changed && changedNodes.indexOf(cur) < 0) changedNodes.push(cur);
 | 
	
		
			
				|  |  | -                cur = cur.parent;
 | 
	
		
			
				|  |  | -            };
 | 
	
		
			
				|  |  |          };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          me.saveNodes(changedNodes);
 | 
	
		
			
				|  |  |      };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      // 计算并保存指定的一个树节点。修改一个树节点,实际上要计算和保存的是一批树结点:层层父结点、被其它结点(的公式)引用的公式结点。
 | 
	
		
			
				|  |  | -    // 这个方法实际上封装了calculate()和saveNodes()两个方法,方便外部调用,少写一点代码。
 | 
	
		
			
				|  |  | +    // 这个方法实际上封装了calculate()和saveNodes()两个方法,主要目的是为了外部调用方便,少写一点累赘代码。
 | 
	
		
			
				|  |  |      calcAndSave(treeNode){
 | 
	
		
			
				|  |  |          let changedNodes = this.calculate(treeNode);
 | 
	
		
			
				|  |  |          this.saveNodes(changedNodes);
 |