Bläddra i källkod

Merge branch '分摊功能' into 养护调价

zhangweicheng 5 år sedan
förälder
incheckning
43aa6af10d
53 ändrade filer med 2272 tillägg och 806 borttagningar
  1. 1 1
      modules/all_models/divide_setting.js
  2. 1 0
      modules/all_models/ration.js
  3. 2 1
      modules/all_models/user.js
  4. 4 0
      modules/main/controllers/divide_controller.js
  5. 0 9
      modules/main/controllers/project_controller.js
  6. 104 7
      modules/main/facade/divide_facade.js
  7. 64 140
      modules/main/facade/project_facade.js
  8. 13 3
      modules/main/facade/ration_facade.js
  9. 99 0
      modules/main/middleware/index.js
  10. 0 52
      modules/main/middleware/system_setting.js
  11. 1 0
      modules/main/routes/divide_route.js
  12. 1 1
      modules/main/routes/ration_route.js
  13. 50 18
      modules/pm/facade/pm_facade.js
  14. 1 1
      modules/pm/routes/pm_route.js
  15. 83 32
      modules/ration_glj/facade/glj_calculate_facade.js
  16. 20 14
      modules/ration_glj/facade/ration_glj_facade.js
  17. 4 1
      modules/users/controllers/login_controller.js
  18. 20 3
      modules/users/models/user_model.js
  19. 17 0
      public/common_util.js
  20. 5 5
      public/web/commonAlert.js
  21. 3 1
      public/web/common_ajax.js
  22. 81 2
      public/web/id_tree.js
  23. 91 0
      public/web/sheet/sheet_common.js
  24. 9 0
      public/web/socket/connection.js
  25. 15 10
      public/web/syntax-detection.js
  26. 9 37
      server.js
  27. 5 0
      socket.js
  28. 16 1
      web/building_saas/css/custom.css
  29. 9 27
      web/building_saas/main/html/divide.html
  30. 1 1
      web/building_saas/main/html/main.html
  31. 78 0
      web/building_saas/main/js/controllers/project_controller.js
  32. 31 0
      web/building_saas/main/js/models/cache_tree.js
  33. 45 32
      web/building_saas/main/js/models/calc_base.js
  34. 101 23
      web/building_saas/main/js/models/project.js
  35. 1 0
      web/building_saas/main/js/models/project_glj.js
  36. 34 52
      web/building_saas/main/js/models/ration.js
  37. 10 1
      web/building_saas/main/js/models/ration_coe.js
  38. 12 3
      web/building_saas/main/js/views/billsElf.js
  39. 513 89
      web/building_saas/main/js/views/divide_view.js
  40. 3 3
      web/building_saas/main/js/views/glj_col.js
  41. 171 134
      web/building_saas/main/js/views/project_view.js
  42. 52 2
      web/building_saas/main/js/views/std_billsGuidance_lib.js
  43. 40 19
      web/building_saas/main/js/views/zmhs_view.js
  44. 6 0
      web/building_saas/pm/js/pm_gc.js
  45. 63 14
      web/building_saas/pm/js/pm_newMain.js
  46. 1 1
      web/building_saas/pm/js/pm_share.js
  47. 33 0
      web/building_saas/pm/js/pm_tree.js
  48. 1 1
      web/building_saas/unit_price_file/index.html
  49. 3 0
      web/common/html/header.html
  50. 320 0
      web/over_write/hunan_2020.js
  51. 12 29
      web/over_write/js/anhui_2019.js
  52. 8 21
      web/over_write/js/neimeng_2019.js
  53. 5 15
      web/over_write/js/zhejiang_2005.js

+ 1 - 1
modules/all_models/divide_setting.js

@@ -7,7 +7,7 @@ let divideSchema = {
   divideList:[Schema.Types.Mixed],
   ration_gljs:[Schema.Types.Mixed],
   ration_coes:[Schema.Types.Mixed],
-  bills:[Schema.Types.Mixed]
+  divideType:Number//分摊方式(3种)
 }
 
 mongoose.model("divide_setting", new Schema(divideSchema, {versionKey: false, collection: "divide_setting"}));

+ 1 - 0
modules/all_models/ration.js

@@ -73,6 +73,7 @@ let rationSchema = new Schema({
     ruleText: String,                            // 计算规则
     prefix: {type: String, default: ''},                              //定额是补充、借用时用  补 借
     referenceRationID:String,//如果是通过模板关联子目生成的定额,这里记录对应的主定额ID
+    divideID:String,//分摊项生成的量价关联的ID
 
     //工料机特有属性
     projectGLJID:Number,  //项目工料机ID

+ 2 - 1
modules/all_models/user.js

@@ -111,6 +111,7 @@ let schema = {
         type: Number,
         default: 0
     },
-    welcomeShowTime:String
+    welcomeShowTime:String,
+    token: String
 };
 mongoose.model(collectionName, new Schema(schema, {versionKey: false}));

+ 4 - 0
modules/main/controllers/divide_controller.js

@@ -8,6 +8,10 @@ let controller = {
     updateItem:async function(req) {
         let data = req.body.data;
         return await divideFacade.updateItem(JSON.parse(data));
+    },
+    updateCoeAdjust:async function(req){
+      let data = req.body.data;
+      return await divideFacade.updateCoeAdjust(JSON.parse(data),req.session.sessionCompilation);
     }
    
 };

+ 0 - 9
modules/main/controllers/project_controller.js

@@ -81,15 +81,6 @@ module.exports = {
         res.json(result);
     },
     updateNodes:async function (req,res) {
-    /*    var data = JSON.parse(req.body.data);
-        project_facade.updateNodes(data, function (err, message, result) {
-            if (err) {
-                logger.err(err);
-                callback(req, res, err, message, null);
-            } else {
-                callback(req, res, err, message, result);
-            }
-        });*/
         let result={
             error:0
         }

+ 104 - 7
modules/main/facade/divide_facade.js

@@ -1,11 +1,14 @@
 module.exports = {
   getData: getData,
-  updateItem:updateItem
+  updateItem:updateItem,
+  updateCoeAdjust:updateCoeAdjust
 };
 
 let mongoose = require('mongoose');
 let divideModel = mongoose.model("divide_setting");
 let consts = require('../models/project_consts');
+let calculate_facade = require("../../ration_glj/facade/glj_calculate_facade");
+let ration_glj_facade = require("../../ration_glj/facade/ration_glj_facade");
 let projectConsts = consts.projectConst;
 const uuidV1 = require('uuid/v1');
 
@@ -13,6 +16,8 @@ const uuidV1 = require('uuid/v1');
 async function updateItem(data){
   let ID = data.ID;
   let tasks= [];
+  let udatas = [];
+  let proportioRationID = null;
   for(let u of data.updateDatas){
     if(u.type == 'add'){
       tasks.push({updateOne: {filter: {ID: ID}, update: {"$push":{divideList:u.doc} }}});
@@ -20,10 +25,31 @@ async function updateItem(data){
     if(u.type == "update"){
       let ndoc = {};
       let pre = "divideList.$."
+      if(u.model) pre= u.model+".$."; 
       for(let key in u.doc){
         ndoc[pre+key] = u.doc[key]
       }
-      tasks.push({updateOne: {filter: {"ID": ID,"divideList.ID":u.ID}, update: ndoc}});
+      if(u.model){//改定额工料机的自定消耗或者子目换算时,要重算消耗量,所以就不走task流程了,另外算
+        let query = {"ID": ID};
+        query[u.model+".ID"] = u.ID;
+        if(u.updateProportion == true){//更新稳定土的时候要先把所有定额工料机更新后,再计算消耗量,所以放在builkWrite后面
+          tasks.push({updateOne: {filter: query, update: ndoc}});
+          proportioRationID = u.rationID;
+        }else{
+          await divideModel.update(query,ndoc);
+          //重新计算消耗量
+          let [ntasks,ndatas] = await calculate_facade.calculateQuantityFromDivide(ID,u.rationID);
+          if(ntasks.length > 0) tasks = tasks.concat(ntasks);
+          if(ndatas.length > 0) udatas = udatas.concat(ndatas);
+        }   
+      }else if(u.updateAss == true){//修改辅助定额
+        await divideModel.update({"ID": ID,"divideList.ID":u.ID},ndoc);
+        let [ntasks,ndatas] = await calculate_facade.calculateQuantityFromDivide(ID,u.ID);
+        if(ntasks.length > 0) tasks = tasks.concat(ntasks);
+        if(ndatas.length > 0) udatas = udatas.concat(ndatas);
+      }else{
+        tasks.push({updateOne: {filter: {"ID": ID,"divideList.ID":u.ID}, update: ndoc}});
+      }
     }
     if(u.type == "delete"){
       tasks.push({updateOne: {filter: {ID: ID}, update: {"$pull":{divideList:{ID:u.ID}} }}});
@@ -34,16 +60,90 @@ async function updateItem(data){
           { multi: true })
       }
     }
-
   }
 
   if(tasks.length > 0) await divideModel.bulkWrite(tasks);
+  if(udatas.length > 0) data.updateDatas = data.updateDatas.concat(udatas);
 
+  if(proportioRationID!=null){//说明更新了稳定土
+    let [ptasks,pdatas] = await calculate_facade.calculateQuantityFromDivide(ID,proportioRationID);
+    if(ptasks.length > 0) await divideModel.bulkWrite(ptasks);
+    if(pdatas.length > 0) data.updateDatas = data.updateDatas.concat(pdatas);
+  }
 
 
   return data;
 }
 
+async function updateCoeAdjust(data,compilation){
+  let replace = [],projectGLJList=[],ration_gljs=[];
+  let result = {updateDatas:[]}
+  let ndoc = {};
+  let pre = "ration_coes.$."
+  for(let key in data.doc){
+    ndoc[pre+key] = data.doc[key]
+  }
+  await divideModel.update({ID:data.divideID,"ration_coes.ID":data.ID},ndoc);
+  result.updateDatas.push({ID:data.ID,type:'update',model:'ration_coes',doc:data.doc});
+   //添加单个工料机的情况
+   if (data.add.length > 0){
+     let [tg,pl] = await ration_glj_facade.insertAddTypeGLJ(data.add,compilation,false);
+     await divideModel.update({ID:data.divideID},{$push:{ration_gljs:{$each:tg}}});
+     ration_gljs = ration_gljs.concat(tg);
+     if(pl.length > 0) projectGLJList = projectGLJList.concat(pl);
+   }
+  //删除的情况 
+  if(data.delete.length > 0){
+    await divideModel.update({ID:data.divideID},{$pull:{ration_gljs:{$in: data.delete}}});
+  } 
+  //替换工料机的情况
+  if (data.replace.length > 0){
+    let rationGLJlist = await getRationGLJs(data.divideID,data.rationID);
+    for(let r of data.replace){
+        let ration_glj_ID = r.ID;
+        if(data.toCommercial == true) ration_glj_ID = r.originalID;
+        let r_result = await  ration_glj_facade.replaceGLJByData(r,compilation,false,rationGLJlist);
+        let rdoc = {};
+        for(let key in r_result.data){
+          rdoc[pre+key] = result.data[key]
+        }
+        //修改定额工料机
+        await divideModel.update({ID:data.divideID,"ration_gljs.ID":ration_glj_ID},rdoc);
+        result.updateDatas.push({ID:data.ID,type:'update',model:'ration_gljs',doc:r_result.data});
+        projectGLJList.push(r_result.projectGLJ);
+        
+        //新增定额工料机
+        if(r_result.newRecodes.length > 0){
+          await divideModel.update({ID:data.divideID},{$push:{ration_gljs:{$each:r_result.newRecodes}}});
+          ration_gljs = ration_gljs.concat(r_result.newRecodes);
+        }
+        //删除定额工料机
+        if(r_result.deleteList.length > 0){
+          await divideModel.update({ID:data.divideID},{$pull:{ration_gljs:{$in: r_result.deleteList}}});
+          data.delete = data.delete.concat(r_result.deleteList);
+        } 
+    }
+  }
+
+  let [ptasks,pdatas] = await calculate_facade.calculateQuantityFromDivide(data.divideID,data.rationID);
+  if(ptasks.length > 0) await divideModel.bulkWrite(ptasks);
+  if(pdatas.length > 0) result.updateDatas = result.updateDatas.concat(pdatas);
+  result.ration_gljs= ration_gljs;
+  result.projectGLJList = projectGLJList;
+  result.delete = data.delete;
+
+  return result;
+}
+
+
+async function getRationGLJs(divideID,rationID){
+  let gljList = [];
+  let divide_setting = await divideModel.findOne({ID:divideID}).lean();
+  for(let rg of divide_setting.ration_gljs){
+    if(rg.rationID == rationID) gljList.push(rg);
+  }
+  return gljList;
+}
 
 function getData(projectID, callback) {
   divideModel.findOne({projectID: projectID}, '-_id', async function (err, datas) {
@@ -55,8 +155,7 @@ function getData(projectID, callback) {
             projectID:projectID,
             divideList:[],
             ration_gljs:[],
-            ration_coes:[],
-            bills:[]
+            ration_coes:[]
           }
           await divideModel.create(datas);
           callback(0, projectConsts.DIVIDE_SETTING, datas);
@@ -68,6 +167,4 @@ function getData(projectID, callback) {
     };
   });
 
-
-
 }

+ 64 - 140
modules/main/facade/project_facade.js

@@ -6,7 +6,6 @@ module.exports = {
     markUpdateProject:markUpdateProject,
     removeProjectMark:removeProjectMark,
     updateNodes:updateNodes,
-    calcInstallationFee:calcInstallationFee,
     saveProperty: saveProperty,
     getDefaultColSetting: getDefaultColSetting,
     markProjectsToChange:markProjectsToChange,
@@ -38,77 +37,13 @@ const { fixedFlag } = require('../../../public/common_constants');
 import GLJListModel from "../../glj/models/glj_list_model";
 const projectDao = require('../../pm/models/project_model').project;
 
-
-
-async function calcInstallationFee(data) {
-    let result={};
-    let projectGLJList = [];
-    let billTasks  = generateTasks(data.bills,data.useID);
-    let rationTasks = generateTasks(data.ration,data.useID);
-    if(billTasks.length>0){
-        await bill_model.model.bulkWrite(billTasks);
-    }
-    console.log(rationTasks);
-    if(rationTasks.length>0){
-        await ration_model.model.bulkWrite(rationTasks);
-    }
-    //如果删除定额,需要删除对应的工料机
-    if(data.ration.delete.length>0){
-        let rationIDS = _.map(data.ration.delete,'ID');
-        await ration_glj_model.deleteMany({projectID: data.ration.delete[0].projectID, rationID: {"$in": rationIDS}});//删除定额工料机
-    }
-
-    let rationGLJTasks = [];
-    let updateList = [];
-    if(data.ration.update.length>0){//如果有需要更新的定额工料机
-        for(let ur of data.ration.update){
-            for(let g of ur.glj){
-                let gTasks = {
-                    updateOne:{
-                        filter:{
-                            ID:g.ID,
-                            projectID:g.projectID
-                        },
-                        update :{
-                            quantity:g.quantity,
-                            rationItemQuantity:g.rationItemQuantity
-                        }
-                    }
-                };
-                rationGLJTasks.push(gTasks);
-                updateList.push(g);
-            }
-        }
-    }
-    if(rationGLJTasks.length>0){
-       await ration_glj_model.bulkWrite(rationGLJTasks);
-    }
-
-    let newGljList = [];
-    if(data.ration.add.length>0){//新增的安装子目要增加对应的工料机
-        for(let nr of data.ration.add){
-            for(let tkey in nr.glj){
-                let [newRecode,projectGLJ] = await addInstallationGLJ(nr.glj[tkey]);
-                newGljList.push(newRecode);
-            }
-        }
-    }
-    if(newGljList.length>0){
-        await ration_glj_model.insertMany(newGljList);
-    }
-    result.update = updateList;
-    result.add = newGljList;
-    return result;
+async function createRationGLJData(glj) {
+  glj.ID = uuidV1();
+  let [info,projectGLJ ] = await ration_glj_facade.getInfoFromProjectGLJ(glj);
+  let newRecode  = ration_glj_facade.createNewRecord(info);
+  return [newRecode,projectGLJ];
 }
 
-async function addInstallationGLJ(glj) {
-    glj.ID = uuidV1();
-    let [info,projectGLJ ] = await ration_glj_facade.getInfoFromProjectGLJ(glj);
-    let newRecode  = ration_glj_facade.createNewRecord(info);
-    return [newRecode,projectGLJ];
-}
-
-
 function generateTasks(data,userID) {
     let tasks=[];
     let deleteInfo={deleted: true, deleteDateTime: new Date(), deleteBy: userID};
@@ -151,81 +86,70 @@ function generateTasks(data,userID) {
 }
 
 async function updateNodes(datas){
-    let nodeGroups = _.groupBy(datas,'type');
-    let rationTasks = [];
-    let billTasks = [];
-    let rationGLJTasks = [];
-    let projectGLJTasks = [];
-    let projectTasks = [];
-    let rationTemplateTasks = [];
-    let asyncTasks = [];
-    for(let type in nodeGroups){
-        for(let node of nodeGroups[type]){
-            if(type == projectConsts.BILLS){
-                billTasks.push(getTask(node));
-            }else if(type == projectConsts.RATION){
-                rationTasks.push(getTask(node));
-            }else if(type == projectConsts.RATION_GLJ){
-                rationGLJTasks.push(getTask(node));
-            }else if(type == projectConsts.PROJECTGLJ){
-                projectGLJTasks.push(getTask(node,'id'));
-            }else if(type == projectConsts.PROJECT){
-                projectTasks.push(getTask(node));
-            }else if(type == projectConsts.RATION_TEMPLATE){
-                rationTemplateTasks.push(getTask(node))
-            }
+  let nodeGroups = _.groupBy(datas,'type');
+  let asyncTasks = [];
+  let deleteRationIDs=[];
+  let taskMap = {};
+  taskMap[projectConsts.BILLS] = {tasks:[],model:bill_model.model};
+  taskMap[projectConsts.RATION] = {tasks:[],model:ration_model.model};
+  taskMap[projectConsts.RATION_GLJ] = {tasks:[],model:ration_glj_model};
+  taskMap[projectConsts.PROJECTGLJ] = {tasks:[],model:project_glj_model};
+  taskMap[projectConsts.PROJECT] = {tasks:[],model:projectsModel};
+  taskMap[projectConsts.RATION_TEMPLATE] = {tasks:[],model:rationTemplateModel};
+
+  for(let type in nodeGroups){
+      for(let node of nodeGroups[type]){
+          if(taskMap[type]){
+              if(type == projectConsts.RATION){
+                  if(node.action == "delete") deleteRationIDs.push(node.data.ID);
+              }
+              if(type == projectConsts.RATION_GLJ){
+                  if(node.action == "add"){//添加定額工料机的時候,要先走项目工料机的逻辑
+                      let [newRecode,projectGLJ] = await createRationGLJData(node.data);
+                      node.data =newRecode;
+                      node.projectGLJ = projectGLJ;
+                  }
+              }
+              taskMap[type].tasks.push(getTask(node));
+          }
+      }
+  }
 
-        }
-    }
-    rationTasks.length>0?asyncTasks.push(ration_model.model.bulkWrite(rationTasks)):'';
-    billTasks.length>0?asyncTasks.push(bill_model.model.bulkWrite(billTasks)):"";
-    rationGLJTasks.length>0?asyncTasks.push(ration_glj_model.bulkWrite(rationGLJTasks)):"";
-    projectGLJTasks.length>0?asyncTasks.push(project_glj_model.bulkWrite(projectGLJTasks)):"";
-    projectTasks.length>0?asyncTasks.push(projectsModel.bulkWrite(projectTasks)):"";
-    rationTemplateTasks.length>0?asyncTasks.push(rationTemplateModel.bulkWrite(rationTemplateTasks)):"";
-    return  asyncTasks.length>0?await Promise.all(asyncTasks):"";
+  for(let key in taskMap){
+      if(taskMap[key].tasks.length> 0) asyncTasks.push(taskMap[key].model.bulkWrite(taskMap[key].tasks))
+  }
 
-    function getTask(node,idFiled = 'ID') {
+  if(asyncTasks.length>0) await Promise.all(asyncTasks);
+  if(deleteRationIDs.length > 0) await ration_glj_model.deleteMany({rationID: {$in: deleteRationIDs}});
 
-        let task={
-            updateOne:{
-                filter:{},
-                update :_.cloneDeep(node.data)
-            }
-        };
-        task.updateOne.filter[idFiled] = node.data[idFiled];//现在复制项目也重新生成一个新的ID了,所以ID是唯一的
-        delete task.updateOne.update[idFiled];//防止误操作
-        return task;
-    }
-}
 
+  return datas;
+
+
+  function getTask(node,idFiled = 'ID') {
+      let task={};
+      if(node.action == "add"){
+          task.insertOne ={
+              document:node.data
+          }
+      }else if(node.action =="delete"){
+          task.deleteOne ={
+              filter:{}
+          };
+          task.deleteOne.filter[idFiled] = node.data[idFiled];
+      }else {
+          task.updateOne = {
+              filter:{},
+              update :_.cloneDeep(node.data)
+          };
+          task.updateOne.filter[idFiled] = node.data[idFiled];//现在复制项目也重新生成一个新的ID了,所以ID是唯一的
+          delete task.updateOne.update[idFiled];//防止误操作
+      }
+
+      return task;
+  }
+}
 
-/*function updateNodes(datas,callback) {
-    let tasks = [];
-    for(let node of datas){
-        tasks.push(updateOne(node))
-    }
-    async_n.parallel(tasks, function(err, results) {
-        if (!err){
-            callback(0, '', results);
-        }
-        else{
-            console.log(err);
-            callback(1, 'save project failed'+err.message, null);
-        }
-    });
-    function updateOne(node) {
-        if(node.type == projectConsts.BILLS){
-            return function (asCallback) {
-                bill_model.model.findOneAndUpdate({projectID: node.data.projectID, ID: node.data.ID,deleteInfo: null}, node.data,{new: true}, asCallback);
-            }
-        }else if(node.type ==projectConsts.RATION){
-            return function (asCallback) {
-                ration_model.model.findOneAndUpdate({projectID: node.data.projectID, ID: node.data.ID,deleteInfo: null}, node.data,{new: true}, asCallback);
-            }
-        }
-    }
-}*/
 
 //data = {feeRateID:111111,projectID:1245}; type = feeRate
 async function markUpdateProject(data,type) {

+ 13 - 3
modules/main/facade/ration_facade.js

@@ -181,7 +181,7 @@ async function modifyDivideRation(data,compilation){//目前只有替换分摊
   newRation.quantity = data.oldData.quantity;
   newRation.type=1;
   newRation.itemType = "定额";
-  newRation.programID = data.oldData.programID; 
+  if(data.oldData.programID && data.oldData.programID!="")newRation.programID = data.oldData.programID; 
   newRation.seq = data.oldData.seq;
   newRation.ParentID = data.oldData.ParentID;
   await divideModel.update({ID:data.ID},{$push:{divideList:newRation,ration_gljs:{$each:ration_gljs},ration_coes:{$each:ration_coes}}});
@@ -224,16 +224,26 @@ async function createNewMaterialRation(std,quantityDecimal,projectID){
     newData.quantity=scMathUtil.roundForObj(1 / FilterNumberFromUnit(std.unit),quantityDecimal);
     newData.from = std.type === 'complementary' ? 'cpt' : 'std';
     newData.rationAssList =await  createRationAss(std,true);
+    if(std.feeType == undefined || std.feeType == null || std.feeType ==''){//定额取费专业为空的情况下,取项目属性中的定额取费专业ID
+      newData.programID = await getProgramForProject(projectID);
+    }else {
+      newData.programID = std.feeType;
+    }
     return newData;
 }
 
 async function addMultiRation(datas,compilation) {
-    let rst = [];
+    /* let rst = [];
     for(let data of datas){
         let r = await addNewRation(data,compilation);
         rst.push(r);
     }
-    return rst;
+    return rst; */
+    const task = [];
+    for (const data of datas) {
+        task.push(addNewRation(data, compilation));
+    }
+    return await Promise.all(task);
 }
 
 async function getSameSectionRations(data,userId,compilationId){

+ 99 - 0
modules/main/middleware/index.js

@@ -0,0 +1,99 @@
+/**
+ * Created by zhang on 2020/1/8.
+ */
+
+module.exports = {
+    rationNumberChecking,
+    tenderNumberChecking,
+    stateChecking,
+};
+
+const mongoose = require("mongoose");
+const rationModel = mongoose.model("ration");
+const pmFacade = require('../../pm/facade/pm_facade');
+const online_logs = require('../../../logs/online_logs');
+import UserModel from '../../../modules/users/models/user_model';
+
+async function rationNumberChecking(req, res, next) {
+    if (req.session.systemSetting) {
+        let type = req.session.compilationVersion.indexOf("免费") == -1 ? "professional" : "normal";
+        let data = req.body.data;
+        if (typeof data === 'object') {
+            data = JSON.stringify(data);
+        }
+        data = JSON.parse(data);
+        let projectID = data.projectID;
+        let no = await rationModel.find({ projectID: projectID }).count();
+        if (no >= req.session.systemSetting[type].ration) {
+            let result = { error: 1, message: "您套用定额个数超限,请联系我们的客服人员。" };
+            return res.json(result);
+        }
+    }
+    next();
+}
+
+async function tenderNumberChecking(req, res, next) {
+    const data = JSON.parse(req.body.data);
+    const tenderCount = data.tenderCount;
+    if (tenderCount) {
+        const tenderOverrun = await pmFacade.isTenderOverrun(tenderCount, req.session);
+        if (tenderOverrun) {
+            return res.json({
+                error: 1,
+                message: '您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。'
+            });
+        }
+    }
+    next();
+}
+
+function isAjax(req) {
+    return req.headers['x-requested-with'] === 'XMLHttpRequest';
+}
+
+// 登录状态全局判断
+async function stateChecking(req, res, next) {
+    const url = req.originalUrl;
+    if (url == "\/" || /^\/login/.test(url) || /\.map|\.ico$/.test(url) || /^\/sms/.test(url) || /^\/cld/.test(url) || /^\/captcha/.test(url)) {
+        // 如果是登录页面或短信接口或cld接口则忽略判断数据
+        next();
+    } else {
+        try {
+            if (req.query.ssoID !== undefined && req.query.ssoID !== null && req.query.token !== undefined && req.query.token !== null) {
+                delete req.session.sessionUser;
+                delete req.session.sessionCompilation;
+                return res.redirect('/login' + url);
+            } else {
+                // 判断session
+                const sessionUser = req.session.sessionUser;
+                if (!sessionUser) {
+                    //处理 ajax 请求 session 过期问题
+                    if (isAjax(req)) {
+                        return res.json({ ret_code: 99, ret_msg: '登录信息失效,请您重新登录' });
+                    } else {
+                        throw 'session error';
+                    }
+                } else {
+                    const userModel = new UserModel();
+                    const isValidToken = await userModel.checkToken(sessionUser.id, sessionUser.token);
+                    if (!isValidToken) {
+                        delete req.session.sessionUser;
+                        delete req.session.sessionCompilation;
+                        if (isAjax(req)) {
+                            return res.json({ ret_code: 99, ret_msg: '' });
+                        } else {
+                            throw 'session token invalid';
+                        }
+                    }
+                }
+                res.locals.sessionUser = sessionUser;
+            }
+        } catch (error) {
+            // 最后一个页面存入session
+            req.session.lastPage = url;
+            return res.redirect('/login');
+        }
+        next();
+        await online_logs.saveOnlineTime(req);//记录登录时长
+    }
+}

+ 0 - 52
modules/main/middleware/system_setting.js

@@ -1,52 +0,0 @@
-/**
- * Created by zhang on 2020/1/8.
- */
-
-module.exports={
-    getSystemSetting,
-    rationNumberChecking:rationNumberChecking,
-    tenderNumberChecking
-};
-
-let mongoose = require("mongoose");
-let rationModel = mongoose.model("ration");
-const systemSettingModel = mongoose.model('system_setting');
-const pmFacade = require('../../pm/facade/pm_facade');
-
-// 获取系统设置,这个系统设置正常情况下有存在session中
-async function getSystemSetting() {
-    return await systemSettingModel.findOne({}).lean();
-}
-
-async function rationNumberChecking(req, res, next) {
-    if(req.session.systemSetting){
-        let type = req.session.compilationVersion.indexOf("免费") == -1?"professional":"normal";
-        let data = req.body.data;
-        if(typeof data === 'object'){
-            data = JSON.stringify(data);
-        }
-        data = JSON.parse(data);
-        let projectID = data.projectID;
-        let no = await rationModel.find({projectID:projectID}).count();
-        if(no >= req.session.systemSetting[type].ration){
-            let  result = {error:1,message:"您套用定额个数超限,请联系我们的客服人员。"};
-            return  res.json(result);
-        }
-    }
-    next();
-}
-
-async function tenderNumberChecking(req, res, next) {
-    const data = JSON.parse(req.body.data);
-    const tenderCount = data.tenderCount;
-    if (tenderCount) {
-        const tenderOverrun = await pmFacade.isTenderOverrun(tenderCount, req.session);
-        if (tenderOverrun) {
-            return res.json({
-                error: 1,
-                message: '您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。'
-            });
-        }
-    }
-    next();
-}

+ 1 - 0
modules/main/routes/divide_route.js

@@ -10,6 +10,7 @@ module.exports = function (app) {
     var divideRouter = express.Router();
 
     divideRouter.post('/updateItem', divideController.action);
+    divideRouter.post('/updateCoeAdjust', divideController.action);
     //materialRouter.post('/replace', materialController.action);//材料替换,其实是材料修改
 
     app.use('/divide',divideRouter);

+ 1 - 1
modules/main/routes/ration_route.js

@@ -2,7 +2,7 @@
  * Created by jimiz on 2017/4/7.
  */
 let express = require('express');
-let ss_middleware = require("../middleware/system_setting");
+let ss_middleware = require("../middleware/index");
 
 module.exports = function (app) {
     let rationRouter = express.Router();

+ 50 - 18
modules/pm/facade/pm_facade.js

@@ -89,7 +89,7 @@ let progressiveModel = mongoose.model('std_progressive_lib');
 let importLogsModel = mongoose.model("import_logs");
 const shareListModel = mongoose.model('share_list');
 let welcomeModel = mongoose.model("welcome_setting");
-
+let divideModel = mongoose.model("divide_setting");
 
 let featureLibModel =  mongoose.model("std_project_feature_lib");
 let scMathUtil = require('../../../public/scMathUtil').getUtil();
@@ -106,7 +106,7 @@ let qiniu = require("qiniu");
 let fs = require("fs");
 let path = require("path");
 let request = require("request");
-const systemSettingMiddleware = require('../../main/middleware/system_setting');
+const systemSettingModel = mongoose.model('system_setting');
 
 let qiniu_config = {
     "AccessKey": "_gR1ed4vi1vT2G2YITGSf4_H0fJu_nRS9Tzk3T4z",
@@ -585,6 +585,10 @@ async function copyProject(userID, compilationID, data, newProjectID = null, del
     if(originalProperty.labourCoeFile){
         copyTasks.push(commonCopy(newProjectID,originalProperty.labourCoeFile.ID,labourCoeFileID,labourCoesModel));
     }
+    //复制分摊信息
+    let divide_setting = await divideModel.findOne({projectID:originalID}, '-_id').lean();
+    if(divide_setting) copyTasks.push(copyDivide(divide_setting,newProjectID,projectGLJMap.IDMap));
+
     // 处理单价、费率文件
     if (saveAsFile) {
         let feeRateFileName = isSaveAs ? projectName : originalProperty.feeFile.name;
@@ -752,6 +756,15 @@ async  function commonCopy(newProjectID,oldID,newID,model) { //对于只需更
     return result;
 }
 
+async function copyDivide(divide_setting,newProjectID,projectGLJIDMap){
+  divide_setting.ID = uuidV1();
+  divide_setting.projectID = newProjectID;
+  for(let rg of divide_setting.ration_gljs){
+    rg.projectGLJID = projectGLJIDMap[rg.projectGLJID]
+  }
+  return await divideModel.create(divide_setting);
+}
+
 async function copyFeeRate(rootProjectID,userID,originalFeeRateFileID,feeRateFileID,newName) {//复制费率和费率文件
     let [feeRateFile,feeRate] =await feeRate_facade.getFeeRateByID(originalFeeRateFileID);
     let newFeeRateID = uuidV1();
@@ -1589,17 +1602,18 @@ async function exportTenderData(data){
     let result = {};
     let projectSetting =  await projectSettingModel.findOne({"projectID": data.projectID}, '-_id');
     if(projectSetting) result['projSetting'] = projectSetting;
-    result.bills = await billsModel.find({"projectID": data.projectID});
-    result.rations = await rationModel.find({'$or': [{projectID: data.projectID, deleteInfo: null}, {projectID: data.projectID, 'deleteInfo.deleted': {$in: [null, false]}}]});
-    result.projectGLJs = await gljListModel.find({'project_id':data.projectID});
-    result.installationFees =  await installationFeeModel.find({projectID:data.projectID});
-    result.rationGLJs = await rationGLJModel.find({projectID:data.projectID});
-    result.rationCoes = await rationCoeModel.find({projectID:data.projectID});
-    result.quantityDetails = await quantityDetailModel.find({projectID:data.projectID});
-    result.rationInstallations = await rationInstallationModel.find({projectID:data.projectID});
-    result.rationTemplates = await rationTemplateModel.find({projectID:data.projectID});
-    result.calcProgramsFile = await calcProgramsModel.findOne({projectID:data.projectID});
-    result.labourCoes = await labourCoesModel.findOne({projectID:data.projectID});
+    result.bills = await billsModel.find({"projectID": data.projectID}).lean();
+    result.rations = await rationModel.find({'$or': [{projectID: data.projectID, deleteInfo: null}, {projectID: data.projectID, 'deleteInfo.deleted': {$in: [null, false]}}]}).lean();
+    result.projectGLJs = await gljListModel.find({'project_id':data.projectID}).lean();
+    result.installationFees =  await installationFeeModel.find({projectID:data.projectID}).lean();
+    result.rationGLJs = await rationGLJModel.find({projectID:data.projectID}).lean();
+    result.rationCoes = await rationCoeModel.find({projectID:data.projectID}).lean();
+    result.quantityDetails = await quantityDetailModel.find({projectID:data.projectID}).lean();
+    result.rationInstallations = await rationInstallationModel.find({projectID:data.projectID}).lean();
+    result.rationTemplates = await rationTemplateModel.find({projectID:data.projectID}).lean();
+    result.calcProgramsFile = await calcProgramsModel.findOne({projectID:data.projectID}).lean();
+    result.labourCoes = await labourCoesModel.findOne({projectID:data.projectID}).lean();
+    result.divide_setting = await divideModel.findOne({projectID:data.projectID}, '-_id').lean();
 
     return cipher.aesEncrypt(JSON.stringify(result));
 }
@@ -1712,9 +1726,9 @@ async function importProject(data,req,updateData) {
             let [constructionProjectID,projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap] = await handleMainProjectDatas(mainData,updateData,req.session.sessionUser.id);
             result.constructionProjectID = constructionProjectID;
             if(datas.length > 1 ){//生成后统一次插入 2020-05-29
-              let newProjectSettings=[],bills=[],rations=[],projectGLJs=[],rationGLJs=[],rationCoes=[],quantityDetails=[],rationInstallations=[],rationTemplates=[],newCalcProgramsFiles=[],newLabourCoes=[];
+              let newProjectSettings=[],bills=[],rations=[],projectGLJs=[],rationGLJs=[],rationCoes=[],quantityDetails=[],rationInstallations=[],rationTemplates=[],newCalcProgramsFiles=[],newLabourCoes=[],newDivides=[];
                 for(let i = 1;i<datas.length;i++){
-                  let [newProjectSetting,tbills,trations,tprojectGLJs,trationGLJs,trationCoes,tquantityDetails,trationInstallations,trationTemplates,newCalcProgramsFile,newLabourCoe] =  await handleEachProject(datas[i],projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap)
+                  let [newProjectSetting,tbills,trations,tprojectGLJs,trationGLJs,trationCoes,tquantityDetails,trationInstallations,trationTemplates,newCalcProgramsFile,newLabourCoe,newDivide] =  await handleEachProject(datas[i],projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap)
                   if(newProjectSetting) newProjectSettings.push(newProjectSetting);
                   if(tbills.length > 0) bills = bills.concat(tbills);
                   if(trations.length > 0) rations = rations.concat(trations);
@@ -1726,6 +1740,7 @@ async function importProject(data,req,updateData) {
                   if(trationTemplates.length > 0) rationTemplates = rationTemplates.concat(trationTemplates); 
                   if(newCalcProgramsFile) newCalcProgramsFiles.push(newCalcProgramsFile);
                   if(newLabourCoe) newLabourCoes.push(newLabourCoe);
+                  if(newDivide) newDivides.push(newDivide);
                 }
 
                 if(newProjectSettings.length > 0) await insertMany(newProjectSettings,projectSettingModel);
@@ -1739,6 +1754,7 @@ async function importProject(data,req,updateData) {
                 if(rationTemplates.length > 0) await insertMany(rationTemplates,rationTemplateModel);
                 if(newCalcProgramsFiles.length > 0) await insertMany(newCalcProgramsFiles,calcProgramsModel);
                 if(newLabourCoes.length>0) await insertMany(newLabourCoes,labourCoesModel);
+                if(newDivides.length>0) await divideModel.insertMany(newDivides);
             }
 
          }
@@ -1748,7 +1764,7 @@ async function importProject(data,req,updateData) {
 
 async function handleEachProject(data,projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap){
     let bills = [],rations = [],projectGLJs = [],rationGLJs=[],rationCoes=[],quantityDetails=[],rationInstallations=[],rationTemplates=[];
-    let newProjectSetting =null,newCalcProgramsFile = null,newLabourCoe = null;
+    let newProjectSetting =null,newCalcProgramsFile = null,newLabourCoe = null,newDivide=null;
     let billsIDMap = {},projectGLJIDMap={},rationIDMap = {};
     let newProjectID = projectIDMap[data.projSetting.projectID];
     //生成新的清单;
@@ -1817,7 +1833,18 @@ async function handleEachProject(data,projectIDMap,labourCoeFileIDMap,calcProgra
         newLabourCoe = data.labourCoes;
         delete newLabourCoe._id;
     }
-    return [newProjectSetting,bills,rations,projectGLJs,rationGLJs,rationCoes,quantityDetails,rationInstallations,rationTemplates,newCalcProgramsFile,newLabourCoe]
+
+    if(data.divide_setting){
+      data.divide_setting.ID = uuidV1();
+      data.divide_setting.projectID = newProjectID;
+      for(let rg of data.divide_setting.ration_gljs){
+        rg.projectGLJID = projectGLJIDMap[rg.projectGLJID]
+      }
+      newDivide = data.divide_setting
+      delete newDivide._id;
+    } 
+
+    return [newProjectSetting,bills,rations,projectGLJs,rationGLJs,rationCoes,quantityDetails,rationInstallations,rationTemplates,newCalcProgramsFile,newLabourCoe,newDivide]
 
 }
 
@@ -2059,6 +2086,11 @@ function uploadToken() {
     return result
 }
 
+// 获取系统设置,这个系统设置正常情况下有存在session中
+async function getSystemSetting() {
+    return await systemSettingModel.findOne({}).lean();
+}
+
 // 有些方法无法通过中间件就检查单位工程数量是否超限
 // 需要到具体的业务代码中进行判断
 // 这个方法就是具体业务代码中,需要检查单位工程数量是否超限用
@@ -2066,7 +2098,7 @@ async function isTenderOverrun(tenderCount, session) {
     const userID = session.sessionUser.id;
     const compilation = session.sessionCompilation._id;
     const compilationVersion = session.compilationVersion || '免费';
-    let systemSetting = session.systemSetting || (session.systemSetting = await systemSettingMiddleware.getSystemSetting());
+    let systemSetting = session.systemSetting || (session.systemSetting = await getSystemSetting());
     // 这种情况只有在刚上线此功能时会出现,不考虑时间差
     if (!systemSetting) {
         return false;

+ 1 - 1
modules/pm/routes/pm_route.js

@@ -6,7 +6,7 @@ import BaseController from "../../common/base/base_controller";
 let express = require('express');
 let pmController = require('./../controllers/pm_controller');
 const baseController = new BaseController();
-const systemMiddleware = require('../../main/middleware/system_setting');
+const systemMiddleware = require('../../main/middleware/index');
 
 module.exports = function (app) {
 

+ 83 - 32
modules/ration_glj/facade/glj_calculate_facade.js

@@ -11,6 +11,7 @@ let ration_coe = mongoose.model('ration_coe');
 const ProjectModel = require('../../pm/models/project_model').project;
 let mixRatioModel = mongoose.model('mix_ratio');
 let std_ration_lib_ration_items = mongoose.model('std_ration_lib_ration_items');
+let divideModel = mongoose.model("divide_setting");
 let std_glj_lib_gljList_model = mongoose.model('std_glj_lib_gljList');
 let glj_type_util = require('../../../public/cache/std_glj_type_util');
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
@@ -20,6 +21,7 @@ const common_util = require('../../../public/common_util');
 
 module.exports={
     calculateQuantity:calculateQuantity,
+    calculateQuantityFromDivide:calculateQuantityFromDivide,
     getGLJTypeByID:getGLJTypeByID
 }
 //辅助定额调整、稳定土配合比、替换工料机、标准附注条件调整、添加工料机、自定义消耗量(包括删除工料机)、自定义乘系数、市场单价调整
@@ -44,6 +46,78 @@ let coeTypeMap = {
 };
 
 
+async function handleAndGetAssList(impactRation,adjustState){
+  let assList=[], assRation = null;
+  if(impactRation && impactRation.rationAssList.length>0){
+    prepareAss(impactRation.rationAssList);
+    let temTimes = [];
+    let thirdRationCodes=[];
+    for(let i=0;i<impactRation.rationAssList.length;i++){
+        let times = calculateTimes(impactRation.rationAssList[i]);
+        if(times!=0){
+            let thirdRationCode = impactRation.rationAssList[i].thirdRationCode;
+            if(thirdRationCode && thirdRationCode !=''){
+                temTimes.push(times);
+                thirdRationCodes.push(thirdRationCode)
+            }
+            assRation = await  std_ration_lib_ration_items.findOne({rationRepId:impactRation.libID,code:impactRation.rationAssList[i].assistCode});
+            assList.push({times:times,assRation:assRation});
+            adjustState.push({index:stateSeq.ass,content:impactRation.rationAssList[i].name+" "+impactRation.rationAssList[i].actualValue+" : +"+impactRation.rationAssList[i].assistCode+"x"+times});
+        }
+    }
+    if(temTimes.length == 2 &&thirdRationCodes[0] == thirdRationCodes[1] ){ //说明有第三定额
+        let times_t = temTimes[0] * temTimes[1];
+        let tration =  await  std_ration_lib_ration_items.findOne({rationRepId:impactRation.libID,code:thirdRationCodes[0]});
+        if(tration){
+            assList.push({times:times_t,assRation:tration});
+            adjustState.push({index:stateSeq.ass,content:"+"+thirdRationCodes[0]+"x"+times_t});
+        }
+    }
+  }
+  return assList;
+}
+
+
+async function calculateQuantityFromDivide(ID,rationID){
+  let tasks=[],udatas=[];
+  let divide_setting = await divideModel.findOne({ID:ID}).lean();
+  let impactRation = null,gljList=[],coeList=[],assList=[], adjustState=[],mixRatioMap = {};
+  for(let r of divide_setting.divideList){
+    if(r.ID == rationID){
+      impactRation = r;
+      break; 
+    } 
+  }
+  if(impactRation == null) return null;
+  for(let rg of divide_setting.ration_gljs){
+    if(rg.rationID == rationID) gljList.push(rg);
+  }
+  for(let co of divide_setting.ration_coes){
+    if(co.rationID == rationID) coeList.push(co);
+  }
+  assList = await handleAndGetAssList(impactRation,adjustState);
+  for(let glj of gljList){//先把混凝土,砂浆,配合比有自定义消耗的挑出来
+    if(gljUtil.isConcreteType(glj.type)) await getMixRatioMap(glj,gljList,coeList,assList,mixRatioMap);
+  }
+  for(let i =0;i<gljList.length;i++ ){
+    let r = await calculateQuantityPerGLJ(gljList[i],gljList,coeList,assList,adjustState,mixRatioMap,null);
+    if(quantityUpdateCheck(gljList[i],r) == true){
+      let ndoc={};
+      for(let key in r.doc){
+        ndoc["ration_gljs.$."+key] = r.doc[key]
+      }
+      udatas.push({ID:gljList[i].ID,model:"ration_gljs",type:"update",doc:r.doc});
+      tasks.push({updateOne: {filter: {"ID": ID,"ration_gljs.ID":gljList[i].ID}, update: ndoc}});
+    } 
+  }
+  let newName = generateRationName(impactRation,gljList);
+  udatas.push({ID:impactRation.ID,type:"update",doc:{name:newName}});
+  tasks.push({updateOne: {filter: {"ID": ID,"divideList.ID":impactRation.ID}, update: {"divideList.$.name":newName}}});
+  return [tasks,udatas];
+}
+  
+
+
 async function calculateQuantity(query,noNeedCal=null,refreshRationName = false){
     try {
         let  result ={
@@ -53,35 +127,12 @@ async function calculateQuantity(query,noNeedCal=null,refreshRationName = false)
         let impactRation = await ration.findOne({ID:query.rationID});
         let gljList = await ration_glj.find(query);//{projectID:query.projectID,rationID:query.rationID}
         let coeList = await ration_coe.find({rationID:query.rationID}).sort('seq').exec();
-        let assList=[], assRation = null, adjustState=[],mixRatioMap = {};
+        let assList=[], adjustState=[],mixRatioMap = {};
         if(!impactRation){//如果定额不存在或者已删除,返回空
             return null;
         }
         if(impactRation._doc.hasOwnProperty("rationAssList")&&impactRation.rationAssList.length>0){
-            prepareAss(impactRation.rationAssList);
-            let temTimes = [];
-            let thirdRationCodes=[];
-            for(let i=0;i<impactRation.rationAssList.length;i++){
-                let times = calculateTimes(impactRation.rationAssList[i]);
-                if(times!=0){
-                    let thirdRationCode = impactRation.rationAssList[i].thirdRationCode;
-                    if(thirdRationCode && thirdRationCode !=''){
-                        temTimes.push(times);
-                        thirdRationCodes.push(thirdRationCode)
-                    }
-                    assRation = await  std_ration_lib_ration_items.findOne({rationRepId:impactRation.libID,code:impactRation.rationAssList[i].assistCode});
-                    assList.push({times:times,assRation:assRation});
-                    adjustState.push({index:stateSeq.ass,content:impactRation.rationAssList[i].name+" "+impactRation.rationAssList[i].actualValue+" : +"+impactRation.rationAssList[i].assistCode+"x"+times});
-                }
-            }
-            if(temTimes.length == 2 &&thirdRationCodes[0] == thirdRationCodes[1] ){ //说明有第三定额
-                let times_t = temTimes[0] * temTimes[1];
-                let tration =  await  std_ration_lib_ration_items.findOne({rationRepId:impactRation.libID,code:thirdRationCodes[0]});
-                if(tration){
-                    assList.push({times:times_t,assRation:tration});
-                    adjustState.push({index:stateSeq.ass,content:"+"+thirdRationCodes[0]+"x"+times_t});
-                }
-            }
+          assList = await handleAndGetAssList(impactRation,adjustState);
         }
         // 稳定土调整状态
         const proportionStr = gljList.filter(glj => glj.rationProportion).map(glj => glj.adjustProportion || 0).join(':');
@@ -119,7 +170,7 @@ async function calculateQuantity(query,noNeedCal=null,refreshRationName = false)
 
 function quantityUpdateCheck(glj,r) {//检查,有改变的才更新
     for(let key in r.doc){
-        if(glj._doc[key] != r.doc[key]) return true
+        if(glj[key] != r.doc[key]) return true
     }
     return false
 }
@@ -290,7 +341,7 @@ async function getMixRatioMap(glj,gljList,coeList,assList,mixRatioMap) {//生成
 
 
 function noCustomQuantiyt(glj) {//是否有自定义消耗量
-    return !glj._doc.hasOwnProperty('customQuantity')||glj.customQuantity==null||glj.customQuantity==""
+    return glj.customQuantity===undefined||glj.customQuantity===null||glj.customQuantity==""
 }
 
 async function calculateAss(quantity,assList,glj) {
@@ -320,11 +371,11 @@ async function calculateAss(quantity,assList,glj) {
 
 function generateAdjustState(glj,coeList,adjustState,gljList,quantity) {
    //替换工料机 and 添加工料机
-    if(glj._doc.createType=='replace'&&glj.rcode!=glj.code){
+    if(glj.createType=='replace'&&glj.rcode!=glj.code){
         adjustState.push({index:stateSeq.replace,content:glj.rcode+'换'+glj.code});
-    }else if(glj._doc.createType=='add'){
+    }else if(glj.createType=='add'){
         let displayQuantity = quantity;
-        if(glj._doc.hasOwnProperty('customQuantity')&&(glj.customQuantity != null||glj.customQuantity != '')){
+        if(glj.hasOwnProperty('customQuantity')&&(glj.customQuantity != null||glj.customQuantity != '')){
             displayQuantity = glj.customQuantity;
         }
         displayQuantity = displayQuantity&&displayQuantity!=""?parseFloat(displayQuantity):0;
@@ -354,13 +405,13 @@ function generateAdjustState(glj,coeList,adjustState,gljList,quantity) {
     }
 
     //自定义消耗量
-    if(glj._doc.createType!='add'&&glj._doc.hasOwnProperty('customQuantity')){
+    if(glj.createType!='add'&&glj.hasOwnProperty('customQuantity')){
         if(glj.customQuantity!==null&&glj.customQuantity!=""){
             adjustState.push({index:stateSeq.cusQuantity,content:glj.code+'量'+parseFloat(glj.customQuantity)});
         }
     }
     //市场单价调整
-    if(glj._doc.hasOwnProperty('marketPriceAdjust')&&glj.marketPriceAdjust&&glj.marketPriceAdjust!=0){
+    if(glj.hasOwnProperty('marketPriceAdjust')&&glj.marketPriceAdjust&&glj.marketPriceAdjust!=0){
         //0101005价66.00
         adjustState.push({index:stateSeq.adjMak,content:glj.code+'价'+glj.marketPriceAdjust});
     }

+ 20 - 14
modules/ration_glj/facade/ration_glj_facade.js

@@ -732,7 +732,7 @@ async function addGLJ(rgList,compilation) {
     return result;
 }
 
-async function insertAddTypeGLJ(rgList,compilation) {
+async function insertAddTypeGLJ(rgList,compilation,needInsert=true) {
     let newRecodes = [],GLJMap=null;
     let projectGLJList = [];
     let [unitFileId,ext] = await  prepareExtData(rgList[0].projectID,compilation);
@@ -764,7 +764,8 @@ async function insertAddTypeGLJ(rgList,compilation) {
         newRecodes.push(createNewRecord(g));
         projectGLJList.push(result);
     }
-    await ration_glj.insertMany(newRecodes);
+    //分摊那里调用时不用真的插入
+    if(needInsert == true) await ration_glj.insertMany(newRecodes);
     return [newRecodes,projectGLJList];
 }
 
@@ -843,32 +844,37 @@ function addMixRatioToRationGLJ(g,subList,newRecodes,GLJMap){
     return newMap;
 }
 
-async  function replaceGLJByData(data,compilation) {
+async  function replaceGLJByData(data,compilation,needUpdate = true,rationGLJList) {
     let projectGljModel = new GLJListModel(),newRecodes=[],deleteList=[];
     let [unitFileId,ext] = await  prepareExtData(data.projectID,compilation);
     let result = await projectGljModel.addList(getGLJSearchInfo(data),unitFileId,ext);
     data.projectGLJID = result.id;
     if(data.toCommercial == true){//从混凝土改成商品混凝土,
-       let [contype,newR] =  await concreteTypeToCommercial(data);
+       let [contype,newR] =  await concreteTypeToCommercial(data,needUpdate);
         newRecodes.push(newR);
         data=contype;
     }else {
-        let [newList,tdelList] = await replaceMixRatio(data,result,unitFileId);
+        let [newList,tdelList] = await replaceMixRatio(data,result,unitFileId,needUpdate,rationGLJList);
         newRecodes = newList;
         deleteList = tdelList;
-        let updateResult = await ration_glj.findOneAndUpdate({ID: data.ID, projectID: data.projectID}, data);//更新定额工料机
+        if(needUpdate==true) await ration_glj.findOneAndUpdate({ID: data.ID, projectID: data.projectID}, data);//更新定额工料机
     }
 
     return {data:data,newRecodes:newRecodes,deleteList:deleteList,projectGLJ:result};
 }
 
 
-async function concreteTypeToCommercial(data) {
-   //旧的自定义消耗量改为0
-   let contype =  await ration_glj.findOneAndUpdate({ID: data.originalID}, {customQuantity:'0'});
+async function concreteTypeToCommercial(data,needUpdate) {
+    //旧的自定义消耗量改为0
+    let contype = null;
+    if(needUpdate == true){
+      contype =  await ration_glj.findOneAndUpdate({ID: data.originalID}, {customQuantity:'0'});
+    }else{
+      contype = {customQuantity:'0'}
+    }
     //因为商品混凝土是没有组成物的,所以不用考虑组成物的情况
     let new_glj =  createComercialConcreteData(data);
-    await ration_glj.create(new_glj);
+    if(needUpdate == true) await ration_glj.create(new_glj);
     return [contype,new_glj];
 }
 
@@ -878,12 +884,12 @@ function createComercialConcreteData(data){
 }
 
 
-async function replaceMixRatio(g,result,unitFileId){
+async function replaceMixRatio(g,result,unitFileId,needUpdate,trationGLJList){
     let newRecodes=[],deleteList = [];
     if(gljUtil.isConcreteType(g.type)||gljUtil.isCommercialConcreteType(g.type)){//混凝土大类,商品混凝土属于相同大类,替换前和替换后只判断一个就好了
         let IDMap = {}, projectGLJMap = {},referenceMap={},concreteList=[],newMap={};
         let projectGljModel = new GLJListModel();
-        let rationGLJList = await ration_glj.find({'rationID':g.rationID});
+        let rationGLJList = trationGLJList?trationGLJList:await ration_glj.find({'rationID':g.rationID});
         for(let r of rationGLJList){
             IDMap[r.ID] = r;
             projectGLJMap[r.projectGLJID] = r;
@@ -906,8 +912,8 @@ async function replaceMixRatio(g,result,unitFileId){
         }
     }
 
-    if(deleteList.length > 0) await ration_glj.deleteMany({'ID': {"$in": deleteList}});//删除定额工料机
-    if(newRecodes.length > 0)  await ration_glj.insertMany(newRecodes);
+    if(deleteList.length > 0 && needUpdate==true) await ration_glj.deleteMany({'ID': {"$in": deleteList}});//删除定额工料机
+    if(newRecodes.length > 0 && needUpdate==true)  await ration_glj.insertMany(newRecodes);
     return [newRecodes,deleteList]
 }
 

+ 4 - 1
modules/users/controllers/login_controller.js

@@ -16,6 +16,7 @@ const moment = require('moment');
 const Captcha = require("../models/captcha");
 let mongoose = require("mongoose");
 let systemSettingModel = mongoose.model("system_setting");
+const uuidV1 = require('uuid/v1');
 
 class LoginController {
 
@@ -71,6 +72,7 @@ class LoginController {
                     mobile: userData.mobile,
                     qq: userData.qq,
                     isUserActive: userData.isUserActive,
+                    token: uuidV1(),
                 };
 
                 request.session.sessionUser = sessionUser;
@@ -241,7 +243,8 @@ class LoginController {
                 mobile: userData.mobile,
                 qq: userData.qq,
                 isUserActive: userData.isUserActive,
-                newLogin:true
+                newLogin:true,
+                token: uuidV1(),
             };
 
             request.session.sessionUser = sessionUser;

+ 20 - 3
modules/users/models/user_model.js

@@ -185,6 +185,7 @@ class UserModel extends BaseModel {
                 qq: userData.qq,
                 latest_login: userData.latest_login,
                 isUserActive: userData.isUserActive,
+                token: userData.token,
             };
             let updateResult = await this.updateUser(condition, UpdateData);
             if (updateResult.ok === 1) {
@@ -256,9 +257,24 @@ class UserModel extends BaseModel {
      * @param {string} ssoId
      * @return {object}
      */
-    async findDataById(id) {
-        let objId = mongoose.Types.ObjectId(id);
-        return await this.db.findOne({ _id: objId });
+    async findDataById(id, fields) {
+        const objId = mongoose.Types.ObjectId(id);
+        return fields ? await this.db.findOne({_id: objId}, fields) : await this.db.findOne({_id: objId});
+    }
+
+    /**
+     * 验证用户token正确性
+     * 一个账号不允许多处同时在线,每次登陆都会更新session和数据库的token,每个请求都会比对session和数据库的token
+     * @param {String} id - 用户ID
+     * @param {String} token - 登陆生成的token
+     * @return {Boolean}
+     */
+    async checkToken(id, token) {
+        const user = await this.findDataById(id, '-_id token');
+        if (!user.token) { // 兼容第一次上线,已登陆的用户还没token,需要返回验证正确
+            return true;
+        }
+        return user.token === token;
     }
 
     async findDataByAccount(account) {
@@ -289,6 +305,7 @@ class UserModel extends BaseModel {
             create_time: new Date().getTime(),
             latest_login: new Date().getTime(),
             isUserActive: userData.isUserActive,
+            token: userData.token
         };
         return this.db.create(insertData);
     }

+ 17 - 0
public/common_util.js

@@ -44,6 +44,22 @@
         }
     }
 
+    // 控制全屏(浏览器有限制)
+    // Element.requestFullscreen的全屏和“F11”的全屏是不一样的。前者是将相关Element变成全屏显示。后者是将浏览器导航、标签等隐藏。
+    // Fullscreen API对于全屏的判断和监听都是基于Element.requestFullscreen的,比如Document.fullscreenElement。通过F11触发的全屏Document.fullscreenElement返回null,无法正确返回全屏状态。
+    // F11全屏后,无法通过Fullscreen API对全屏状态判断,会导致F11全屏后点击按钮变成了再次调用api全屏。因此,使用window.innerHeight和window.screen.height作为判断。(打开了控制台后,此方法可能会失效:无法正确或缺innerHeight)
+    // 通过F11打开全屏后,没有办法通过代码退出全屏,只能通过F11退出:
+    // https://stackoverflow.com/questions/51114885/combining-requestfullscreen-and-f11; https://stackoverflow.com/questions/43392583/fullscreen-api-not-working-if-triggered-with-f11/44368592#44368592;
+    function handleFullscreen() {
+        const isFullscreen = window.innerHeight === window.screen.height;
+        if (isFullscreen) {
+            const p = document.exitFullscreen();
+            p.catch(() => alert('按F11即可退出全屏模式'));
+        } else {
+            document.documentElement.requestFullscreen();
+        }
+    }
+
     // 给数值加上分割
     // eg: 1234567.00 => 1,234,567.00
     function standardNumber(str) {
@@ -70,6 +86,7 @@
         isDef,
         isEmptyVal,
         getSortedTreeData,
+        handleFullscreen,
         standardNumber,
     };
 });

+ 5 - 5
public/web/commonAlert.js

@@ -8,14 +8,14 @@
  * @version
  */
 
-window.alert = function(str) {
-    /*$('#commonAlert').find('p').text(str);
-     $('#commonAlert').modal('show');*/
-    hintBox.infoBox('系统提示', str, 1);
+window.alert = function (str) {
+    if (str) {
+        hintBox.infoBox('系统提示', str, 1);
+    }
 };
 
 
-!function loadHintBox(){
+!function loadHintBox() {
     $("body").append('<div id = "hintBox_container"></div>');
     $("#hintBox_container").load("../../public/scHintBox.html");
 }();

+ 3 - 1
public/web/common_ajax.js

@@ -154,7 +154,9 @@ var CommonAjax = {
 $.ajaxSetup({
     complete: function (data) {
         if (data.responseJSON&&data.responseJSON.ret_code && data.responseJSON.ret_code == 99) {
-            alert(data.responseJSON.ret_msg);
+            if (data.responseJSON.ret_msg) {
+                alert(data.responseJSON.ret_msg);
+            }
             var top = getTopWindow();
             setTimeout('top.location.href = "/login";', 300);
         }

+ 81 - 2
public/web/id_tree.js

@@ -611,7 +611,7 @@ var idTree = {
             }
             return data;
         };
-        Tree.prototype.insertByData = function (data, parentID, nextSiblingID, uid = null) {
+        Tree.prototype.insertByData = function (data, parentID, nextSiblingID, uid = null, resort = true) {
             var parent = parentID == -1 ? null : this.nodes[this.prefix + parentID];
             var nextSibling = nextSiblingID == -1 ? null : this.nodes[this.prefix + nextSiblingID];
             var node = this.nodes[this.prefix + data[this.setting.id]];
@@ -625,13 +625,45 @@ var idTree = {
                     tools.addNodes(this, parent, [node]);
                 }
                 this.nodes[this.prefix +  data[this.setting.id]] = node;
-                tools.sortTreeItems(this);
+                if (resort) {
+                    tools.sortTreeItems(this);
+                }
                 if(!uid){
                     this.maxNodeID( data[this.setting.id]);
                 }
                 return node;
             }
         };
+        Tree.prototype.multiInsert = function (datas, preID) {
+            const newNodes = [];
+            for (const data of datas) {
+                this.nodes[this.prefix + data.ID] = new Node(this, data.ID);
+                this.nodes[this.prefix + data.ID]['data'] = data;
+                newNodes.push(this.nodes[this.prefix + data.ID]);
+            }
+            const fisrtNode = this.nodes[this.prefix + datas[0].ID];
+            const firstPre = this.nodes[this.prefix + preID];
+            if (firstPre) {
+                firstPre.nextSibling = fisrtNode;
+                firstPre.data.NextSiblingID = fisrtNode.getID();
+            }
+            const parent = this.nodes[this.prefix + datas[0].ParentID] || null;
+            const parentChildren = parent ? parent.children : this.roots;
+            let baseIndex = firstPre ? parentChildren.indexOf(firstPre) + 1 : 0;
+            datas.forEach(data => {
+                const node = this.nodes[this.prefix + data.ID];
+                node.parent = parent;
+                parentChildren.splice(baseIndex++, 0, node);
+                const next = this.nodes[this.prefix + data.NextSiblingID] || null;
+                node.nextSibling = next;
+                if (next) {
+                    next.preSibling = node;
+                }
+            });
+            this.roots = tools.reSortNodes(this.roots, true);
+            tools.sortTreeItems(this);
+            return newNodes;
+        }
         //批量新增节点,节点已有树结构数据
         Tree.prototype.insertByDatas = function (datas) {
             for(let data of datas){
@@ -897,6 +929,53 @@ var idTree = {
             tools.addUpdateDataForNextSibling(data, lastNode.nextSibling, firstNode.getID());
             return data;
         };
+          //检查树结构数据有没问题
+        Tree.prototype.check = function (roots) {
+            return isValid(roots);
+            function isValid(nodes) {
+                for (let node of nodes) {
+                    if (node.data.ParentID !== -1 &&
+                        (!node.parent || node.parent.data.ID !== node.data.ParentID)) {
+                        console.log(`${node.serialNo() + 1}:${node.data.name} parent对应错误`);
+                        return false;
+                    }
+                    if (node.data.ParentID === -1 && node.parent) {
+                        console.log(`${node.serialNo() + 1}:${node.data.name} 不应有parent`);
+                        return false;
+                    }
+                    if (node.data.NextSiblingID !== -1 &&
+                        (!node.nextSibling || node.nextSibling.data.ID !== node.data.NextSiblingID)) {
+                        console.log(`${node.serialNo() + 1}:${node.data.name} next对应错误`);
+                        return false;
+                    }
+                    if (node.data.NextSiblingID === -1 && node.nextSibling) {
+                        console.log(`${node.serialNo() + 1}:${node.data.name} 不应有next`);
+                        return false;
+                    }
+                    let sameDepthNodes = node.parent ? node.parent.children : roots,
+                        nodeIdx = sameDepthNodes.indexOf(node),
+                        nextIdx = sameDepthNodes.indexOf(node.nextSibling);
+                    if (nodeIdx !== -1 && nextIdx !== -1 && nodeIdx > nextIdx) {
+                        console.log(`${node.serialNo() + 1}:${node.data.name} node索引大于next索引`);
+                        return false;
+                    }
+                    // nextSibling跟parent children的下一节点对应不上
+                    if (nodeIdx !== -1 && 
+                        (nodeIdx === sameDepthNodes.length - 1 && nextIdx !== -1) || 
+                        (nodeIdx !== sameDepthNodes.length - 1 && nodeIdx + 1 !== nextIdx)) {
+                        console.log(`${node.serialNo() + 1}:${node.data.name} nextSibling与树显示的下一节点对应不上`);
+                        return false;
+                    }
+                    if (node.children.length) {
+                        let v = isValid(node.children);
+                        if (!v) {
+                            return false;
+                        }
+                    }
+                }
+                return true;
+            }
+        };
         return new Tree(setting);
     },
     updateType: {update: 'update', new: 'new', delete: 'delete'}

+ 91 - 0
public/web/sheet/sheet_common.js

@@ -1249,5 +1249,96 @@ var sheetCommonObj = {
             sheet.getCell(row, col).wordWrap(wordWrap);
             sheet.autoFitRow(row);
         });
+    },
+    // 获取带输入框的右键子目
+    registerInputContextMenuItem(name, html, icon, callback) {
+        $.contextMenu.types[name] = function (item, opt, root) {
+            // 因为contextMenu有自己的键盘事件处理,因此输入框的键盘控制光标需要自己定义事件实现覆盖。
+            const Direction = {
+                BACKWARD: 'backward',
+                FORWARD: 'forward'
+            };
+            function moveLeft(input, isShifting) {
+                const start = input.selectionStart;
+                const end = input.selectionEnd;
+                const direction = end === start ? Direction.BACKWARD : input.selectionDirection;
+                if (isShifting) {
+                    if (direction === Direction.FORWARD) {
+                        const curEnd = end - 1;
+                        input.setSelectionRange(start, curEnd);
+                    } else {
+                        const curStart = start - 1 < 0 ? 0 : start - 1;
+                        input.setSelectionRange(curStart, end, Direction.BACKWARD);
+                    }
+                } else {
+                    const idx = end > start
+                        ? start
+                        : start - 1 < 0 
+                            ? 0 
+                            : start - 1;
+                    input.setSelectionRange(idx, idx);
+                }
+            }
+            function moveRight(input, isShifting) {
+                const start = input.selectionStart;
+                const end = input.selectionEnd;
+                const direction = start === end ? Direction.FORWARD : input.selectionDirection;
+                if (isShifting) {
+                    if (direction === Direction.BACKWARD) {
+                        const curStart = start + 1;
+                        input.setSelectionRange(curStart, end);
+                    } else {
+                        const curEnd = end + 1;
+                        input.setSelectionRange(start, curEnd, Direction.FORWARD);
+                    }
+                } else {
+                    const idx = start < end
+                        ? end
+                        : end + 1
+                    input.setSelectionRange(idx, idx);
+                }
+            }
+            function handleConfirm() {
+                if (callback) {
+                    callback();
+                }
+                root.$menu.trigger('contextmenu:hide');
+            }
+            $(html)
+                .appendTo(this)
+                .on('input', 'input', function () {
+                    const number = +$(this).val();
+                    if (isNaN(number)) {
+                        $(this).val(1);
+                    } else if (number > 99) {
+                        $(this).val(99);
+                    }
+                })
+                .on('keydown', 'input', function (e) {
+                    const key = e.key;
+                    if (key === 'ArrowUp' || key === 'ArrowDown') {
+                        return false;
+                    }
+                    if (key === 'Enter') {
+                        handleConfirm();
+                        return false;
+                    }
+                    const input = $(this)[0];
+                    if (key === 'ArrowLeft') {
+                        moveLeft(input, e.shiftKey);
+                    } else if (key === 'ArrowRight') {
+                        moveRight(input, e.shiftKey);
+                    }
+                })
+                .parent().on('click', function (e) {
+                    if (e.target.tagName === 'INPUT') {
+                        return false;
+                    }
+                    handleConfirm();
+                });
+
+            this.addClass(`context-menu-icon context-menu-icon--fa fa ${icon}`);
+        };
+        return name;
     }
 }

+ 9 - 0
public/web/socket/connection.js

@@ -92,6 +92,15 @@ socketObject = {
         socket.on('fileDataChange', function (data) {//收到单价文件、费率文件内容修改、文件切换、另存(暂时能共用,以后有需要可分离)推送消息
             if (data.projectID && typeof projTreeObj !== 'undefined') projTreeObj.refreshWhenFileDateChange(data.projectID);
         });
+        // 项目管理树数据发生变化,提示刷新
+        socket.on('pmTreeChange', function ({ expandState, selection }) {
+            const isActive = $('#tab_pm_all').hasClass('active');
+            if (isActive) {
+                $("#message").html(`树结构发生变化,请<a href="javascript:void(0);" id="load-data">点击刷新列表</a>`);
+                $('#load-data').on('click', () => projTreeObj.handleNotifyClick(expandState, selection));
+                $("#notify").show();
+            }
+        });
     },
     getFeeRateRoomID: function () {
         return projectObj.project.FeeRate.getActivateFeeRateFileID();

+ 15 - 10
public/web/syntax-detection.js

@@ -150,6 +150,11 @@ function checkSyntax() {
             }
         }
 
+        // DOM
+        if (typeof document.documentElement.requestFullscreen !== 'function') {
+            throw new TypeError('document.documentElement.requestFullscreen is not a function');
+        }
+
     } catch (err) {
         console.log(err);
         return false;
@@ -161,16 +166,16 @@ function showBrowserTip() {
     var html = '<div class="modal fade" id="browser" data-backdrop="static">' +
         '<div class="modal-dialog modal-lg" role="document">' +
         '<div class="modal-content">' +
-            '<div class="modal-body">' +
-                '<h5>浏览器版本过低,可能会有安全风险;</h5>' +
-                '<h5>请更新 「浏览器」 或者 使用 「纵横Z+造价工作平台」 登录。</h5>' +
-                '<div class="row my-4">' +
-                    '<div class="col-6">' +
-                        '<div class="text-center"><a href="https://www.microsoft.com/zh-cn/edge" class="btn btn-primary" target="_blank">下载 Microsoft Edge</a></div>' +
-                    '</div>' +
-                    '<div class="col-6">' +
-                        '<div class="text-center"><a href="https://smartcost.com.cn/downloadzplus" class="btn btn-primary" target="_blank">下载 纵横Z+造价工作平台</a></div>' +
-                    '</div></div></div></div></div></div>';
+        '<div class="modal-body">' +
+        '<h5>浏览器版本过低,可能会有安全风险;</h5>' +
+        '<h5>请更新 「浏览器」 或者 使用 「纵横Z+造价工作平台」 登录。</h5>' +
+        '<div class="row my-4">' +
+        '<div class="col-6">' +
+        '<div class="text-center"><a href="https://www.microsoft.com/zh-cn/edge" class="btn btn-primary" target="_blank">下载 Microsoft Edge</a></div>' +
+        '</div>' +
+        '<div class="col-6">' +
+        '<div class="text-center"><a href="https://smartcost.com.cn/downloadzplus" class="btn btn-primary" target="_blank">下载 纵横Z+造价工作平台</a></div>' +
+        '</div></div></div></div></div></div>';
     $('body').append(html);
     $('#browser').modal('show');
 }

+ 9 - 37
server.js

@@ -23,7 +23,6 @@ fileUtils.getGlobbedFiles('./modules/all_models/*.js').forEach(function(modelPat
 //config.setupCache();
 let cfgCacheUtil = require("./config/cacheCfg");
 cfgCacheUtil.setupDftCache();
-let online_logs = require("./logs/online_logs");
 
 
 let app = express();
@@ -56,43 +55,16 @@ app.use(session({
 }));
 
 // 登录状态全局判断
-app.use(async function (req, res, next) {
-    let url = req.originalUrl;
-    // if (/^\/login/.test(url) || /\.map|\.ico$/.test(url) || /^\/sms/.test(url) || /^\/cld/.test(url) || /^\/captcha/.test(url)  || /^\/accountIsPro/.test(url)) {
-    if (/^\/login/.test(url) || /\.map|\.ico$/.test(url) || /^\/sms/.test(url) || /^\/cld/.test(url) || /^\/captcha/.test(url)) {
-        // 如果是登录页面或短信接口或cld接口则忽略判断数据
-        next();
-    } else {
-        try {
-            if (req.query.ssoID !== undefined && req.query.ssoID !== null && req.query.token !== undefined && req.query.token !== null) {
-                delete req.session.sessionUser;
-                delete req.session.sessionCompilation;
-                return res.redirect('/login' + url);
-            } else {
-                // 判断session
-                let sessionUser = req.session.sessionUser;
-                if (!sessionUser) {
-
-                    //处理 ajax 请求 session 过期问题
-                    if (req.headers["x-requested-with"] != null
-                        && req.headers["x-requested-with"] == "XMLHttpRequest"
-                        && req.url != "/login") {
-                        return res.json({ret_code: 99, ret_msg: '登录信息失效,请您重新登录'});
-                    } else {
-                        throw 'session error';
-                    }
-                }
-                res.locals.sessionUser = sessionUser;
-            }
-        } catch (error) {
-            // 最后一个页面存入session
-            req.session.lastPage = url;
-            return res.redirect('/login');
-        }
-        await online_logs.saveOnlineTime(req);//记录登录时长
-        next();
-    }
+/* const { stateChecking } = require('./modules/main/middleware/index');
+app.use(stateChecking); */
+app.use(function (req, res, next) {
+    // 在内部在调用,而不直接在外部require后直接作为中间件回调函数app.use(stateChecking);的原因:
+    // 由于各模块的引用不全是require,有些是import,import是预编译的。外部引用中间件可能会造成一些引用丢失。如:project_model.js中gljFacade.addMixRatioForNew会丢失
+    // 不用过分担心性能问题。require有cache机制
+    const { stateChecking } = require('./modules/main/middleware/index');
+    stateChecking(req, res, next);
 });
+
 //加载路由文件
 fileUtils.getGlobbedFiles('./modules/**/routes/*.js').forEach(function(modelPath) {
     if(modelPath.indexOf("import/routes")==-1) require(path.resolve(modelPath))(app);//排除掉导出导入服务

+ 5 - 0
socket.js

@@ -123,6 +123,11 @@ socketIO.on('connection', function(socket) {
         }
     });
 
+    // 项目管理树结构变化
+    socket.on('pmTreeChange', function ({ userID, compilationID, expandState, selection }) {
+        socket.broadcast.to(`${userID}@${compilationID}`).emit('pmTreeChange', { expandState, selection });
+    });
+
     socket.on('disconnect', function () {
         // 由于用户可以重复打开项目,因此不做唯一性处理,只删除一个数据,不删除所有的同用户数据
         if (curProjectID && userCache[curProjectID]) {

+ 16 - 1
web/building_saas/css/custom.css

@@ -460,4 +460,19 @@ input.text-right{
     left: 50% !important;
     transform: translateX(-50%) !important;
     width: 123% !important;
-}
+}
+
+/* 右键菜单input */
+.menu-input {
+    width: 2.5rem;
+    border: 1px solid rgb(221, 221, 221);
+    border-radius: 2px;
+    height: 1.3rem;
+    text-align: center;
+}
+.menu-input:focus {
+    outline: none;
+}
+/* .menu-input::selection {
+    background-color: rgb(41, 128, 185);
+} */

+ 9 - 27
web/building_saas/main/html/divide.html

@@ -1,8 +1,8 @@
 <div class="toolsbar d-flex px-1 justify-content-between" id="divideToolsBar">
   <div class="form-inline py-1">
-      <a class="btn btn-sm ml-1" href="#" id = "exec_divide_btn" >执行分摊</a>
-      <a class="btn btn-sm ml-1" href="#" data-toggle="modal" data-target="#cancel_divide"> 取消分摊</a></label>
-      <a class="btn btn-sm ml-1" href="#" data-toggle="modal" data-target="#cancel_all_divide"> 取消所有分摊</a></label>
+      <a class="btn btn-sm ml-1" href="#" id ="exec_divide_btn" >执行分摊</a>
+      <a class="btn btn-sm ml-1" href="#" id="cancel_divide_btn" > 取消分摊</a></label>
+      <a class="btn btn-sm ml-1" href="#" id="cancel_all_divide_btn" > 取消所有分摊</a></label>
   </div>
   <div class="divide_side-tabs form-inline">
     <ul class="nav nav-tabs" role="tablist">
@@ -18,8 +18,8 @@
   <div class="row" id="divideRow">
       <div class="main-content col p-0 " id="divide_main" style="overflow: hidden">
        <div class="top-content row"  id="divide_top" style="overflow: hidden;margin-left: 0px;margin-right: 0px;">
-          <div class="main-data-top left col-6" style="padding: 0px;" id="divide_main_sheet"></div>
-          <div class="main-data-top left col-6" style="padding: 0px;" id="divide_bills_sheet"></div>
+          <div class="main-data-top left col-7" style="padding: 0px;" id="divide_main_sheet"></div>
+          <div class="main-data-top left col-5" style="padding: 0px;" id="divide_bills_sheet"></div>
        </div>
        <div class="resize-y" id="divideResize"></div>
           <div class="bottom-content" id="divideBottom">
@@ -148,45 +148,27 @@
                       <div class=" row" style="margin-left: 10px;">
                         <div class=" col-6">
                           <label class="form-check-label">
-                            <input class="form-check-input" name="divide_type" id="divide_by_bills" value="0" type="radio">
+                            <input class="form-check-input divide_type" name="divide_type" id="divide_by_bills" value="0" type="radio">
                             按清单金额分摊
                         </label>
                         </div>
                          <div class=" col-6">
                           <label class="form-check-label">
-                              <input class="form-check-input" name="divide_type" id="divide_by_concrete" value="1" type="radio">
+                              <input class="form-check-input divide_type" name="divide_type" id="divide_by_concrete" value="1" type="radio">
                               按混凝土用量分摊
                           </label>
                         </div>
                         <div class=" col-6">
                           <label class="form-check-label">
-                            <input class="form-check-input" name="divide_type" id="divide_by_customer" value="2" type="radio">
+                            <input class="form-check-input divide_type" name="divide_type" id="divide_by_customer" value="2" type="radio">
                             自定义分摊比例
                         </label>
                         </div>
                       </div>
-                     <!--  <div class="form-check col-6">
-                          <label class="form-check-label">
-                              <input class="form-check-input" name="install_setting_radios" id="all_project_calc" value="0" type="radio">
-                              整个项目统一计取
-                          </label>
-                      </div>
-                      <div class="form-check col-6">
-                          <label class="form-check-label">
-                              <input class="form-check-input" name="install_setting_radios" id="FB_calc" value="1" type="radio">
-                              每个分部单独计取
-                          </label>
-                      </div>
-                      <div class="form-check col-6">
-                        <label class="form-check-label">
-                            <input class="form-check-input" name="install_setting_radios" id="FB_calc" value="1" type="radio">
-                            每个分部单独计取
-                        </label>
-                    </div> -->
                   </fieldset>
               </div>
           </div>
-              <button  class="btn btn-primary" id="calc_installation_fee_confirm">确定</button>
+              <button  class="btn btn-primary" id="divide_confirm" data-dismiss="modal">确定</button>
               <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
           </div>
       </div>

+ 1 - 1
web/building_saas/main/html/main.html

@@ -1906,7 +1906,7 @@
     <img src="/web/dest/css/img/engineering.png" id="eng_pic" style="display: none">
     <img src="/web/dest/css/img/tender.png" id="tender_pic" style="display: none">
 
-    <img src="/web/dest/css/img/blockLib.png" id="blockLib_pic" style="display: none">
+    <img src="/web/dest/css/img/blocklib.png" id="blockLib_pic" style="display: none">
     <img src="/web/dest/css/img/folder_open.png" id="folder_pic" style="display: none">
     <img src="/web/dest/css/img/tender.png" id="block_pic" style="display: none">
 

+ 78 - 0
web/building_saas/main/js/controllers/project_controller.js

@@ -54,6 +54,84 @@ ProjectController = {
             cbTools.refreshFormulaNodes();
         });
     },
+    /* addBillsByData: async function (postData, insertFunc) {
+        await ajaxPost('/bills/insertBills', { postData });
+        // 插入
+        const insertData = postData.filter(item => item.updateType === 'create');
+        const treeData = insertData.map(item => item.updateData);
+        // 插入清单节点
+        if (!insertFunc) {
+            insertFunc = projectObj.project.Bills.tree.insertByDatas;
+        }
+        projectObj.project.Bills.tree.insertByDatas(treeData);
+        projectObj.project.Bills.datas = projectObj.project.Bills.datas.concat(treeData);
+        // 插入主树节点
+        const newNodes = projectObj.project.mainTree.insertByDatas(treeData);
+        for (const node of newNodes) {
+            node.source = projectObj.project.Bills.tree.nodes[projectObj.project.Bills.tree.prefix + node.getID()];
+            node.data = node.source.data;
+            node.sourceType = projectObj.project.Bills.getSourceType();
+        }
+        ProjectController.syncDisplayNewNodes(projectObj.mainController, newNodes, true);
+        return newNodes;
+    }, */
+    addBillsByData: async function (postData, isSameDepth = false) {
+        await ajaxPost('/bills/insertBills', { postData });
+        // 插入
+        const insertData = postData.filter(item => item.updateType === 'create');
+        const treeData = insertData.map(item => item.updateData);
+        // 插入清单节点和主树节点
+        projectObj.project.Bills.datas = projectObj.project.Bills.datas.concat(treeData);
+        let newNodes;
+        if (isSameDepth) {
+            const pre = postData.find(item => item.updateType === 'update');
+            const preID = pre && pre.updateData.ID || null;
+            projectObj.project.Bills.tree.multiInsert(treeData, preID);
+            newNodes = projectObj.project.mainTree.multiInsert(treeData, preID);
+        } else {
+            projectObj.project.Bills.tree.insertByDatas(treeData);
+            newNodes = projectObj.project.mainTree.insertByDatas(treeData);
+        }
+        for (const node of newNodes) {
+            node.source = projectObj.project.Bills.tree.nodes[projectObj.project.Bills.tree.prefix + node.getID()];
+            node.data = node.source.data;
+            node.sourceType = projectObj.project.Bills.getSourceType();
+        }
+        ProjectController.syncDisplayNewNodes(projectObj.mainController, newNodes, true);
+        return newNodes;
+    },
+    getBillsPostData: function (number) {
+        const project = projectObj.project;
+        const target = project.getParentTarget(project.mainTree.selected, 'sourceType', project.Bills.getSourceType());
+        const baseParentID = target.depth() === 0 ? target.source.getID() : target.source.getParentID();
+        const baseNextID = target.depth() === 0 ? -1 : target.source.getNextSiblingID();
+        const updateNode = target.depth() === 0 ? target.source.children[target.source.children.length - 1] : target;
+        const insertData = [];
+        for (let i = 0; i < number; i++) {
+            const data = {
+                type: billType.BILL,
+                projectID: project.ID(),
+                ID: uuid.v1(),
+                ParentID: baseParentID,
+            };
+            const pre = insertData[i - 1];
+            if (pre) {
+                pre.NextSiblingID = data.ID;
+            }
+            if (i === number - 1) {
+                data.NextSiblingID = baseNextID;
+            }
+            insertData.push(data);
+        }
+        const postData = insertData.map(item => ({
+            updateType: 'create',
+            updateData: item
+        }));
+        if (updateNode) {
+            postData.push({ updateType: 'update', updateData: { ID: updateNode.getID(), NextSiblingID: insertData[0].ID } });
+        }
+        return postData;
+    },
     addBills: function (project, sheetController, std) {
         if (!project || !sheetController) { return null; }
         let target = project.getParentTarget(project.mainTree.selected, 'sourceType', project.Bills.getSourceType());

+ 31 - 0
web/building_saas/main/js/models/cache_tree.js

@@ -408,6 +408,37 @@ var cacheTree = {
 
             return newNode;
         };
+        //  一次性插入多个连续的同层节点
+        Tree.prototype.multiInsert = function (datas, preID) {
+            const newNodes = [];
+            for (const data of datas) {
+                this.nodes[this.prefix + data.ID] = new Node(this, data.ID);
+                newNodes.push(this.nodes[this.prefix + data.ID]);
+            }
+            const fisrtNode = this.nodes[this.prefix + datas[0].ID];
+            const firstPre = this.nodes[this.prefix + preID];
+            if (firstPre) {
+                firstPre.nextSibling = fisrtNode;
+                firstPre.data.NextSiblingID = fisrtNode.getID();
+            }
+            const parent = this.nodes[this.prefix + datas[0].ParentID] || null;
+            const parentChildren = parent ? parent.children : this.roots;
+            let baseIndex = firstPre ? parentChildren.indexOf(firstPre) + 1 : 0;
+            datas.forEach(data => {
+                const node = this.nodes[this.prefix + data.ID];
+                node.parent = parent;
+                parentChildren.splice(baseIndex++, 0, node);
+                const next = this.nodes[this.prefix + data.NextSiblingID] || null;
+                node.nextSibling = next;
+                if (next) {
+                    next.preSibling = node;
+                }
+            });
+            this.roots = tools.reSortNodes(this.roots, true);
+            this.sortTreeItems();
+            return newNodes;
+        }
+        // 插入某一完整片段到某节点的子项中
         Tree.prototype.insertByDatas = function (datas) {
             let rst = [];
             for(let data of datas){

+ 45 - 32
web/building_saas/main/js/models/calc_base.js

@@ -2,7 +2,6 @@
  * Created by Zhong on 2017/11/28.
  */
 
-
 let cbTools = {
     isDef: function (v) {
         return v !== undefined && v !== null;
@@ -384,6 +383,11 @@ let cbTools = {
              if (node.data.calcBase.hasSubStr(sID)) return true;
         };
     },
+    // 获取直接关联清单节点的基数金额
+    getBaseFee: function (flag, tender, feeField) {
+        const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
+        return this.getBillsFee(flag, feeField, subFeeField);
+    },
     //获取清单节点的金额
     //@param {Number}fixedFlag(清单固定行类别) {String}feeField(外层金额字段: common) {String}subFeeField(子金额字段: totalFee)
     //@return {Number}
@@ -418,10 +422,17 @@ let cbTools = {
             ? bills.feesIndex[feeField][subFeeField]
             : 0;
     },
-    //获取扣除固定项后的金额,扣除其节点后重新汇总
-    //@param {Number}fixedFlag(基数取值固定行类别) {Array}deductFlags(扣除的固定类别组) {String}fullFeeField(完整的取费字段: 'rationCommon.totalFee')
-    //@return {Number}
-    getFeeWithDeduction: function (fixedFlag, deductFlags, fullFeeField) {
+    /**
+     * 获取扣除固定项后的金额,扣除其节点后重新汇总
+     * @param {Number} fixedFlag - 基数取值固定行类别
+     * @param {Array} deductFlags - 扣除的固定类别组
+     * @param {Boolean} tender - 是否调价
+     * @param {String} feeField - 价格字段
+     * @param {Boolean = true} isRound - 是否取舍
+     * @return {Number}
+     */
+    getFeeWithDeduction: function (fixedFlag, deductFlags, tender, feeField, isRound = true) {
+        const fullFeeField = tender ? `${feeField}.tenderTotalFee` : `${feeField}.totalFee`;
         let baseNode = this.findNodeByFlag(fixedFlag);
         if (!baseNode) {
             return 0;
@@ -434,8 +445,24 @@ let cbTools = {
                 deductNodes.push(node);
             }
         }
-        return projectObj.project.calcProgram.getTotalFee([baseNode], deductNodes, fullFeeField);
+        const fee = projectObj.project.calcProgram.getTotalFee([baseNode], deductNodes, fullFeeField);
+        return isRound ? fee.toDecimal(decimalObj.bills.totalPrice) : fee;
     },
+    /* getFeeWithDeduction: function (fixedFlag, deductFlags, fullFeeField) {
+        let baseNode = this.findNodeByFlag(fixedFlag);
+        if (!baseNode) {
+            return 0;
+        }
+        //要扣除的节点
+        let deductNodes = [];
+        for (let deFlag of deductFlags) {
+            let node = this.findNodeByFlag(deFlag);
+            if (node) {
+                deductNodes.push(node);
+            }
+        }
+        return projectObj.project.calcProgram.getTotalFee([baseNode], deductNodes, fullFeeField);
+    }, */
     //获取累进办法计算的金额
     //@param {Number}baseFee(相关基数金额) {String}name(使用累进计算的基数名称)
     //@return {Number}
@@ -537,65 +564,51 @@ let baseFigureTemplate = {
         //{定额建筑安装工程费(不含定额设备购置费及专项费用)}
         //取清单固定类别是“建筑安装工程”的定额建安费,但要扣除清单固定类别是“设备购置费”、及“专项费用”的定额建安费
         'DEJZAZGCFBHSBZX': function (tender) {
-            let fullFeeField = tender ? 'rationCommon.tenderTotalFee' : 'rationCommon.totalFee',
-                deductFlags = [fixedFlag.EQUIPMENT_ACQUISITION_FEE, fixedFlag.SPECIAL_COST];
-            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, fullFeeField).toDecimal(decimalObj.bills.totalPrice);
+            const deductFlags = [fixedFlag.EQUIPMENT_ACQUISITION_FEE, fixedFlag.SPECIAL_COST];
+            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, tender, 'rationCommon');
         },
         //{定额建筑安装工程(其中定额设备购置费按 40%计)} (定额建筑安装工程设备四十)
         //扣除设备购置费,再加上设备购置费的40%,扣除汇总算法不四舍五入,相当于汇总当中定额设备购置费就按照了40%计
         'DEJZAZGCSBSS': function (tender) {
-            let feeField = 'rationCommon',
-                subFeeField = tender ? 'tenderTotalFee' : 'totalFee',
-                deductFlags = [fixedFlag.EQUIPMENT_ACQUISITION_FEE];
+            const feeField = 'rationCommon';
+            const deductFlags = [fixedFlag.EQUIPMENT_ACQUISITION_FEE];
             //建安费扣除定额设备购置费
-            let afterDeductFee = cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, `${feeField}.${subFeeField}`);
+            const afterDeductFee = cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, tender, feeField, false);
             //定额设备购置费
-            let equipmentAcFee = cbTools.getBillsFee(deductFlags[0], feeField, subFeeField);
+            let equipmentAcFee = cbTools.getBaseFee(deductFlags[0], tender, feeField);
             return (afterDeductFee + equipmentAcFee * 0.4).toDecimal(decimalObj.bills.totalPrice);
         },
         //{建筑安装工程费(不含安全生产费)}
         // 取清单固定类别是“建筑安装工程”的金额,但要扣除清单固定类别是“安全生产费”的金额
         'JZAZGCFBHSC': function (tender) {
-            let fullFeeField = tender ? 'common.tenderTotalFee' : 'common.totalFee',
-                deductFlags = [fixedFlag.SAFE_COST];
             //建安费扣除安全生产费
-            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, fullFeeField).toDecimal(decimalObj.bills.totalPrice);
+            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, [fixedFlag.SAFE_COST], tender, 'common');
         },
         //{建筑安装工程费(不含设备费)}
         // 取清单固定类别是“建筑安装工程”的金额,但要扣除清单固定类别是“设备购置费”的金额
         'JZAZGCFBHSB': function (tender) {
-            let fullFeeField = tender ? 'common.tenderTotalFee' : 'common.totalFee',
-                deductFlags = [fixedFlag.EQUIPMENT_ACQUISITION_FEE];
             //建安费扣除设备费
-            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, fullFeeField).toDecimal(decimalObj.bills.totalPrice);
+            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, [fixedFlag.EQUIPMENT_ACQUISITION_FEE], tender, 'common');
         },
         //{建筑安装工程费}
         // 取清单固定类别是“建筑安装工程”的金额
         'JZAZGCF': function (tender) {
-            let feeField = 'common',
-                subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(calcBase.fixedFlag.CONSTRUCTION_INSTALL_FEE, feeField, subFeeField);
+            return cbTools.getBaseFee(calcBase.fixedFlag.CONSTRUCTION_INSTALL_FEE, tender, 'common');
         },
         //{土地使用及拆迁补偿费}
         // 取清单固定类别是“土地使用及拆迁补偿费”的金额
         'TDSYJCQBCF': function (tender) {
-            let feeField = 'common',
-                subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(calcBase.fixedFlag.LAND_USED_DEMOLITION, feeField, subFeeField);
+            return cbTools.getBaseFee(calcBase.fixedFlag.LAND_USED_DEMOLITION, tender, 'common');
         },
         //{养护工程其他费}
         // 取清单固定类别是“养护工程其他费”的金额
         'YHGCQTF': function (tender) {
-            let feeField = 'common',
-                subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(calcBase.fixedFlag.MAINTENANCE_EXPENSES, feeField, subFeeField);
+            return cbTools.getBaseFee(calcBase.fixedFlag.MAINTENANCE_EXPENSES, tender, 'common');
         },
         //{预备费}
         // 取清单固定类别是“预备费”的金额
         'YBF': function(tender) {
-            let feeField = 'common',
-                subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(calcBase.fixedFlag.BUDGET_FEE, feeField, subFeeField);
+            return cbTools.getBaseFee(calcBase.fixedFlag.BUDGET_FEE, tender, 'common');
         },
         //{施工场地建设费}
         //使用累进办法计算,基数为{定额建筑安装工程费(不含定额设备购置费及专项费用)}

+ 101 - 23
web/building_saas/main/js/models/project.js

@@ -404,30 +404,108 @@ var PROJECT = {
                 }
             })
         };
-
+        project.prototype.syncUpdateNodesAndRefresh =async function (datas) {
+          let me = this;
+          return new Promise(function (resolve, reject) {
+              me.updateNodes(datas,function (result) {
+                  let nodes = me.updateNodesCache(result);
+                  projectObj.mainController.refreshTreeNode(nodes);
+                  resolve(nodes);
+              });
+          });
+      };
         project.prototype.updateNodesCache =function (datas) {
-            let refreshNode = [];
-            for(let d of datas){
-                let temObj = null;
-                if(d.type == ModuleNames.bills || d.type == ModuleNames.ration){//如果是树节点类型,直接取树节点更新
-                    let temNode = this.mainTree.getNodeByID(d.data.ID);
-                    if(temNode){
-                        temObj = temNode.data;
-                        refreshNode.push(temNode);
-                    }
-                }else {//其它类型,更新datas
-                    temObj = _.find(this[d.type].datas,{"ID":d.data.ID});
-                }
-                if(temObj){
-                    for(let key in d.data){
-                        if(key == 'ID' || key == 'id'){
-                            continue;
-                        }
-                        this.setValue(temObj,key,d.data[key])
-                    }
-                }
-            }
-            return refreshNode;
+          let refreshNode = [];
+          let reclacQuantity = false;
+          let deleteNode=[],addNodeDatas=[];
+          for(let d of datas){
+              let temObj = null;
+              if(d.type == ModuleNames.bills || d.type == ModuleNames.ration){//如果是树节点类型,直接取树节点更新
+                  if(d.action =="add"){
+                      if(d.type == ModuleNames.ration) this.Ration.datas.push(d.data);
+                      reclacQuantity = true;
+                      addNodeDatas.push(d);
+                  }else {
+                      let temNode = this.mainTree.getNodeByID(d.data.ID);
+                      if(temNode){
+                          if(d.action =="delete"){
+                              if(d.type == ModuleNames.ration){
+                                  _.remove(this.Ration.datas,{'ID':d.data.ID});
+                                  this.Ration.deleteSubListOfRation({ID:d.data.ID});
+                                  reclacQuantity = true;
+                              }
+                              deleteNode.push(temNode);//对于删除节点,统一不加入refreshNode中,由外部加入,避免重复
+                          }else {
+                              temObj = temNode.data;
+                              if(gljUtil.isDef(d.data.quantity))reclacQuantity = true;
+                              refreshNode.push(temNode);
+                          }
+                      }
+                  }
+              }else if(d.type == ModuleNames.project){
+                  temObj = this;
+              }else if (d.type == ModuleNames.ration_glj && d.action == "add"){
+                  this[d.type].datas.push(d.data);
+                  if(d.projectGLJ) this.projectGLJ.loadNewProjectGLJToCache(d.projectGLJ);
+              } else {//其它类型,更新datas
+                  temObj = _.find(this[d.type].datas,{"ID":d.data.ID});
+              }
+              if(temObj){
+                  for(let key in d.data){
+                      if(key == 'ID' || key == 'id'){
+                          continue;
+                      }
+                      this.setValue(temObj,key,d.data[key])
+                  }
+              }
+          }
+
+          //对树节点的操作并删除、添加清单、删除添加定额、删除对应的定额工料机缓存
+          TREE_SHEET_HELPER.massOperationSheet(projectObj.mainController.sheet, function () {
+              deleteTreeNodes(deleteNode);
+              addNewNodes(addNodeDatas,refreshNode);
+          });
+
+
+          if(reclacQuantity) this.projectGLJ.calcQuantity();
+          return refreshNode;
+
+
+
+
+          function deleteTreeNodes(deleteNodes) {
+              let controller = projectObj.mainController, project = projectObj.project;
+              let Bill = project.Bills, Ration = project.Ration;
+              for(let rd of deleteNodes){
+                  controller.sheet.deleteRows(rd.serialNo(),1);
+                  controller.tree.delete(rd);
+                  if(rd.sourceType == Bill.getSourceType()) Bill.tree.delete(rd.source);
+              }
+          }
+          function addNewNodes(newDatas,refreshNode) {
+              let controller = projectObj.mainController;
+              let Bill = projectObj.project.Bills;
+              let newAddNode = [];
+              for(let nr of newDatas){
+                  let nextID = -1;
+                  let preNode = projectObj.project.mainTree.getNodeByID(nr.preSiblingID);
+                  if(preNode) nextID = preNode.getNextSiblingID();
+                  let newNode = projectObj.project.mainTree.insert(nr.parentID, nextID, nr.data.ID);
+                  if(nr.type == ModuleNames.bills){
+                      let newSource = Bill.tree.insertByData(nr.data, nr.ParentID, nextID, true);
+                      newNode.source = newSource;
+                  }else {
+                      newNode.source = nr.data;
+                  }
+                  newNode.sourceType = nr.type;
+                  newNode.data = nr.data;
+                  controller.sheet.addRows(newNode.serialNo(), 1);
+                  controller.sheet.showRow(newNode.serialNo(), GC.Spread.Sheets.VerticalPosition.center);
+                  newAddNode.push(newNode);
+                  refreshNode.push(newNode);
+              }
+              TREE_SHEET_HELPER.refreshTreeNodeData(controller.setting, controller.sheet, newAddNode, false);
+          }
         };
         project.prototype.setValue=function (obj,key,value) {
             let keyArray = key.split('.');

+ 1 - 0
web/building_saas/main/js/models/project_glj.js

@@ -1151,6 +1151,7 @@ ProjectGLJ.prototype.updatePrice = function (recode, updateField, newval,from,cb
             let nodes = me.getImpactRationNodes(gljs);//取到因为改变工料机价格而受影响的定额
             projectObj.project.calcProgram.calcNodesAndSave(nodes);//触发计算程序
             if(materialGljList.length==0)projectGljObject.onUnitFileChange(result);
+            divideObj.calcDivideAfterGLJUpdate(gljs);
             if(cb){
                 cb(gljs);
             }

+ 34 - 52
web/building_saas/main/js/models/ration.js

@@ -461,7 +461,7 @@ var Ration = {
                 }
             })
         };
-        ration.prototype.addMultiRation = function (items, callback) {
+        ration.prototype.addMultiRation = async function (items) {
             let me = this;
             let project = projectObj.project, sheetController = projectObj.mainController;
             let engineering = projectObj.project.projectInfo.property.engineering;
@@ -511,57 +511,39 @@ var Ration = {
                     }
                     newDatas.push({itemQuery: items[i].itemQuery, newData: newData, defaultLibID: rationLibObj.getDefaultStdRationLibID(), calQuantity: calQuantity, brUpdate: brUpdate, needInstall: needInstall})
                 }
-                let showLoding = true;
-                $.bootstrapLoading.start();
-                //保证由于异步的关系loading界面被隐藏,比如清单指引插入清单定额时,endUpdate中提前隐藏了loading
-                let interval =setInterval(function () {
-                    if(!$.bootstrapLoading.isLoading()&& showLoding){
-                        $.bootstrapLoading.start();
-                        clearInterval(interval);
-                    }
-                    else{
-                        clearInterval(interval);
-                    }
-                }, 100);
-                CommonAjax.post("/ration/addMultiRation",{projectID:me.project.ID(),newDatas: newDatas},function (rstData) {
-                    let newNodes = [];
-                    //更新缓存
-                    for(let data of rstData){
-                        me.datas.push(data.ration);
-                        me.addSubListOfRation(data,false);
-                        //插入树节点
-                        newSource = data.ration;
-                        newNode = project.mainTree.insert(billItemID, nextID, newSource.ID);
-                        newNodes.push(newNode);
-                        newNode.source = newSource;
-                        newNode.sourceType = project.Ration.getSourceType();
-                        newNode.data = newSource;
-                        ProjectController.syncDisplayNewNode(sheetController, newNode);
-                        nextID = project.mainTree.selected.getNextSiblingID();
-                    }
-                    project.projectGLJ.calcQuantity();
-                    for(let data of rstData){
-                        project.ration_glj.addToMainTree(data.ration_gljs);
-                    }
-                    projectObj.mainController.refreshTreeNode(newNodes, false);
-                    if(project.Bills.isFBFX(newNodes[0])) { //判断是否属于分部分项工程 ,是的话才需要做计取安装费计算
-                        project.installation_fee.calcInstallationFee(function (isChange,rations) {
-                            if(isChange){
-                                rations = rations.concat(newNodes);
-                                project.calcProgram.calcNodesAndSave(rations);
-                            }else {
-                                project.calcProgram.calcNodesAndSave(newNodes);
-                            }
-                        });
-                    }else {
-                        project.calcProgram.calcNodesAndSave(newNodes);
-                    }
-                    if(callback){
-                        callback();
-                    }
-                    showLoding = false;
-                    $.bootstrapLoading.end();
-                })
+                const rstData = await ajaxPost('/ration/addMultiRation', { projectID: me.project.ID(), newDatas });
+                let newNodes = [];
+                //更新缓存
+                for(let data of rstData){
+                    me.datas.push(data.ration);
+                    me.addSubListOfRation(data,false);
+                    //插入树节点
+                    newSource = data.ration;
+                    newNode = project.mainTree.insert(billItemID, nextID, newSource.ID);
+                    newNodes.push(newNode);
+                    newNode.source = newSource;
+                    newNode.sourceType = project.Ration.getSourceType();
+                    newNode.data = newSource;
+                    ProjectController.syncDisplayNewNode(sheetController, newNode);
+                    nextID = project.mainTree.selected.getNextSiblingID();
+                }
+                project.projectGLJ.calcQuantity();
+                for(let data of rstData){
+                    project.ration_glj.addToMainTree(data.ration_gljs);
+                }
+                projectObj.mainController.refreshTreeNode(newNodes, false);
+                if(project.Bills.isFBFX(newNodes[0])) { //判断是否属于分部分项工程 ,是的话才需要做计取安装费计算
+                    project.installation_fee.calcInstallationFee(function (isChange,rations) {
+                        if(isChange){
+                            rations = rations.concat(newNodes);
+                            project.calcProgram.calcNodesAndSave(rations);
+                        }else {
+                            project.calcProgram.calcNodesAndSave(newNodes);
+                        }
+                    });
+                }else {
+                    project.calcProgram.calcNodesAndSave(newNodes);
+                }
             }
         };
         ration.prototype.insertVolumePrice = function(type){

+ 10 - 1
web/building_saas/main/js/models/ration_coe.js

@@ -140,7 +140,13 @@ var ration_coe = {
                 delete:[],
                 replace:[]
             };
-            let gljList = project.ration_glj.getGLJListByRationID(record.rationID);
+            let gljList = [];
+            if($('#divide_subSpread').is(':visible')){
+              gljList = divideObj.getGLJListByRationID(record.rationID);
+            }else{
+              gljList = project.ration_glj.getGLJListByRationID(record.rationID);
+            }
+            
             if(gljUtil.isDef(record.option_codes)&&record.option_codes!=""){ //说明编辑的是下拉选择编号的类型
                 //勾选而不是下拉的情况下,默认选择下拉框中的第一个
                 if(newval == 1 && (!gljUtil.isDef(doc.select_code)||doc.select_code == '')){
@@ -193,6 +199,9 @@ var ration_coe = {
                         updateData.replace.push(r_tem);
                     }
                 }
+                if($('#divide_subSpread').is(':visible')){
+                  return divideObj.updateCoeAdjust(updateData);
+                }
                 $.bootstrapLoading.start();
                 CommonAjax.post("/ration/updateCoeAdjust",updateData,function (result) {
                     $.bootstrapLoading.end();

+ 12 - 3
web/building_saas/main/js/views/billsElf.js

@@ -732,11 +732,20 @@ const BillsElf = (function() {
     }
     //插入定额
     //@return {void}
-    function insertRations(addRationDatas){
+    async function insertRations(addRationDatas){
         if(addRationDatas.length > 0){
-            projectObj.project.Ration.addMultiRation(addRationDatas, function () {
+            try {
+                $.bootstrapLoading.start();
+                await projectObj.project.Ration.addMultiRation(addRationDatas);
                 projectObj.setActiveCell('quantity', true);
-            });
+            } catch (err) {
+                console.log(err);
+                if (!$('hintBox_form').is(':visible')) {
+                    alert(err);
+                }
+            } finally {
+                $.bootstrapLoading.end();
+            }
         }
     }
     //各监听事件

+ 513 - 89
web/building_saas/main/js/views/divide_view.js

@@ -3,13 +3,13 @@ let divideObj = {
     header: [
       {headerName: "类别", headerWidth: 60, dataCode: "itemType", hAlign:'center', dataType: "String"},
       {headerName: "清单编号", headerWidth: 130, dataCode: "code", hAlign:'left', dataType: "String", formatter: "@"},
-      {headerName: "名称", headerWidth: 160, dataCode: "name", dataType: "String",formatter: "@"},
-      {headerName: "单位", headerWidth: 100, dataCode: "unit", dataType: "String",cellType:'comboBox',editable:true,options:['m', 'm2', 'm3', 'km', 't', 'kg', '台班', '工日', '昼夜', '元', '项', '处', '个', '件',
+      {headerName: "名称", headerWidth: 250, dataCode: "name", dataType: "String",formatter: "@"},
+      {headerName: "单位", headerWidth: 50, dataCode: "unit", dataType: "String",cellType:'comboBox',editable:true,options:['m', 'm2', 'm3', 'km', 't', 'kg', '台班', '工日', '昼夜', '元', '项', '处', '个', '件',
       '根', '组', '系统', '台', '套', '株', '丛', '缸', '支', '只', '块', '座', '对', '份', '樘', '攒', '榀']},
       {headerName: "工程量", headerWidth: 80, dataCode: "quantity", dataType: "Number",validator:"number",getText:"notZero"},
-      {headerName: "单价", headerWidth: 100, dataCode: "marketPrice", dataType: "Number",validator:"number",getText:"notZero"},
-      {headerName: "金额", headerWidth: 100, dataCode: "marketTotalFee", dataType: "Number",validator:"number",getText:"notZero"},
-      {headerName: "预算价", headerWidth: 100, dataCode: "marketUnitFee", dataType: "Number",validator:"number",getText:"notZero"},
+      {headerName: "单价", headerWidth: 80, dataCode: "marketPrice", dataType: "Number",validator:"number",getText:"notZero"},
+      {headerName: "金额", headerWidth: 80, dataCode: "marketTotalFee", dataType: "Number",validator:"number",getText:"notZero"},
+      {headerName: "预算价", headerWidth: 80, dataCode: "marketUnitFee", dataType: "Number",validator:"number",getText:"notZero"},
       {headerName: "取费类别", headerWidth: 60, dataCode: "programID", hAlign:'center', dataType: "String",cellType:'comboBox',editorValueType:true}
     ],
     view:{ 
@@ -35,7 +35,7 @@ let divideObj = {
       {headerName: "不可分摊", headerWidth: 80, dataCode: "unDivide", dataType: "Number",cellType:'checkBox'}
     ],
     view:{ 
-      lockColumns: [0,1,2,3,4,5,6],
+      lockColumns: [0,1,2,3,4,5,6,7],
       colHeaderHeight:30
     },
     treeCol:1
@@ -47,19 +47,19 @@ let divideObj = {
       {headerName: "分摊比例", headerWidth: 100, dataCode: "divideRate", dataType: "Number",validator:"number"}
     ],
     view:{ 
-      lockColumns: [0,1,2],
+      lockColumns: [0,1],
       colHeaderHeight:30
     }
   },
   billsSetting:{
     header: [
-      {headerName: "编号", headerWidth: 130, dataCode: "code", hAlign:'left', dataType: "String"},
-      {headerName: "名称", headerWidth: 160, dataCode: "name", dataType: "String"},
-      {headerName: "分摊比例", headerWidth: 80, dataCode: "divideRate", dataType: "Number",validator:"number"},
-      {headerName: "分摊金额", headerWidth: 100, dataCode: "dividePrice", dataType: "Number",validator:"number"},
-      {headerName: "原始金额", headerWidth: 100, dataCode: "originalPrice", dataType: "Number",validator:"number"},
-      {headerName: "合计", headerWidth: 100, dataCode: "totalPrice", dataType: "Number",validator:"number"},
-      {headerName: "金额", headerWidth: 60, dataCode: "price", dataType: "Number",validator:"number"}
+      {headerName: "编号", headerWidth: 100, dataCode: "code", hAlign:'left', dataType: "String"},
+      {headerName: "名称", headerWidth: 250, dataCode: "name", dataType: "String"},
+      {headerName: "分摊比例", headerWidth: 70, dataCode: "divideRate", dataType: "Number",validator:"number"},
+      {headerName: "分摊金额", headerWidth: 80, dataCode: "dividePrice", dataType: "Number",validator:"number"},
+      {headerName: "原始金额", headerWidth: 80, dataCode: "originalPrice", dataType: "Number",validator:"number"},
+      {headerName: "合计", headerWidth: 80, dataCode: "totalPrice", dataType: "Number",validator:"number"},
+      {headerName: "金额", headerWidth: 80, dataCode: "price", dataType: "Number",validator:"number"}
     ],
     view:{ 
       lockColumns: [0,1,2,3,4,5,6],
@@ -82,30 +82,39 @@ let divideObj = {
     }
   },
   initExecSpread:function(){
-    if(this.billsSelectedSpread) return;
+    if(this.billsSelectedSpread){
+      this.billsSelectedSpread.refresh();
+      this.execBillsSpread.refresh();
+      return;
+    } 
     this.billsSelectedSpread = SheetDataHelper.createNewSpread($("#billSelectedSheet")[0]);
     sheetCommonObj.spreadDefaultStyle(this.billsSelectedSpread);
     this.billsSelectedSheet = this.billsSelectedSpread.getSheet(0);
     sheetCommonObj.initSheet(this.billsSelectedSheet, this.billsSelectedSetting);
-    // this.billsSelectedSheet.bind(GC.Spread.Sheets.Events.EditStarting,this.onDivideEditStarting);
-    // this.billsSelectedSheet.bind(GC.Spread.Sheets.Events.ValueChanged,this.onDivideValueChange)
-    // this.billsSelectedSheet.bind(GC.Spread.Sheets.Events.SelectionChanged,this.onDivideSelectionChange);  
+    this.billsSelectedSpread.bind(GC.Spread.Sheets.Events.ButtonClicked, this.onbillsSelected);
+
     this.execBillsSpread = SheetDataHelper.createNewSpread($("#execBillSheet")[0]);
     sheetCommonObj.spreadDefaultStyle(this.execBillsSpread);
     this.execBillsSheet = this.execBillsSpread.getSheet(0);
     sheetCommonObj.initSheet(this.execBillsSheet, this.execBillsSetting);
+    this.execBillsSheet.bind(GC.Spread.Sheets.Events.ValueChanged,this.onExecBillsValueChange);
+    this.execBillsSheet.bind(GC.Spread.Sheets.Events.RangeChanged, this.onExecBillsRangeChanged); 
 
     SheetDataHelper.protectdSheet(this.billsSelectedSheet);
     SheetDataHelper.protectdSheet(this.execBillsSheet);
   },
   showDatas:function(){
     this.showDivideDatas();
+    this.showSubDatas();
+  },
+  showSubDatas:function(){
     this.showBillsDatas();
     this.showRationGLJDatas();
     this.showCalcProgramDatas();
   },
-  showExecDatas:function(){
+  showExecDatas:function(init){
     this.showSelectedBillsDatas();
+    this.showExecBillsDatas(init);
   },
   refreshViews:function(){
     if(!$('#divide_main').is(':visible')) return;
@@ -175,14 +184,76 @@ let divideObj = {
     sheetCommonObj.showData(this.calcProgramSheet, calcProgramObj.setting, datas);
     customRowHeader(this.calcProgramSheet, datas.length);
   },
-  showSelectedBillsDatas:function(){
+  showExecBillsDatas:function(init=false){
+    this.execBillsDatas = [];
+    let parentMap = _.indexBy(this.selectedBillsDatas,"ParentID");
+    let divideType = parseInt($("input[name='divide_type']:checked").val());
+    let total = 0;
+    let ctotal = 0;
+    let concreteMap = {};
+    let billsRationGLJMap = {};
+    if(divideType == 1){//按混凝土计算
+      billsRationGLJMap = _.groupBy(projectObj.project.ration_glj.datas,"billsItemID");
+    }
+    for(let b of this.selectedBillsDatas){
+      if(b.selected == 1 && !parentMap[b.ID]){
+        let e = {ID:b.ID,code:b.code,name:b.name,divideRate:0,totalFee:b.totalFee};
+        if(b.totalFee != "") total = scMathUtil.roundForObj(total + parseFloat(b.totalFee),getDecimal("process"))
+        if(divideType == 1 && billsRationGLJMap[b.ID]){
+          let btotal = 0;
+          for(let glj of billsRationGLJMap[b.ID]){
+            //“混凝土”、“砂浆”、“配合比”。
+            if(glj.type == gljType.CONCRETE || glj.type == gljType.MORTAR||glj.type == gljType.MIX_RATIO){
+              let rNode = projectObj.project.mainTree.findNode(glj.rationID);
+              let gquantity = gljUtil.getTotalQuantity(glj,rNode.data,getDecimal("ration.quantity"),getDecimal("glj.quantity"));
+              btotal = scMathUtil.roundForObj(btotal + parseFloat(gquantity),getDecimal("process"));
+            }
+          }
+          btotal = scMathUtil.roundForObj(btotal,getDecimal("glj.quantity"));
+          ctotal = scMathUtil.roundForObj(ctotal + btotal,getDecimal("process"));
+          concreteMap[b.ID] = btotal;
+        }
+        this.execBillsDatas.push(e);
+      }
+    }
+    total = scMathUtil.roundForObj(total,getDecimal("bills.totalPrice"));
+    for(let e of this.execBillsDatas){
+      if(init == true){//如果是初始化打开页面,要读取已经保存的分摊比例
+         //使用已保存的分摊信息
+         let item = divideObj.getSelectedItem();
+         let IDMap = {};
+         if(item.bills) IDMap = _.indexBy(item.bills,"ID");
+         if(IDMap[e.ID]){
+          e.divideRate = IDMap[e.ID].divideRate;
+         } 
+      }else if(divideType == 0){//按清单金额比例
+        if(e.totalFee != "" && total!=0) {
+          totalFee = scMathUtil.roundForObj(e.totalFee,getDecimal("bills.totalPrice"));
+          e.divideRate = scMathUtil.roundForObj((totalFee/total) * 100,3);
+        }
+      }else if(divideType == 1){//按混凝土用量
+         let cquantity = concreteMap[e.ID];
+         if(cquantity && ctotal != 0) e.divideRate = scMathUtil.roundForObj((cquantity/ctotal) * 100,3);
+      }
+      //自定义的不用管
+    }
     
-    this.selectedBillsDatas = getSelectedBillsDatas();
+    sheetCommonObj.showData(this.execBillsSheet, this.execBillsSetting,this.execBillsDatas);
+    this.execBillsSheet.setRowCount(this.execBillsDatas.length);
+  },
+
+  
+
+  showSelectedBillsDatas:function(datas){
+    if(!datas)this.selectedBillsDatas = getSelectedBillsDatas();
     this.billsSelectedSheet.setRowCount(this.selectedBillsDatas.length);
     sheetCommonObj.showTreeData(this.billsSelectedSheet, this.billsSelectedSetting,this.selectedBillsDatas);
 
     function getSelectedBillsDatas(){
       let datas = [];
+      let item = divideObj.getSelectedItem();
+      let IDMap = {};
+      if(item.bills) IDMap = _.indexBy(item.bills,"ID");
       for(let n of projectObj.project.mainTree.roots){
         getDatas(n)
       }
@@ -203,6 +274,12 @@ let divideObj = {
           unDivide:0,
           collapsed:false
         };
+        if(IDMap[d.ID]) t.selected = 1;
+        //有基数计算的,不可选择
+        if(d.calcBase&&d.calcBase!="") t.unDivide = 1;
+        //数量*单价的(判断金额列是否有值),不可选择
+        if(d.calcFlag == treeNodeCalcFlag.customUnitPrice && t.totalFee !="") t.unDivide = 1;
+
         datas.push(t);
         if(!node.children) return;
         for(let c of node.children){
@@ -288,6 +365,14 @@ let divideObj = {
       return datas;
     }
   },
+  getGLJListByRationID:function(rationID){
+    let divideSetting =  projectObj.project.divide_setting.datas;
+    let datas = [];
+    for(r of divideSetting.ration_gljs){
+      if(r.rationID==rationID ) datas.push(r);
+    }
+    return datas;
+  },
   getRationGLJDatas : function (ration){//这里是给计算程序统一调用的方法,树结构不用这个方法了
     let datas = [];
     let selected = ration;
@@ -334,9 +419,53 @@ let divideObj = {
     }
     return cusList;
   },
+  updateActualValue:async function(assList,index,newVal,isAdjust = 1){
+    let item = this.getSelectedItem();
+    let newList = _.cloneDeep(assList);
+    if(gljUtil.isDef(newVal))newList[index].actualValue=newVal;
+    newList[index].isAdjust = isAdjust;
+    let u = {ID:item.ID,type:'update',updateAss:true,doc:{rationAssList:newList}}
+    await this.updateItem([u]);
+    await this.calcDivideItem(item);
+    zmhs_obj.showDatas();
+  },
   getStableList:function(){
     return this.rationGLJDatas.filter(glj => !glj.isMixRatio && glj.rationProportion);
   },
+  updateProportion:async function(datas){
+    let updateDatas = [];
+    let item = this.getSelectedItem();
+    for(let d of datas){
+      updateDatas.push({ID:d.ID,type:'update',rationID:item.ID,model:'ration_gljs',updateProportion:true,doc:{"adjustProportion":d.adjustProportion}});
+    }
+    await this.updateItem(updateDatas);
+    await this.calcDivideItem(item);
+    zmhs_obj.showDatas();
+  },
+  updateCoeAdjust:async function(data){
+    console.log(data);
+    let divideSetting =  projectObj.project.divide_setting.datas;
+    data.divideID = divideSetting.ID;
+    try {
+      $.bootstrapLoading.start();
+      let result = await ajaxPost("/divide/updateCoeAdjust",data)
+      this.refreshDivideCaches(result.updateDatas);
+      if(result.projectGLJList && result.projectGLJList.length > 0) projectObj.project.projectGLJ.loadNewProjectGLJToCaches(result.projectGLJList);   
+      if(result.ration_gljs && result.ration_gljs.length > 0) divideSetting.ration_gljs = divideSetting.ration_gljs.concat(result.ration_gljs);
+      if(result.delete && result.delete.length > 0){
+        _.remove(result.ration_gljs,function(o){
+          return _.includes(result.delete,o.ID)
+        })
+      }
+    } catch (error) {
+      console.log(error)
+    }
+    $.bootstrapLoading.end();
+    await this.calcDivideItem(this.getSelectedItem());
+    this.showDatas();
+    zmhs_obj.showDatas();
+  },
+
   getProgramOptions:function(){
     let names = projectObj.project.calcProgram.compiledTemplateNames;
     let map = projectObj.project.calcProgram.compiledTemplateMaps;
@@ -366,6 +495,15 @@ let divideObj = {
     }
     return false;  
   },
+
+  rationGLJEditChecking:function(row,col){//return false表示不能编辑
+    let me = this;
+    let data = me.rationGLJDatas[row],setting = gljCol.ration_glj_setting;
+    let dataCode = setting.header[col].dataCode;
+    if(data.isMixRatio == true && (dataCode=="customQuantity"||dataCode=="basePrice")) return false;
+    return true;
+  },
+
   showBillsDatas:function(){
     if(!$('#divide_bills_sheet').is(':visible')) return;
     this.billsDatas = getBillsDatas();
@@ -373,7 +511,36 @@ let divideObj = {
     this.billsSheet.setRowCount(this.billsDatas.length);
 
     function getBillsDatas(){
-      return [];
+      let datas = [];
+      let item = divideObj.getSelectedItem();
+      if(item.bills){
+        for(let b of item.bills){
+          let bNode = projectObj.project.mainTree.findNode(b.ID);
+          if (!bNode) continue;
+          let t = {
+            ID:b.ID,
+            divideRate:b.divideRate,
+            name:bNode.data.name,
+            code:bNode.data.code,
+            dividePrice:0,
+            originalPrice:0,
+            totalPrice:0,
+            price:0
+          }
+          if(bNode.data.feesIndex.common.totalFee){
+            let totalFee = scMathUtil.roundForObj(bNode.data.feesIndex.common.totalFee,getDecimal("bills.totalPrice"));
+            t.price = totalFee;
+            t.divideRate = scMathUtil.roundForObj(t.divideRate * 0.01,3);
+            t.dividePrice = scMathUtil.roundForObj(item.marketTotalFee * t.divideRate,getDecimal("bills.totalPrice"));
+          }
+          if(bNode.data.feesIndex.rationCommon && bNode.data.feesIndex.rationCommon.totalFee){
+            t.originalPrice = scMathUtil.roundForObj(bNode.data.feesIndex.rationCommon.totalFee,getDecimal("bills.totalPrice"));
+          }
+          t.totalPrice = scMathUtil.roundForObj(t.dividePrice + t.originalPrice,getDecimal("bills.totalPrice"));
+          datas.push(t);
+        }
+      }
+      return datas;
     }
   },
   getSelectedItem:function(){
@@ -394,6 +561,7 @@ let divideObj = {
     if(type) newItem.subType = type;
     let selected = this.getSelectedItem();
     if(itemType!="分摊项"){//当插入定额或者量价时:
+      newItem.divideType = 0;
       newItem.ParentID = selected.itemType == "分摊项"?selected.ID:selected.ParentID;
       newItem.fees = [{fieldName:"common",tenderTotalFee:0,unitFee:0,totalFee:0,tenderUnitFee:0}];
     }
@@ -421,6 +589,8 @@ let divideObj = {
       } 
       if(c.ID == bills.ID) item = c;
     }
+    item.fees=[];
+    item.feesIndex={};
     if(subNodes.length > 0) projectObj.project.calcProgram.innerCalcBill({data:item, sourceType:"bills", updateData:[],children:subNodes},3);
     if(subNodes.length == 0){
       item.calcFlag = 2;
@@ -454,7 +624,7 @@ let divideObj = {
     }
     subItems.push({type:"delete",ID:selected.ID,itemType:selected.itemType});
     await this.updateItem(subItems);
-
+    zmhs_obj.showDatas();
   },
   getNewSeqs:function(selected,newItem){
     let seq = 1,datas=[],refreshSeq = false;
@@ -476,8 +646,8 @@ let divideObj = {
     let divideSetting =  projectObj.project.divide_setting.datas
     try {
       $.bootstrapLoading.start();
-      let result = ajaxPost("/divide/updateItem",{ID:divideSetting.ID,updateDatas:datas})
-      this.refreshDivideCaches(datas);
+      let result = await ajaxPost("/divide/updateItem",{ID:divideSetting.ID,updateDatas:datas})
+      this.refreshDivideCaches(result.updateDatas);
     } catch (error) {
       console.log(error)
     }
@@ -498,7 +668,9 @@ let divideObj = {
         } 
       }
       if(d.type == "update"){
-        let item = _.find(divideSetting.divideList,{'ID':d.ID});
+        let model = "divideList";
+        if(d.model) model = d.model;
+        let item = _.find(divideSetting[model],{'ID':d.ID});
         if(item) gljUtil.updateProperty(item,d.doc);
       }
     }
@@ -521,9 +693,8 @@ let divideObj = {
     let sel = this.divideSheet.getSelections()[0];
     await this.updateDivideRation(data);
     this.divideSheet.setSelection(sel.row+1,sel.col,sel.rowCount,sel.colCount);
-    this.showRationGLJDatas();
-    this.showCalcProgramDatas();
-
+    this.showSubDatas();
+    zmhs_obj.showDatas();
   },
   updateDivideRation:async function(data){
     let divideSetting =  projectObj.project.divide_setting.datas;
@@ -555,36 +726,73 @@ let divideObj = {
     }
     $.bootstrapLoading.end();
   },
+  calcDivideAfterGLJUpdate:async function(gljs){
+    let divideSetting =  projectObj.project.divide_setting.datas
+    let indexMap = {};
+    let rationIDMap = {};
+    let rations = [];
+    for(let glj of gljs){
+      let index = gljUtil.getIndex(glj);
+      indexMap[index] = true;
+    }
+    for(let rg of divideSetting.ration_gljs){    
+       if(indexMap[gljUtil.getIndex(rg)]){
+         rationIDMap[rg.rationID] = true;
+       }
+    }
+    if(_.isEmpty(rationIDMap)) return;
+    for(let d of divideSetting.divideList){
+      if(rationIDMap[d.ID]){
+        rations.push(d)
+      }
+    }
+    if(rations.length > 0){
+      let updateDatas= this.getCalcDivideDatas(rations);
+      updateDatas.length > 0?await this.updateItem(updateDatas):this.showDatas();
+    }
+  },
+
   calcDivideItem:async function(divide){
+    let updateDatas= this.getCalcDivideDatas([divide]);
+    updateDatas.length > 0?await this.updateItem(updateDatas):this.showDatas();
+  },
+  getCalcDivideDatas:function(divides){
     let updateDatas = [];
-    let children =[];
     let divideSetting =  projectObj.project.divide_setting.datas
-    if(divide.itemType != "分摊项"){//不是分摊项,调用计算程序
-      let parentSum = 0;
-      let parent = null;
-      projectObj.project.calcProgram.innerCalcRation({data:divide, sourceType:"ration", calcType: 1, updateData:[]});
-      if(divide.itemType == "定额"){
-        updateDatas.push({ID:divide.ID,type:'update',doc:{fees:divide.fees}});  
+    let parentMap={};
+    let parents=[];
+    for(let divide of divides){//先计算所有子项和传入的分摊项(与传入子项不关联,因为计算子项时会计算对应的分摊项)
+      if(divide.itemType != "分摊项"){//不是分摊项,调用计算程序
+        divide.fees=[];
+        divide.feesIndex={};
+        projectObj.project.calcProgram.innerCalcRation({data:divide, sourceType:"ration", calcType: 1, updateData:[]});
+        if(divide.itemType == "定额"){
+          updateDatas.push({ID:divide.ID,type:'update',doc:{fees:divide.fees}});  
+        }else{
+          updateDatas.push({ID:divide.ID,type:'update',doc:{fees:divide.fees,marketUnitFee:divide.marketUnitFee}});
+        } 
+        parentMap[divide.ParentID] = [];
       }else{
-        updateDatas.push({ID:divide.ID,type:'update',doc:{fees:divide.fees,marketUnitFee:divide.marketUnitFee}});
-      } 
-      for(let c of divideSetting.divideList){
-        if(c.ID == divide.ParentID) parent = c;
-        if(c.ParentID == divide.ParentID){
-          let t = c.ID != divide.ID?c:divide;
-          this.initFeeIndexs(t);
-          children.push({data:t, sourceType:"ration", calcType: 1, updateData:[]});
-        }
+        divide = this.calcBills(divide);
+        updateDatas.push({ID:divide.ID,type:'update',doc:{fees:divide.fees}});  
       }
-      if(children.length > 0){
-        projectObj.project.calcProgram.innerCalcBill({data:parent, sourceType:"bills", updateData:[],children:children},3);
-        updateDatas.push({ID:parent.ID,type:'update',doc:{fees:parent.fees}});  
-      } 
-    }else{
-      divide = this.calcBills(divide);
-      updateDatas.push({ID:divide.ID,type:'update',doc:{fees:divide.fees}});  
     }
-    updateDatas.length > 0?await this.updateItem(updateDatas):this.showDatas();
+    for(let c of divideSetting.divideList){
+      if(parentMap[c.ID]) parents.push(c);//把所有父节点挑出来
+      if(parentMap[c.ParentID]){
+        let t = c.ID != divide.ID?c:divide;
+        this.initFeeIndexs(t);
+        //生成并归类父节点对应的子节点(用于父节点的计算)
+        parentMap[c.ParentID].push({data:t, sourceType:"ration", calcType: 1, updateData:[]});
+      }
+    }
+    for(let p of parents){
+      p.fees=[];
+      p.feesIndex={};
+      projectObj.project.calcProgram.innerCalcBill({data:p, sourceType:"bills", updateData:[],children:parentMap[p.ID]},3);
+      updateDatas.push({ID:p.ID,type:'update',doc:{fees:p.fees}});  
+    }  
+    return updateDatas;
   },
 
   getTotolFee:function name(divide) {
@@ -606,10 +814,9 @@ let divideObj = {
     this.divideSheet = this.divideSpread.getSheet(0);
     sheetCommonObj.initSheet(this.divideSheet, this.divideSetting, 2);
     this.divideSheet.bind(GC.Spread.Sheets.Events.EditStarting,this.onDivideEditStarting);
-    this.divideSheet.bind(GC.Spread.Sheets.Events.ValueChanged,this.onDivideValueChange)
+    this.divideSheet.bind(GC.Spread.Sheets.Events.ValueChanged,this.onDivideValueChange);
     this.divideSheet.bind(GC.Spread.Sheets.Events.SelectionChanged,this.onDivideSelectionChange);  
-    /*     ;
-    this.divideSheet.bind(GC.Spread.Sheets.Events.RangeChanged, this.onCoeRangeChanged); */
+    this.divideSheet.bind(GC.Spread.Sheets.Events.RangeChanged, this.onDivideRangeChanged); 
 
     SheetDataHelper.protectdSheet(this.divideSheet);
   },
@@ -621,6 +828,54 @@ let divideObj = {
         args.cancel = true;
     }
   },
+  onRationGljEditStarting:function(sender,args){
+    let me = divideObj;
+    let row = args.row;
+    let col = args.col;
+    if(divideObj.rationGLJEditChecking(row,col)==false){
+      args.cancel = true;
+    }
+  },
+  onRationGLJRangeChanged:async function(e,args){
+    for(let c of args.changedCells){
+      let value=  args.sheet.getCell(c.row, c.col).text();
+      if(divideObj.rationGLJEditChecking(c.row,c.col)==false){
+        return divideObj.showRationGLJDatas();
+      }
+      divideObj.onRationGLJValueChange(e,{row:c.row,col:c.col,newValue:value,sheet:args.sheet});
+    }
+  },
+
+  onExecBillsRangeChanged:async function(e,args){
+    for(let c of args.changedCells){
+      let value=  args.sheet.getCell(c.row, c.col).text();
+      divideObj.onExecBillsValueChange(e,{row:c.row,col:c.col,newValue:value,sheet:args.sheet});
+    }
+  },
+
+  onExecBillsValueChange:function(sender,info){
+    let me = divideObj,row = info.row, col = info.col;
+    let setting = me.execBillsSetting;
+    let dataCode = setting.header[col].dataCode;
+    let record = me.execBillsDatas[row];
+    let value = info.newValue;
+    if(value && !sheetCommonObj.checkData(col,setting,value)) {
+      alert('输入的数据类型不对,请重新输入!');
+      return info.sheet.setValue(row, col, record[dataCode], GC.Spread.Sheets.SheetArea.viewport);
+    }
+    record[dataCode] = scMathUtil.roundForObj(value,3);
+
+  },
+  onDivideRangeChanged:async function(e,args){
+    for(let c of args.changedCells){
+      if(divideObj.divideEditChecking(c.row,c.col)==false){
+        return divideObj.showDivideDatas();
+      }
+      let value=  args.sheet.getCell(c.row, c.col).text();
+      divideObj.onDivideValueChange(e,{row:c.row,col:c.col,newValue:value});
+    }
+  },
+
   onDivideValueChange:async function(sender,info){
     let me = divideObj,row = info.row, col = info.col;
     let setting = me.divideSetting;
@@ -641,6 +896,48 @@ let divideObj = {
     await me.updateItem([me.getDivideUpdateData(record,dataCode,value)]);
     await me.calcDivideItem(record);
   },
+
+  onRationGLJValueChange:async function(sender,info){
+    let me = divideObj,row = info.row, col = info.col;
+    let setting = gljCol.ration_glj_setting;
+    let dataCode = setting.header[col].dataCode;
+    let record = me.rationGLJDatas[row];
+    let value = info.newValue;
+    if(value && !sheetCommonObj.checkData(col,setting,value)) {
+      alert('输入的数据类型不对,请重新输入!');
+      return me.showRationGLJDatas();
+    }
+    if (dataCode == 'marketPrice' || dataCode == 'basePrice') {
+      if(!value) value = 0;
+      projectObj.project.projectGLJ.updatePriceFromRG(record, dataCode, value);
+    }else if(dataCode=='customQuantity'){
+      if(value) value = scMathUtil.roundForObj(value, getDecimal("glj.quantity"))
+      let u= {ID:record.ID,type:'update',rationID:record.rationID,model:'ration_gljs',doc:{"customQuantity":value}}
+      await me.updateItem([u]);
+      await me.calcDivideItem(me.getSelectedItem());
+
+    }
+  },
+
+  onbillsSelected:function(sender, args){
+    let dataCode = divideObj.billsSelectedSetting.header[args.col].dataCode;
+    if(dataCode != "selected") return;
+    let checkboxValue = args.sheet.getCell(args.row, args.col).value();
+    let newval = checkboxValue? 0:1;
+    let data = divideObj.selectedBillsDatas[args.row];
+    let parentMap = {};
+    parentMap[data.ID]=true;
+    data.selected=newval;
+    for(let d of divideObj.selectedBillsDatas){
+      if(parentMap[d.ParentID]){
+        if(d.unDivide == 0) d.selected=newval;
+        parentMap[d.ID] = true;
+      }
+    }
+    divideObj.showSelectedBillsDatas(divideObj.selectedBillsDatas);
+    divideObj.showExecBillsDatas();
+  },
+
   replaceDivideRation:async function(code,record){
     let divideSetting =  projectObj.project.divide_setting.datas;
     let data = {ID:divideSetting.ID,type:"update",rationID:record.ID,code:code};
@@ -655,7 +952,7 @@ let divideObj = {
     data.projectID = projectObj.project.ID();
     data.oldData = record;
     await this.updateDivideRation(data);
-
+    zmhs_obj.showDatas();
   },
 
 
@@ -665,8 +962,8 @@ let divideObj = {
     let oldSel = args.oldSelections?args.oldSelections[0]:{};
     if(newSel.row != oldSel.row){
       me.initNavItem();
-      me.showRationGLJDatas();
-      me.showCalcProgramDatas();
+      me.showSubDatas();
+      zmhs_obj.showDatas();
     }
     args.sheet.repaint();
   },
@@ -685,11 +982,14 @@ let divideObj = {
       if(dataCode == "marketPrice"){
         value = scMathUtil.roundForObj(value,getDecimal("bills.unitPrice"));
         let commonFee = _.find(recode.fees,{"fieldName":"common"})
-        if (commonFee){
-          //let tu = scMathUtil.roundForObj(commonFee.unitFee&&commonFee.unitFee!=""?commonFee.unitFee:0,getDecimal("bills.unitPrice"))
-          commonFee.unitFee = scMathUtil.roundForObj(value,getDecimal("bills.unitPrice"));
-          doc["fees"] = recode.fees;
-        }
+        if(!commonFee){
+          commonFee = {fieldName: "common",tenderTotalFee: 0,tenderUnitFee: 0,totalFee: 0,unitFee: 0};
+          recode.fees?recode.fees.push(commonFee):recode.fees=[commonFee];
+        } 
+        //let tu = scMathUtil.roundForObj(commonFee.unitFee&&commonFee.unitFee!=""?commonFee.unitFee:0,getDecimal("bills.unitPrice"))
+        commonFee.unitFee = scMathUtil.roundForObj(value,getDecimal("bills.unitPrice"));
+        doc["fees"] = recode.fees;
+
         return {ID:recode.ID,type:'update',doc:doc};
       }
       
@@ -720,6 +1020,10 @@ let divideObj = {
     sheetCommonObj.spreadDefaultStyle(this.divideSubSpread);
     this.divideSubSheet = this.divideSubSpread.getSheet(0);
     sheetCommonObj.initSheet(this.divideSubSheet, gljCol.ration_glj_setting, 0);
+    this.divideSubSheet.bind(GC.Spread.Sheets.Events.EditStarting,this.onRationGljEditStarting);
+    this.divideSubSheet.bind(GC.Spread.Sheets.Events.ValueChanged,this.onRationGLJValueChange);
+    this.divideSubSheet.bind(GC.Spread.Sheets.Events.RangeChanged, this.onRationGLJRangeChanged); 
+
     SheetDataHelper.protectdSheet(this.divideSubSheet);
     this.calcProgramSheet = this.divideSubSpread.getSheet(1);
     calcProgramObj.initSheet(this.calcProgramSheet);
@@ -857,29 +1161,6 @@ let divideObj = {
                   }
                 }
             },
-            "calcRation": {
-                name: "计算定额/量价",
-                icon: 'fa-sign-in',
-                disabled: function () {
-                  return me.rightClickTarget.row === undefined
-                },
-                callback: function (key, opt) {  
-                  let selected = me.getSelectedItem();
-                  projectObj.project.calcProgram.innerCalcRation({data:selected, sourceType:"ration", calcType: 1, updateData:[]});
-                  console.log(selected)
-                }
-            },
-            "calcBills": {
-                name: "计算分摊项",
-                icon: 'fa-sign-in',
-                disabled: function () {
-                  return me.rightClickTarget.row === undefined
-                },
-                callback: function (key, opt) {  
-                  let selected = me.getSelectedItem();
-                  console.log(me.calcBills(selected));
-                }
-            },
             "removeItem": {
                 name: "删除",
                 icon: 'fa-trash-o',
@@ -914,6 +1195,98 @@ let divideObj = {
         totalHeight: `$(window).height()-$('.header').height()-$('#divideToolsBar').height()-5`
     };
     return divideResizeEles;
+  },
+  calcDivideNodes : async function(item){
+    if(!item.marketTotalFee) return;
+    let changeNode=[];
+    let datas = [];
+    let usedIDMap={};
+    for(let b of item.bills){
+      let bNode = projectObj.project.mainTree.findNode(b.ID);
+      if(!bNode) continue;
+      let divideRate = scMathUtil.roundForObj(b.divideRate * 0.01,3);
+      let marketUnitFee = item.marketPrice?scMathUtil.roundForObj(item.marketPrice,getDecimal("bills.unitPrice")):0;
+      let quantity = item.quantity?scMathUtil.roundForObj(item.quantity,3):0;
+      quantity = scMathUtil.roundForObj(quantity*divideRate,getDecimal("ration.quantity"))//量价数量为分摊项的数量*分摊比例
+      if(quantity > 0 && marketUnitFee > 0){
+        let serialNo = 0;
+        let preID = "";
+        let divdeNode = null;
+        for(let cNode of bNode.children ){
+          if(cNode.data.divideID == item.ID){//说明已经存在了自动生成的量价,更新一下数据就行
+            let doc = {};
+            usedIDMap[cNode.data.ID] = true;
+            divdeNode = cNode;
+            if(cNode.data.name != item.name) doc.name = item.name;
+            if(cNode.data.unit != item.unit) doc.unit = item.unit;
+            if(cNode.data.quantity != quantity) doc.quantity = quantity;
+            if(cNode.data.marketUnitFee != marketUnitFee) doc.marketUnitFee = marketUnitFee;
+            if(!_.isEmpty(doc)){
+              doc.ID = cNode.data.ID;
+              datas.push({type:ModuleNames.ration,data:doc})
+              break;
+            }
+          }
+          preID = cNode.data.ID;
+          serialNo = cNode.data.serialNo;
+        }
+        if(divdeNode == null){//说明需要新增
+          let newID = uuid.v1() 
+          let newData = projectObj.project.Ration.getTempRationData(newID, b.ID, serialNo+1, rationType.volumePrice);
+          newData.name = item.name;
+          newData.unit = item.unit;
+          newData.quantity = quantity; 
+          newData.marketUnitFee = marketUnitFee;
+          newData.divideID = item.ID;
+          datas.push({type:ModuleNames.ration,data:newData,preSiblingID:preID,action:"add",parentID:b.ID});
+        }
+      }
+    }
+    //删除之前生成的,但是这次执行时没有用的节点
+    for(let r of projectObj.project.Ration.datas){
+       if(r.divideID == item.ID){
+         if(!usedIDMap[r.ID]){//没有用的删除
+          datas.push({type:ModuleNames.ration,data:{ID:r.ID},action:"delete"});
+          let pNode = projectObj.project.mainTree.findNode(r.billsItemID);
+          if(pNode)changeNode.push(pNode);
+         }
+       }
+    }
+
+
+    if(datas.length == 0) return;
+    //刷新缓存和树节点的插入删除
+    let nodes = await projectObj.project.syncUpdateNodesAndRefresh(datas);
+    //重新计算
+    cbTools.refreshFormulaNodes();
+    projectObj.project.calcProgram.calcNodesAndSave(changeNode.concat(nodes));
+  },
+  cancelDivide:async function(divideID){//如果不传ID,相当于取消所有
+     let changeNode=[],datas=[],itemDatas=[];
+     let divideSetting =  projectObj.project.divide_setting.datas
+     for(let d of divideSetting.divideList){
+        if(d.itemType == "分摊项"){
+          if(divideID && d.ID != divideID) continue;
+          if(d.bills && d.bills.length > 0){
+            itemDatas.push({ID:d.ID,type:'update',doc:{bills:[],divideType:0}});
+          }
+       }
+      }
+      if(itemDatas.length > 0) await this.updateItem(itemDatas);
+
+     for(let r of projectObj.project.Ration.datas){
+      if(!r.divideID || r.divideID=="") continue;
+      if(divideID && r.divideID != divideID)  continue;
+      datas.push({type:ModuleNames.ration,data:{ID:r.ID},action:"delete"});
+      let pNode = projectObj.project.mainTree.findNode(r.billsItemID);
+      if(pNode)changeNode.push(pNode);
+    }
+    if(datas.length == 0) return;
+  //刷新缓存和树节点的插入删除
+    let nodes = await projectObj.project.syncUpdateNodesAndRefresh(datas);
+    //重新计算
+    cbTools.refreshFormulaNodes();
+    projectObj.project.calcProgram.calcNodesAndSave(changeNode.concat(nodes));
   }
 }
 
@@ -974,7 +1347,7 @@ dividRationLibResizeEles.limit = {
     totalHeight: `$(window).height()-$('.header').height()-$('.toolsbar').height()-$('#divide_deToolsBar').height()-5`
 };
 
-function showDivideSide(tabPanel, show, id){
+function getDivideSideResizeEles(){
   let divideSideResizeEles = {};
   divideSideResizeEles.eleObj = {
       module: 'divideRationLibTab',
@@ -987,7 +1360,21 @@ function showDivideSide(tabPanel, show, id){
       min: 150,
       max: `$('#divideRow').width()-150`
   };
+  return divideSideResizeEles;
+}
 
+let dSideResizeEles =  getDivideSideResizeEles();
+//水平拖动
+SlideResize.horizontalSlide(dSideResizeEles.eleObj, dSideResizeEles.limit, function(){
+  SlideResize.loadVerticalHeight(dividRationLibResizeEles.eleObj.module, dividRationLibResizeEles.eleObj, dividRationLibResizeEles.limit, function () {
+    if(rationLibObj.divide_rationChapterSpread) rationLibObj.divide_rationChapterSpread.refresh();
+    if(rationLibObj.divide_sectionRationsSpread) rationLibObj.divide_sectionRationsSpread.refresh();
+  });
+  divideObj.refreshViews();  
+});
+
+function showDivideSide(tabPanel, show, id){
+  let divideSideResizeEles = getDivideSideResizeEles();
   divideSideResizeEles.eleObj.module = id;
   if (show) {
       //刚打开各库时的默认比例
@@ -1069,9 +1456,22 @@ $('#divide_zmhs_toogle').bind('click',function (){
 
 $('#exec_divide_btn').bind('click',function (){
    if(projectReadOnly) return;
+   let selected = divideObj.getSelectedItem();
+   if(selected.itemType !="分摊项") return;
   $("#exec_divide_div").modal('show');
 });
 
+$('#cancel_divide_btn').bind('click',function (){
+  if(projectReadOnly) return;
+  let selected = divideObj.getSelectedItem();
+  if(selected.itemType !="分摊项") return;
+  divideObj.cancelDivide(selected.ID);
+});
+
+$('#cancel_all_divide_btn').bind('click',function (){
+  if(projectReadOnly) return;
+  divideObj.cancelDivide();
+});
 
 $('#divideBottom ul li a').on('shown.bs.tab',function () {
   divideObj.showRationGLJDatas();
@@ -1080,5 +1480,29 @@ $('#divideBottom ul li a').on('shown.bs.tab',function () {
 
 $('#exec_divide_div').on('shown.bs.modal',function () {
   divideObj.initExecSpread();
-  divideObj.showExecDatas();
+  divideObj.showExecDatas(true);
+  let selected = divideObj.getSelectedItem();
+  let divideType = selected.divideType?selected.divideType:0;
+  for(let radio of $(".divide_type")){
+    $(radio).prop('checked', divideType == $(radio).val());
+  }
+  divideObj.selectedRadio=divideType;
+});
+
+$("#divide_confirm").bind('click',async function (){
+  let divideType = parseInt($("input[name='divide_type']:checked").val());
+  let item = divideObj.getSelectedItem();
+  let updateData = {ID:item.ID,type:'update',doc:{bills:divideObj.execBillsDatas,divideType:divideType}}
+  await divideObj.updateItem([updateData]);
+  await divideObj.calcDivideNodes(item);
+});
+
+
+$("input[name='divide_type']").each(function(){
+  $(this).click(function(){
+      if(divideObj.selectedRadio == $(this).val()) return;
+      if($(this).val() == 2)  return;
+      divideObj.selectedRadio = $(this).val();
+      divideObj.showExecBillsDatas();
+  });
 });

+ 3 - 3
web/building_saas/main/js/views/glj_col.js

@@ -8,9 +8,9 @@ let gljCol = {
             {headerName: "名称", headerWidth: 160, dataCode: "name", dataType: "String",cellType:'tipsCell'},
             {headerName: "规格", headerWidth: 120, dataCode: "specs", dataType: "String", hAlign: "left",cellType:'tipsCell'},
             {headerName: "单位", headerWidth: 45, dataCode: "unit", dataType: "String", hAlign: "center"},
-            {headerName: "预算价", headerWidth: 65, dataCode: "marketPrice", dataType: "Number", hAlign: "right"},//, decimalField: "glj.unitPrice"
-            {headerName: "调整价", headerWidth: 65, dataCode: "adjustPrice", dataType: "Number", hAlign: "right"},//, decimalField: "glj.unitPrice"1
-            {headerName: "自定消耗", headerWidth: 65, dataCode: "customQuantity", dataType: "Number", hAlign: "right", decimalField: "glj.quantity"},
+            {headerName: "预算价", headerWidth: 65, dataCode: "marketPrice", dataType: "Number", validator:"number",hAlign: "right"},//, decimalField: "glj.unitPrice"
+            {headerName: "调整价", headerWidth: 65, dataCode: "adjustPrice", dataType: "Number",validator:"number", hAlign: "right"},//, decimalField: "glj.unitPrice"1
+            {headerName: "自定消耗", headerWidth: 65, dataCode: "customQuantity", dataType: "Number",validator:"number", hAlign: "right", decimalField: "glj.quantity"},
             {headerName: "消耗量", headerWidth: 65, dataCode: "quantity", dataType: "Number", hAlign: "right", decimalField: "glj.quantity"},
             {headerName: "定额价", headerWidth: 65, dataCode: "basePrice", dataType: "Number", hAlign: "right"},//, decimalField: "glj.unitPrice"
             {headerName: "定额消耗", headerWidth: 65, dataCode: "rationItemQuantity", dataType: "Number", hAlign: "right", decimalField: "glj.quantity"},   // dataType: "Number", formatter: "0.00"

+ 171 - 134
web/building_saas/main/js/views/project_view.js

@@ -1165,11 +1165,68 @@ var projectObj = {
       } 
       return true
     },
-
+    // 注册自定义插入清单数量
+    registerFlexibleInsertBillMenu: function (name) {
+        const project = projectObj.project;
+        const insertBillsHtml = `<span>${name}&nbsp;&nbsp;<input id='insert-bills-number' class="menu-input" type="text" value="1" onfocus="this.select()">&nbsp;&nbsp;行</span>`;
+        return sheetCommonObj.registerInputContextMenuItem('insertBills', insertBillsHtml, 'fa-sign-in', async function () {
+            if (project.mainTree.selected.data.type == billType.DXFY) {
+                if (project.mainTree.selected.data.calcBase && project.mainTree.selected.data.calcBase != "") {
+                    alert("当前有基数计算,不能插入子项。");
+                    return;
+                }
+            }
+            try {
+                const number = +$('#insert-bills-number').val();
+                if (!number) {
+                    return;
+                }
+                $.bootstrapLoading.start();
+                const postData = ProjectController.getBillsPostData(number);
+                const newNodes = await ProjectController.addBillsByData(postData, true);
+                projectObj.mainController.setTreeSelected(newNodes[0]);
+                projectObj.selectColAndFocus(project.mainTree.selected);
+            } catch (err) {
+                console.log(err);
+                if (!$('hintBox_form').is(':visible')) {
+                    alert(err);
+                }
+            } finally {
+                $.bootstrapLoading.end();
+            }
+        });
+    },
+    // 注册自定义插入定额数量
+    registerFlexibleInsertRatoinMenu: function () {
+        const project = projectObj.project;
+        const insertRationHtml = `<span>插入定额&nbsp;&nbsp;<input id='insert-ration-number' class="menu-input" type="text" value="1" onfocus="this.select()">&nbsp;&nbsp;行</span>`;
+        return sheetCommonObj.registerInputContextMenuItem('insertRation', insertRationHtml, 'fa-sign-in', async function () {
+            try {
+                const number = +$('#insert-ration-number').val();
+                if (!number) {
+                    return;
+                }
+                $.bootstrapLoading.start();
+                const newData = [];
+                for (let i = 0; i < number; i++) {
+                    newData.push({ itemQuery: null, rationType: rationType.ration });
+                }
+                await project.Ration.addMultiRation(newData);
+                projectObj.setActiveCell('quantity', true);
+            } catch (err) {
+                console.log(err);
+                if (!$('hintBox_form').is(':visible')) {
+                    alert(err);
+                }
+            } finally {
+                $.bootstrapLoading.end();
+            }
+        });
+    },
     loadMainSpreadContextMenu: function () {
         var project = this.project, spread = this.mainSpread, controller = this.mainController;
         let insertBillsName = project.projectInfo.property && project.projectInfo.property.valuationType == commonConstants.ValuationType.BUDGET?"插入项目节":"插入清单";//右键“插入清单”改文字为“插入项目节”,工程量清单中保持不变。
-            $.contextMenu({
+        $.contextMenu({
             selector: '#billsSpread',
             selectableSubMenu: true,
             build: function ($trigger, e) {
@@ -1189,8 +1246,8 @@ var projectObj = {
                     callback: function (key, opt) {
                         ProjectController.addRootBill(project, controller);
                     },
-                    visible: function(key, opt){
-                        return project.mainTree.selected&&project.mainTree.selected.parent==null;
+                    visible: function (key, opt) {
+                        return project.mainTree.selected && project.mainTree.selected.parent == null;
                     }
                 },
                 "insertFB": {
@@ -1201,14 +1258,14 @@ var projectObj = {
                             return true;
                         }
                         let selected = project.mainTree.selected;
-                        if(projectObj.project.isBillsLocked()== false&&selected&&selected.sourceType==project.Bills.getSourceType()){
-                            if(selected.data.type==billType.FB){
+                        if (projectObj.project.isBillsLocked() == false && selected && selected.sourceType == project.Bills.getSourceType()) {
+                            if (selected.data.type == billType.FB) {
                                 return false;
                             }
-                            if(isFlag(selected.data)&&selected.data.flagsIndex.fixed.flag==fixedFlag.SUB_ENGINERRING){//焦点行是分部分项工程
-                                if(selected.children.length>0){
-                                   return selected.children[0].data.type==billType.FX ||selected.children[0].data.type==billType.BX;//焦点行是分部分项工程,且子项是分项或补项
-                                }else {
+                            if (isFlag(selected.data) && selected.data.flagsIndex.fixed.flag == fixedFlag.SUB_ENGINERRING) {//焦点行是分部分项工程
+                                if (selected.children.length > 0) {
+                                    return selected.children[0].data.type == billType.FX || selected.children[0].data.type == billType.BX;//焦点行是分部分项工程,且子项是分项或补项
+                                } else {
                                     return false
                                 }
                             }
@@ -1219,10 +1276,10 @@ var projectObj = {
                         ProjectController.addFB(project, controller);
                         projectObj.selectColAndFocus(project.mainTree.selected);
                     },
-                    visible: function(key, opt){
-                        if(project.mainTree.selected){
-                            return project.Bills.isFBFX(project.mainTree.selected );//不属于分部分项的话隐藏
-                        }else {
+                    visible: function (key, opt) {
+                        if (project.mainTree.selected) {
+                            return project.Bills.isFBFX(project.mainTree.selected);//不属于分部分项的话隐藏
+                        } else {
                             return false;
                         }
                     }
@@ -1235,21 +1292,21 @@ var projectObj = {
                             return true;
                         }
                         let selected = project.mainTree.selected;
-                        if(projectObj.project.isBillsLocked()== false&& selected&&selected.sourceType==project.Bills.getSourceType()){
-                            if(selected.data.type==billType.FX||selected.data.type==billType.BX){//焦点行是分项,有效显示
+                        if (projectObj.project.isBillsLocked() == false && selected && selected.sourceType == project.Bills.getSourceType()) {
+                            if (selected.data.type == billType.FX || selected.data.type == billType.BX) {//焦点行是分项,有效显示
                                 return false
                             }
-                            if(selected.data.type==billType.FB){//点行是分部,
-                                if(selected.children.length>0){//且有子项,子项是分部,灰显。
+                            if (selected.data.type == billType.FB) {//点行是分部,
+                                if (selected.children.length > 0) {//且有子项,子项是分部,灰显。
                                     return selected.children[0].data.type == billType.FB
-                                }else {
+                                } else {
                                     return false;
                                 }
                             }
-                            if(isFlag(selected.data)&&selected.data.flagsIndex.fixed.flag==fixedFlag.SUB_ENGINERRING){//焦点行是分部分项工程
-                                if(selected.children.length>0){
-                                    return selected.children[0].data.type==billType.FB;//焦点行是分部分项工程,且子项是分部时灰显
-                                }else {
+                            if (isFlag(selected.data) && selected.data.flagsIndex.fixed.flag == fixedFlag.SUB_ENGINERRING) {//焦点行是分部分项工程
+                                if (selected.children.length > 0) {
+                                    return selected.children[0].data.type == billType.FB;//焦点行是分部分项工程,且子项是分部时灰显
+                                } else {
                                     return false
                                 }
                             }
@@ -1260,49 +1317,37 @@ var projectObj = {
                         ProjectController.addFX(project, controller);
                         projectObj.selectColAndFocus(project.mainTree.selected);
                     },
-                    visible: function(key, opt){
-                        if(project.mainTree.selected){
-                            return project.Bills.isFBFX(project.mainTree.selected );//不属于分部分项的话隐藏
-                        }else {
+                    visible: function (key, opt) {
+                        if (project.mainTree.selected) {
+                            return project.Bills.isFBFX(project.mainTree.selected);//不属于分部分项的话隐藏
+                        } else {
                             return false;
                         }
                     }
                 },
                 "insertBills": {
-                    name: insertBillsName,
-                    icon: 'fa-sign-in',
+                    type: projectObj.registerFlexibleInsertBillMenu(insertBillsName),
                     disabled: function () {
                         if (projectReadOnly) {
                             return true;
                         }
                         let selected = project.mainTree.selected;
-                        if(!(projectObj.project.isBillsLocked()== true && project.withinBillsLocked(selected)) && selected && selected.sourceType === project.Bills.getSourceType()){
+                        if (!(projectObj.project.isBillsLocked() == true && project.withinBillsLocked(selected)) && selected && selected.sourceType === project.Bills.getSourceType()) {
                             return false
                         }
                         return true;
                     },
-                    callback: function (key, opt) {
-                        if(project.mainTree.selected.data.type == billType.DXFY){
-                            if(project.mainTree.selected.data.calcBase&&project.mainTree.selected.data.calcBase!=""){
-                                alert("当前有基数计算,不能插入子项。");
-                                return;
-                            }
+                    visible: function (key, opt) {
+                        if (project.mainTree.selected) {
+                            return project.Bills.isFBFX(project.mainTree.selected) == true ? false : true;
+                        } else {
+                            return false;
                         }
-                        ProjectController.addBills(project, controller);
-                        projectObj.selectColAndFocus(project.mainTree.selected);
-                    },
-                     visible: function(key, opt){
-                         if(project.mainTree.selected){
-                             return  project.Bills.isFBFX(project.mainTree.selected)==true?false:true;
-                         }else {
-                             return false;
-                         }
-                     }
+                    }
                 },
                 "spr1": '--------',
                 "insertRation": {
-                    name: "插入定额",
-                    icon: 'fa-sign-in',
+                    type: projectObj.registerFlexibleInsertRatoinMenu(),
                     disabled: function () {
                         if (projectReadOnly) {
                             return true;
@@ -1312,37 +1357,29 @@ var projectObj = {
                         // 工具栏要加按钮,且不能隐藏。菜单可以隐藏,两者又必须统一,所以启用新规则。怕以后又要改回来,所以保留。 CSL, 2018-01-02
                         return !project.Ration.canAdd(project.mainTree.selected);
                     },
-                    callback: function (key, opt) {
-                        project.Ration.addNewRation(null,rationType.ration,projectObj.selectColAndFocus,false);
-                       // ProjectController.addRation(project, controller, rationType.ration);
-                    }/*,
-                    visible: function(key, opt){
-                        var selected = project.mainTree.selected;
-                        return canInsertRationNode(selected);
-                    }*/
                 },
                 "insertGLJ": {
-                  name: "插入工料机",
-                  icon: 'fa-sign-in',
-                  disabled: function () {
-                      if (projectReadOnly) {
-                          return true;
-                      }
-                      // var selected = project.mainTree.selected;
-                      // return project.Ration.addRationChecking(selected);  // Vincent, 2018-01-02
-                      return !project.Ration.canAdd(project.mainTree.selected);
-                  },
-                  callback: function (key, opt) {
-                    let selected = project.mainTree.selected;
-                    if(selected.data.calcBase&&selected.data.calcBase!=""){
-                        alert("当前有基数计算,不能插入定额/量价/工料机。");
-                        return;
+                    name: "插入工料机",
+                    icon: 'fa-sign-in',
+                    disabled: function () {
+                        if (projectReadOnly) {
+                            return true;
+                        }
+                        // var selected = project.mainTree.selected;
+                        // return project.Ration.addRationChecking(selected);  // Vincent, 2018-01-02
+                        return !project.Ration.canAdd(project.mainTree.selected);
+                    },
+                    callback: function (key, opt) {
+                        let selected = project.mainTree.selected;
+                        if (selected.data.calcBase && selected.data.calcBase != "") {
+                            alert("当前有基数计算,不能插入定额/量价/工料机。");
+                            return;
+                        }
+                        getGLJData('insert');// ProjectController.addRation(project, controller, rationType.volumePrice);
+                    },
+                    visible: function (key, opt) {
+                        return false
                     }
-                    getGLJData('insert');// ProjectController.addRation(project, controller, rationType.volumePrice);
-                  },
-                  visible: function(key, opt){
-                    return false
-                  }
                 },
                 "insertLJ": {
                     name: "插入量价",//插入量价不需要自动定位到编号列
@@ -1354,29 +1391,29 @@ var projectObj = {
                         return !project.Ration.canAdd(project.mainTree.selected);
                     },
                     callback: function (key, opt) {
-                       /* project.Ration.addNewRation(null,rationType.volumePrice,function (newNode) {
-                            projectObj.selectColAndFocus(newNode,null);
-                        },true);*/
+                        /* project.Ration.addNewRation(null,rationType.volumePrice,function (newNode) {
+                             projectObj.selectColAndFocus(newNode,null);
+                         },true);*/
                     },
-                    items:{
-                        insertLabour:{
+                    items: {
+                        insertLabour: {
                             name: "人工",
                             icon: 'fa-sign-in',
-                            callback:function(key){
+                            callback: function (key) {
                                 project.Ration.insertVolumePrice(gljType.LABOUR);
                             }
                         },
-                        insertMaterial:{
-                           name:"材料" ,
+                        insertMaterial: {
+                            name: "材料",
                             icon: 'fa-sign-in',
-                            callback:function(key){
+                            callback: function (key) {
                                 project.Ration.insertVolumePrice(gljType.GENERAL_MATERIAL);
                             }
                         },
-                        insertMachine:{
-                            name:"机械" ,
+                        insertMachine: {
+                            name: "机械",
                             icon: 'fa-sign-in',
-                            callback:function(key){
+                            callback: function (key) {
                                 project.Ration.insertVolumePrice(gljType.GENERAL_MACHINE);
                             }
                         }
@@ -1393,13 +1430,13 @@ var projectObj = {
                     },
                     callback: function (key, opt) {
                         let selected = project.mainTree.selected;
-                        if(selected.data.calcBase&&selected.data.calcBase!=""){
+                        if (selected.data.calcBase && selected.data.calcBase != "") {
                             alert("当前有基数计算,不能插入定额/量价/工料机。");
                             return;
                         }
                         getGLJData('insertEquipment');// ProjectController.addRation(project, controller, rationType.volumePrice);
                     },
-                    visible: function(key, opt){//2018-11-08  新需求,这个按钮先隐藏,有需要再放开
+                    visible: function (key, opt) {//2018-11-08  新需求,这个按钮先隐藏,有需要再放开
                         let selected = project.mainTree.selected;
                         return projectObj.isInsertEquipmentVisable(selected);
                     }
@@ -1417,18 +1454,18 @@ var projectObj = {
                     callback: function (key, opt) {
                         installationFeeObj.showCalcInstallSettingDiv();
                     },
-                    visible: function(key, opt){
+                    visible: function (key, opt) {
                         return projectObj.project.isInstall();
                     }
                 },
-                "cleanzmhs":{
-                    name:'清空定额调整',
+                "cleanzmhs": {
+                    name: '清空定额调整',
                     icon: 'fa-remove',
                     callback: function (key, opt) {
                         let selected = project.mainTree.selected;
-                        projectObj.project.Ration.updateRationCodes([{'node':selected, value:selected.data.code}],true);
+                        projectObj.project.Ration.updateRationCodes([{ 'node': selected, value: selected.data.code }], true);
                     },
-                    visible: function(key, opt){
+                    visible: function (key, opt) {
                         let selected = project.mainTree.selected;
                         if (selected && selected.sourceType == ModuleNames.ration) {
                             return true;
@@ -1440,7 +1477,7 @@ var projectObj = {
                         if (projectReadOnly) {
                             return true;
                         }
-                        if(selected && selected.data.type != rationType.ration){
+                        if (selected && selected.data.type != rationType.ration) {
                             return true;
                         }
                         return false;
@@ -1540,10 +1577,10 @@ var projectObj = {
                     },
                     callback: function () {
                         var selected = project.mainTree.selected;
-                        if(selected.sourceType == project.Bills.getSourceType()&&selected.data.type==billType.FB&&selected.children.length<=0){//选中的是分部,并且没有子项,直接删除
+                        if (selected.sourceType == project.Bills.getSourceType() && selected.data.type == billType.FB && selected.children.length <= 0) {//选中的是分部,并且没有子项,直接删除
                             project.Bills.deleteSelectedNodes();//project.Bills.deleteSelectedNode();
-                        }else {
-                            $("#delete_row").modal({show:true});//弹出删除提示框;
+                        } else {
+                            $("#delete_row").modal({ show: true });//弹出删除提示框;
                         }
                     }
                 },
@@ -1558,11 +1595,11 @@ var projectObj = {
                             return true;
                         }
                     },
-                    visible:function(key, opt){//2019-11-11 新需求重新开放右键“造价计算”。
+                    visible: function (key, opt) {//2019-11-11 新需求重新开放右键“造价计算”。
                         return true;
                     }
                 },
-                "spr4":'--------',
+                "spr4": '--------',
                 "copyBlock": {
                     name: '复制整块',
                     icon: 'fa-copy',
@@ -1572,10 +1609,10 @@ var projectObj = {
                         }
                         let selection = projectObj.mainSpread.getActiveSheet().getSelections()[0];
                         let firstNode = projectObj.project.mainTree.items[selection.row];//当多选的情况,用mainTree.selected判断不正确,要用第一个选中的节点
-                        for(let i = 0;i< selection.rowCount;i++){ //多选的时候判断所有与第一个节点同级的节点
+                        for (let i = 0; i < selection.rowCount; i++) { //多选的时候判断所有与第一个节点同级的节点
                             let temNode = projectObj.project.mainTree.items[selection.row + i];
-                            if(firstNode.getParentID() == temNode.getParentID()){
-                                if(BlockController.copyBtnDisable(temNode) == true){
+                            if (firstNode.getParentID() == temNode.getParentID()) {
+                                if (BlockController.copyBtnDisable(temNode) == true) {
                                     return true;
                                 }
                             }
@@ -1589,14 +1626,14 @@ var projectObj = {
                         setTimeout(function () {
                             BlockController.copyBlock(selections[0]);
                             $.bootstrapLoading.end();
-                        },100)
+                        }, 100)
 
                     }
                 },
                 "pasteBlock": {
                     name: '粘贴整块',
                     icon: 'fa-paste',
-                    disabled: function (){
+                    disabled: function () {
                         if (projectReadOnly) {
                             return true;
                         }
@@ -1609,7 +1646,7 @@ var projectObj = {
                 "recoverDeletedNodes": {
                     name: '恢复删除节点',
                     icon: 'fa-undo',
-                    disabled: function (){
+                    disabled: function () {
                         return BlockController.recoverBlockDisabled();
                     },
                     callback: function () {
@@ -1622,14 +1659,14 @@ var projectObj = {
                     disabled: function () {
                         return projectReadOnly;
                     },
-                    visible: function(key, opt){
-                         let  selected = project.mainTree.selected;
-                         return selected.sourceType==ModuleNames.bills&&project.Bills.isEngineerEst(selected);//当焦点行是“专业工程暂估价”时,右键可见并有效。
+                    visible: function (key, opt) {
+                        let selected = project.mainTree.selected;
+                        return selected.sourceType == ModuleNames.bills && project.Bills.isEngineerEst(selected);//当焦点行是“专业工程暂估价”时,右键可见并有效。
                     },
                     callback: function () {
                         let node = project.mainTree.selected;//project.Bills.getNodeByFlag(project.mainTree.selected,fixedFlag.ENGINEERING_ESITIMATE);
-                        if(node){
-                            projectObj.editContent(node,'engineeringContent');
+                        if (node) {
+                            projectObj.editContent(node, 'engineeringContent');
                         }
                     }
                 },
@@ -1639,14 +1676,14 @@ var projectObj = {
                     disabled: function () {
                         return projectReadOnly;
                     },
-                    visible: function(key, opt){
-                        let  selected = project.mainTree.selected;
-                        return selected.sourceType==ModuleNames.bills&&project.Bills.isTotalService(selected);//当焦点行是“总承包服务费”时,右键可见并有效。
+                    visible: function (key, opt) {
+                        let selected = project.mainTree.selected;
+                        return selected.sourceType == ModuleNames.bills && project.Bills.isTotalService(selected);//当焦点行是“总承包服务费”时,右键可见并有效。
                     },
                     callback: function () {
                         let node = project.mainTree.selected;//project.Bills.getNodeByFlag(project.mainTree.selected,fixedFlag.TURN_KEY_CONTRACT);
-                        if(node){
-                            projectObj.editContent(node,'serviceContent');
+                        if (node) {
+                            projectObj.editContent(node, 'serviceContent');
                         }
                     }
                 },
@@ -1656,48 +1693,48 @@ var projectObj = {
                     disabled: function () {
                         return projectReadOnly;
                     },
-                    visible: function(key, opt){
-                        let  selected = project.mainTree.selected;
-                        return selected.sourceType==ModuleNames.bills&&project.Bills.isClaimVisa(selected);//当焦点行是“签证及索赔计价”时,右键可见并有效。
+                    visible: function (key, opt) {
+                        let selected = project.mainTree.selected;
+                        return selected.sourceType == ModuleNames.bills && project.Bills.isClaimVisa(selected);//当焦点行是“签证及索赔计价”时,右键可见并有效。
                     },
                     callback: function () {
                         let node = project.mainTree.selected;//project.Bills.getNodeByFlag(project.mainTree.selected,fixedFlag.CLAIM_VISA);
-                        if(node){
-                            projectObj.editContent(node,'claimVisa');
+                        if (node) {
+                            projectObj.editContent(node, 'claimVisa');
                         }
                     }
                 },
-                "replaceMaterial":{
-                    name:'智能材料替换',
+                "replaceMaterial": {
+                    name: '智能材料替换',
                     icon: 'fa-edit',
-                    disabled:function (key,opt) {
+                    disabled: function (key, opt) {
                         if (projectReadOnly) {
                             return true;
                         }
-                        let  selected = project.mainTree.selected;
-                        return selected.sourceType==ModuleNames.bills ?!(project.Bills.isFXorBX(selected)||selected.source.children.length ==0):true//是分项、补项或叶子清单才有效;
+                        let selected = project.mainTree.selected;
+                        return selected.sourceType == ModuleNames.bills ? !(project.Bills.isFXorBX(selected) || selected.source.children.length == 0) : true//是分项、补项或叶子清单才有效;
                     },
-                    callback:function(){
+                    callback: function () {
                         MaterialController.replaceMaterial([project.mainTree.selected]);
                     },
-                    visible: function(key, opt){//2018-11-15 暂时隐藏
+                    visible: function (key, opt) {//2018-11-15 暂时隐藏
                         return false
                     }
                 },
-                "createBlocks":{
-                    name:'生成组价模板',
+                "createBlocks": {
+                    name: '生成组价模板',
                     icon: 'fa-puzzle-piece',
-                    disabled:function (key,opt) {
+                    disabled: function (key, opt) {
                         if (projectReadOnly) {
                             return true;
                         }
                         let selected = project.mainTree.selected;
                         return selected.sourceType != ModuleNames.bills;
                     },
-                    callback: function(){
+                    callback: function () {
                         blockLibObj.checkShow();
                     },
-                    visible: function(key, opt){
+                    visible: function (key, opt) {
                         return G_SHOW_BLOCK_LIB;
                     }
                 }

+ 52 - 2
web/building_saas/main/js/views/std_billsGuidance_lib.js

@@ -138,7 +138,57 @@ const billsGuidance = (function () {
         }
     };
     //插入清单
-    function insertBills(lowestNodes) {
+    async function insertBills(lowestNodes) {
+        try {
+            let selTree = getSelTree(lowestNodes);
+            const { errMsg, parent, mainTreeFragment } = overwrite.getFragment();
+            if (errMsg) {
+                alert(errMsg);
+                return;
+            }
+            let compareData = compareTree(parent, mainTreeFragment, selTree.roots);
+            let sheet = projectObj.mainSpread.getActiveSheet(),
+                row = sheet.getActiveColumnIndex(),
+                col = sheet.getActiveColumnIndex();
+            if (compareData.postData.length > 0) {
+                //如果插入的是固定清单,则需要判断该固定清单在造价书中是否已存在,造价书中不可存在相同的固定清单
+                let fixedDatas = compareData.postData.filter((data) =>
+                data.updateType === updateType.create && Array.isArray(data.updateData.flags));
+                if (fixedDatas.length > 0) {
+                    //提示已存在此固定清单并且定位
+                    let firstFixed = fixedDatas[0].updateData;
+                    let existNode = projectObj.project.mainTree.items.find((node) =>
+                    node.data && node.data.flagsIndex && node.data.flagsIndex.fixed && node.data.flagsIndex.fixed.flag === firstFixed.flags[0].flag);
+                    if (existNode) {
+                        alert(`固定清单<strong>“${firstFixed.name}”</strong>已被第${existNode.serialNo() + 1}行清单占用。`);
+                        locateAtSpread(sheet, existNode.serialNo(), col);
+                        return;
+                    }
+                }
+                isInserting = true;
+                const newNodes = await ProjectController.addBillsByData(compareData.postData);
+                row = newNodes[newNodes.length - 1].serialNo();
+                //有新的节点插入,也有可能定位至旧节点(批量选用的情况下)
+                if (compareData.locateNode) {
+                    //该清单节点在主树的位置
+                    row = projectObj.project.mainTree.nodes[projectObj.project.mainTree.prefix + compareData.locateNode.data.ID].serialNo();
+                }
+                locateAtSpread(sheet, row, col);
+            } else if (compareData.locateNode) {
+                //该清单节点在主树的位置
+                row = projectObj.project.mainTree.nodes[projectObj.project.mainTree.prefix + compareData.locateNode.data.ID].serialNo();
+                locateAtSpread(sheet, row, col);
+            }
+        } catch (err) {
+            console.log(err);
+            if (!$('hintBox_form').is(':visible')) {
+                alert(err);
+            }
+        } finally {
+            isInserting = false;
+        }
+    }
+    /* function insertBills(lowestNodes) {
         let selTree = getSelTree(lowestNodes);
         const { errMsg, parent, mainTreeFragment } = overwrite.getFragment();
         if (errMsg) {
@@ -199,7 +249,7 @@ const billsGuidance = (function () {
             row = projectObj.project.mainTree.nodes[projectObj.project.mainTree.prefix + compareData.locateNode.data.ID].serialNo();
             locateAtSpread(sheet, row, col);
         }
-    }
+    } */
     function locateAtSpread(sheet, row, col) {
         sheet.setSelection(row, col, 1, 1);
         projectObj.mainController.setTreeSelected(projectObj.mainController.tree.items[row]);//触发树节点选中事件

+ 40 - 19
web/building_saas/main/js/views/zmhs_view.js

@@ -105,6 +105,8 @@ let zmhs_obj = {
               args.sheet.repaint();
           });
           SheetDataHelper.protectdSheet(this[pre+"coeSheet"]);
+      }else{
+        zmhs_obj[pre+"coeSpread"].refresh();
       }
     },
     initCusSpread:function () {
@@ -142,7 +144,7 @@ let zmhs_obj = {
           this[pre+"stableSheet"].name(pre+'ration_stable');
           this[pre+"stableSheet"].bind(GC.Spread.Sheets.Events.ValueChanged,this.onStableValueChange);
           this[pre+"stableSheet"].bind(GC.Spread.Sheets.Events.RangeChanged, this.onStableRangeChanged);
-          SheetDataHelper.protectdSheet(this);
+          SheetDataHelper.protectdSheet(this[pre+"stableSheet"]);
       }
     },
     showCoeData:function (node) {
@@ -151,8 +153,8 @@ let zmhs_obj = {
         let ration_coe = projectObj.project.ration_coe;
         let coeList = [];
         let coeSheet = this[pre+'coeSheet'];
-        this.coeSheet.suspendPaint();
-        this.coeSheet.suspendEvent();
+        coeSheet.suspendPaint();
+        coeSheet.suspendEvent();
         if(pre == "divide_"){//来自分摊
           coeList = divideObj.getCoeList();
         }else{
@@ -207,7 +209,7 @@ let zmhs_obj = {
               if(this.cusSheetData) cusList = this.cusSheetData.coes;
           }
         }
-        
+        this[pre+'cusSheet'].setRowCount(0);
         sheetCommonObj.showData(this[pre+'cusSheet'], this.cusSetting,cusList);
         this[pre+'cusSheet'].getRange(cusList.length,-1,this[pre+'cusSheet'].getRowCount()-cusList.length, -1, GC.Spread.Sheets.SheetArea.viewport).locked(true);
         if(projectReadOnly){
@@ -348,7 +350,7 @@ let zmhs_obj = {
         let me = zmhs_obj;
         let sheet = args.sheet, row = args.row, col = args.col;
         let cellType = sheet.getCellType(row, col);
-        if (args.sheetName == 'ration_coe' && cellType instanceof GC.Spread.Sheets.CellTypes.CheckBox) {
+        if ((args.sheetName == 'divide_ration_coe'||args.sheetName == 'ration_coe')&& cellType instanceof GC.Spread.Sheets.CellTypes.CheckBox) {
             me.onCoeCheckBoxClick(sender, args)
         }
     },
@@ -442,6 +444,7 @@ let zmhs_obj = {
     },
     onCoeCheckBoxClick:function (sender, args) {
         let me = zmhs_obj;
+        let pre = $('#divide_subSpread').is(':visible')?"divide_":"";
         let checkboxValue = args.sheet.getCell(args.row, args.col).value();
         let newval = 0;
         if (checkboxValue) {
@@ -451,7 +454,7 @@ let zmhs_obj = {
             newval = 1;
             args.sheet.getCell(args.row, args.col).value(newval);
         }
-        let recode = me.coeSheetData[args.row];
+        let recode = me[pre+"coeSheetData"][args.row];
         recode.assistCode && recode.assistCode != "" ? me.adjustAssClick(args,newval) : projectObj.project.ration_coe.adjustCoeClick(recode, newval);
     },
     generateHtmlString: function (context,cellRect,$editor) {//这里要改成动态的了,根据自定义系数内容生成对应的输入框
@@ -495,19 +498,22 @@ let zmhs_obj = {
     },
     onCoeValueChange:function (e,args) {
         let fieldID =  zmhs_obj.coeSetting.header[args.col].dataCode;
-        let recode = zmhs_obj.coeSheetData[args.row];
+        let pre = $('#divide_subSpread').is(':visible')?"divide_":"";
+        let recode = zmhs_obj[pre+"coeSheetData"][args.row];   
         if(gljUtil.isDef(recode.option_codes)&&recode.option_codes!=""&& fieldID == 'name'){//说明是选择了下拉框
-            projectObj.project.ration_coe.adjustCoeClick(recode, 1,{'select_code':args.newValue});
+          projectObj.project.ration_coe.adjustCoeClick(recode, 1,{'select_code':args.newValue});
         }else if(recode.assistCode &&recode.assistCode!=""){//编辑的是辅助定额
             zmhs_obj.updateRationAss({editingText:args.newValue,row:args.row - zmhs_obj.assFirstIndex})//转换一下
         }
+        
     },
     onCusValueChange:function (e,args) {
       zmhs_obj.changeCusValue([{row:args.row,col:args.col,value:args.newValue}]);
     },
     changeCusValue:function (datas) {//[{row:,col,value}]
-        if(this.cusSheetData){
-            let tem_coes = _.cloneDeep(this.cusSheetData.coes);
+        let pre = $('#divide_subSpread').is(':visible')?"divide_":"";
+        if(this[pre+"cusSheetData"]){
+            let tem_coes = _.cloneDeep(this[pre+"cusSheetData"].coes);
             for(let d of datas){
                 if(d.value&&!sheetCommonObj.checkData(d.col,this.cusSetting,d.value)){
                     this.showDatas();
@@ -525,7 +531,7 @@ let zmhs_obj = {
                 }
             }
             let doc = {'coes':tem_coes,'content':this.generationContent(tem_coes)};
-            projectObj.project.ration_coe.adjustCoeClick(this.cusSheetData, 1,doc);
+            projectObj.project.ration_coe.adjustCoeClick(this[pre+"cusSheetData"], 1,doc);
         }
     },
     generationContent:function (coes) {
@@ -574,13 +580,18 @@ let zmhs_obj = {
     },
     adjustAssClick:function (args,newval) {
         let me = zmhs_obj, row = args.row - me.assFirstIndex;
-        projectObj.project.ration_ass.updateActualValue(me.assSheetData, row,null,newval);
+        if($('#divide_subSpread').is(':visible')){
+          divideObj.updateActualValue(me.divide_assSheetData, args.row, null,newval);
+        }else{
+          projectObj.project.ration_ass.updateActualValue(me.assSheetData, row,null,newval);
+        }
     },
     updateRationAss: function (args) {
         var me = zmhs_obj;
         var newval;
+        let pre = $('#divide_subSpread').is(':visible')?"divide_":"";
         newval = me.numberValueChecking(args.editingText);
-        var recode = me.assSheetData[args.row];
+        var recode = me[pre+"assSheetData"][args.row];
         if (args.editingText === null) {
             newval = parseFloat(recode.stdValue);
         }
@@ -590,7 +601,11 @@ let zmhs_obj = {
         }
         if (isValidate) {
             newval = scMathUtil.roundTo(newval, -2);
-            projectObj.project.ration_ass.updateActualValue(me.assSheetData, args.row, newval);
+            if($('#divide_subSpread').is(':visible')){
+              divideObj.updateActualValue(me.divide_assSheetData, args.row, newval);
+            }else{
+              projectObj.project.ration_ass.updateActualValue(me.assSheetData, args.row, newval);
+            }
         } else {
             me.showDatas();
         }
@@ -645,9 +660,13 @@ let zmhs_obj = {
         // 获取需要更新的数据
         const rationGLJ = projectObj.project.ration_glj;
         const updateProportionList = this.getStablePostData(sheetData);
-        const projectID = projectObj.project.ID();
-        const rationID = projectObj.project.mainTree.selected.data.ID;
-        rationGLJ.updateProportion(updateProportionList, projectID, rationID);
+        if($('#divide_subSpread').is(':visible')){
+          divideObj.updateProportion(updateProportionList);
+        }else{
+          const projectID = projectObj.project.ID();
+          const rationID = projectObj.project.mainTree.selected.data.ID;
+          rationGLJ.updateProportion(updateProportionList, projectID, rationID);
+        }
     },
     // 将值进行四舍五入处理
     handleDataWithPrecision: function (data, precision) {
@@ -657,10 +676,11 @@ let zmhs_obj = {
     },
     // 获取经过自动处理后的表格数据
     getAutoStableSheetData: function (changedData, precision) {
+        let pre = $('#divide_subSpread').is(':visible')?"divide_":"";
         const expectedValue = 100;
         const unchangedData = [];
         const col = this.stableSetting.header.findIndex(item => item.dataCode === 'adjustProportion');
-        this.stableSheetData.forEach((item, index) => {
+        this[pre+"stableSheetData"].forEach((item, index) => {
             const matchData = changedData.find(dItem => dItem.row === index);
             if (!matchData) {
                 unchangedData.push({ row: index, col: col, value: item.adjustProportion});
@@ -696,9 +716,10 @@ let zmhs_obj = {
         }
     },
     getStablePostData: function (sheetData) {
+        let pre = $('#divide_subSpread').is(':visible')?"divide_":"";
         const postData = [];
         sheetData.forEach(cell => {
-            const originItem = this.stableSheetData[cell.row];
+            const originItem = this[pre+"stableSheetData"][cell.row];
             if (cell.value !== originItem.adjustProportion) {
                 postData.push({ ID: originItem.ID, adjustProportion: cell.value});
             }

+ 6 - 0
web/building_saas/pm/js/pm_gc.js

@@ -905,8 +905,10 @@ function e_recFiles(btn){
             let findData = type === fileType.unitPriceFile ? {id: recObjs[i].id} : {ID: recObjs[i].id};
             updateDatas.push(getUpdateObj(type, findData, {deleteInfo: null, name: delPostFix(recObjs[i].name) + decDate}));
         }
+        let isRecoverProj = false;
         //恢复建设项目
         if(updateDatas.length > 0 && deleted(selected)){
+            isRecoverProj = true;
             updateDatas.push(getUpdateObj(projectType.project, {ID: selected.data.ID}, {deleteInfo: null, name: delPostFix(selected.data.name) + decDate}));
         }
         updateDatas = deWeightName(updateDatas);
@@ -925,6 +927,9 @@ function e_recFiles(btn){
                     gcTreeObj.refreshNodeData(selected);
                 }
             }
+            if (isRecoverProj) {
+                projTreeObj.emitTreeChange();
+            }
         }
     });
 }
@@ -986,6 +991,7 @@ function e_recProj(btn){
             }
             v_removeNode(selected);
             v_refreshNode(selected, true);
+            projTreeObj.emitTreeChange();
         }
     });
 }

+ 63 - 14
web/building_saas/pm/js/pm_newMain.js

@@ -503,6 +503,7 @@ const projTreeObj = {
             }
             projTreeObj.moveTo(selected, null, parent, next, null, action);
             $.bootstrapLoading.end();
+            projTreeObj.emitTreeChange();
         });
     },
     //升级后选中节点的后兄弟节点不成为其子节点,因为有层级类型限制(相当于选中节点移动到父项后成为其后兄弟)
@@ -1623,6 +1624,40 @@ const projTreeObj = {
         let result =await ajaxGet("/pm/api/getUploadToken");
         $("#confirm-import").show();
         projTreeObj.uptoken=result.uptoken;
+    },
+    // 树数据发生变化,触发推送
+    emitTreeChange: function () {
+        const compilationID = compilationData._id;
+        // 获取当前树节点展开状态和焦点行
+        const isActive = $('#tab_pm_all').hasClass('active');
+        const expandState = isActive ? this.tree.getExpState(this.tree.items) : null;
+        const selection = isActive && this.tree.selected ? { row: this.tree.selected.serialNo(), rowCount: 1 } : null;
+        socket.emit('pmTreeChange', { userID, compilationID, expandState, selection });
+    },
+    initTree: function (refresh = false, callback, expandCallback) {
+        if (gcTreeObj.workBook) {
+            gcTreeObj.workBook.destroy();
+            gcTreeObj.workBook = null;
+        }
+        gcTreeObj.tree = null;
+        init(refresh, callback, expandCallback);
+    },
+    handleNotifyClick: function (expandState, selection) {
+        $('#notify').hide();
+        const callback = () => {
+            const sheet = this.workBook.getSheet(0);
+            if (selection && this.tree.items[selection.row]) {
+                this.initSelection(selection, { row: 0, rowCount: 1 }, sheet);
+                const col = sheet.getActiveColumnIndex();
+                sheet.setSelection(selection.row, col, 1, 1);
+            }
+        };
+        const expandCallback = () => {
+            if (expandState) {
+                this.tree.setExpandedByState(this.tree.items, expandState);
+            }
+        }
+        this.initTree(true, callback, expandCallback);
     }
 
 };
@@ -1651,15 +1686,8 @@ $(document).ready(function() {
 
     });
 
-    init();
-    $('#tab_pm_all').on('show.bs.tab', function () {
-        if(gcTreeObj.workBook){
-            gcTreeObj.workBook.destroy();
-            gcTreeObj.workBook = null;
-        }
-        gcTreeObj.tree = null;
-        init();
-    });
+    projTreeObj.initTree();
+    $('#tab_pm_all').on('show.bs.tab', () => projTreeObj.initTree(true));
 
     // 侧滑数据
    /* $(".poj-list").on('click', ".open-sidebar", function() {
@@ -1952,6 +1980,7 @@ $(document).ready(function() {
                 setTimeout(function () {
                     STATE.deleting = false;
                 }, 500);
+                projTreeObj.emitTreeChange();
             }, function () {
                 $.bootstrapLoading.end();
                 setTimeout(function () {
@@ -1986,6 +2015,7 @@ $(document).ready(function() {
             select.data.name = newName;
             let sheet = projTreeObj.workBook.getActiveSheet();
             projTreeObj.setCellValue({ row: sheet.getActiveRowIndex(), col: 0 }, select, sheet, projTreeObj.setting);
+            projTreeObj.emitTreeChange();
         });
     });
 
@@ -2269,7 +2299,7 @@ function initNodesVisibility(nodes, visible) {
     });
 }
 
-function initProjects(callback) {
+function initProjects(callback, expandCallback) {
     GetAllProjectData(function (datas) {
         //设置项目类别
         for (let data of datas) {
@@ -2288,7 +2318,11 @@ function initProjects(callback) {
             sheet.name('projectSheet');
             sheetCommonObj.spreadDefaultStyle(projTreeObj.workBook);
             projTreeObj.sumEngineeringCost();
-            initNodesVisibility(projTreeObj.tree.items, false);
+            if (expandCallback) {
+                expandCallback();
+            } else {
+                initNodesVisibility(projTreeObj.tree.items, false);
+            }
             projTreeObj.showTreeData(projTreeObj.tree.items, projTreeObj.setting, sheet);
             //初始选择
             const initSel = sheet.getSelections()[0] ? sheet.getSelections()[0] : {row: 0, rowCount: 1};
@@ -2307,7 +2341,7 @@ function initProjects(callback) {
 /**
  * 初始化数据
  * @return {void} */
-async function init(refresh = false) {//refresh是刷新页面时才使用的
+async function init(refresh = false, callback, expandCallback) {//refresh是刷新页面时才使用的
     try {
         billValuation = billValuation.replace(/\n/g, '\\n');
         rationValuation = rationValuation.replace(/\n/g, '\\n');
@@ -2319,7 +2353,12 @@ async function init(refresh = false) {//refresh是刷新页面时才使用的
             $("#progress_modal_body").text('首次加载例题,请稍候……');
             await ajaxPost('/pm/api/prepareInitialData', { user_id: userID });
             await importProcessChecking(null, null, () => {
-                initProjects(() => $.bootstrapLoading.progressEnd());
+                initProjects(() => {
+                    $.bootstrapLoading.progressEnd();
+                    if (callback) {
+                        callback();
+                    }
+                }, expandCallback);
             }, true);
         } else {
             await importProcessChecking(null, ({ content }) => {
@@ -2327,7 +2366,12 @@ async function init(refresh = false) {//refresh是刷新页面时才使用的
                 $("#progress_modal_body").text(content);
             }, () => {
                 $.bootstrapLoading.start();
-                initProjects(() => $.bootstrapLoading.end());
+                initProjects(() => {
+                    $.bootstrapLoading.end();
+                    if (callback) {
+                        callback();
+                    }
+                }, expandCallback);
             }, true);
         }
         engineering = engineeringList !== null && engineeringList !== undefined ? JSON.parse(engineeringList) : [];
@@ -2371,6 +2415,7 @@ function AddProject() {
         setTimeout(function () {
             STATE.addingProject = false;
         }, 500);
+        projTreeObj.emitTreeChange();
     };
     let errCB = function () {
         $.bootstrapLoading.end();
@@ -2957,6 +3002,7 @@ function AddTender() {
             setTimeout(function () {
                 STATE.addingTender = false;
             }, 500);
+            projTreeObj.emitTreeChange();
         };
         let errCB = function () {
             $.bootstrapLoading.end();
@@ -3053,6 +3099,7 @@ function AddFolder() {
         setTimeout(function () {
             STATE.addingFolder = false;
         }, 500);
+        projTreeObj.emitTreeChange();
     };
     let errCB = function () {
         $.bootstrapLoading.end();
@@ -3819,6 +3866,7 @@ function handleProjectAfterChecking(projectData) {
     const rootData = projectData.find(item => item.projType === projectType.project);
     const sorted = commonUtil.getSortedTreeData(rootData.ParentID, projectData);
     doAfterImport(sorted);
+    projTreeObj.emitTreeChange();
 }
 // 导入检查完成时,对新增单位工程的处理
 function handleTenderAfterChecking(projectData, orgTender) {
@@ -3831,6 +3879,7 @@ function handleTenderAfterChecking(projectData, orgTender) {
     const newNode = projTreeObj.insert(tenderData, parent, next);
     const refreshNodes = projTreeObj.calEngineeringCost(newNode);
     projTreeObj.refreshNodeData(refreshNodes);
+    projTreeObj.emitTreeChange();
 }
 
 async function importProcessChecking(key, processingFunc = null, completeFunc = null, immediately = false) {

+ 1 - 1
web/building_saas/pm/js/pm_share.js

@@ -923,7 +923,7 @@ const pmShare = (function () {
             $.bootstrapLoading.progressStart('拷贝项目', true);
             $("#progress_modal_body").text('正在拷贝项目,请稍候……');
             await ajaxPost('/pm/api/copyProjects', { projectMap: copyMap, user_id: userID, tenderCount: 1 });
-            importProcessChecking();
+            importProcessChecking(null, null, projTreeObj.emitTreeChange);
         } catch (err) {
             alert(err);
         }

+ 33 - 0
web/building_saas/pm/js/pm_tree.js

@@ -434,6 +434,13 @@ const pmTree = {
                             console.log(`${node.serialNo() + 1}:${node.data.name} node索引大于next索引`);
                             return false;
                         }
+                        // nextSibling跟parent children的下一节点对应不上
+                        if (nodeIdx !== -1 && 
+                            (nodeIdx === parent.children.length - 1 && nextIdx !== -1) || 
+                            (nodeIdx !== parent.children.length - 1 && nodeIdx + 1 !== nextIdx)) {
+                            console.log(`${node.serialNo() + 1}:${node.data.name} nextSibling与树显示的下一节点对应不上`);
+                            return false;
+                        }
                         if (node.nextSibling && node.parent !== node.nextSibling.parent) {
                             console.log(`${node.serialNo() + 1}:${node.data.name} 与兄弟节点 ${node.nextSibling.serialNo() + 1}:${node.nextSibling.data.name} 父节点不同`);
                             return false;
@@ -444,10 +451,36 @@ const pmTree = {
                                 return false;
                             }
                         }
+                        
                     }
                     return true;
                 }
             };
+
+            Tree.prototype.getExpState = function (nodes) {
+                let sessionExpanded = [];
+                function getStat(items){
+                    for(let item of items){
+                        sessionExpanded.push(item.expanded ? 1 : 0);
+                    }
+                }
+                getStat(nodes);
+                let expState = sessionExpanded.join('');
+                return expState;
+            };
+    
+            //节点根据展开收起列表'010101'展开收起
+            Tree.prototype.setExpandedByState = function (nodes, expState) {
+                let expStateArr = expState.split('');
+                for(let i = 0; i < nodes.length; i++){
+                    let expanded = expStateArr[i] == 1 ? true : false;
+                    if(nodes[i].expanded === expanded){
+                        continue;
+                    }
+                    nodes[i].setExpanded(expanded);
+                }
+            };
+
             Tree.prototype.setNodesExpanded = function (nodes, sheet) {
                 TREE_SHEET_HELPER.massOperationSheet(sheet, () => {
                     nodes.forEach(node => {

+ 1 - 1
web/building_saas/unit_price_file/index.html

@@ -174,7 +174,7 @@
     <img src="/web/dest/css/img/engineering.png" id="eng_pic" style="display: none">
     <img src="/web/dest/css/img/tender.png" id="tender_pic" style="display: none">
 
-    <img src="/web/dest/css/img/blockLib.png" id="blockLib_pic" style="display: none">
+    <img src="/web/dest/css/img/blocklib.png" id="blockLib_pic" style="display: none">
     <img src="/web/dest/css/img/folder_open.png" id="folder_pic" style="display: none">
     <img src="/web/dest/css/img/tender.png" id="block_pic" style="display: none">
 

+ 3 - 0
web/common/html/header.html

@@ -36,6 +36,9 @@
                     <a class="dropdown-item" href="/logout">退出登录</a>
                 </div>
             </li>
+            <li class="nav-item">
+                <a id="fullscreen-a" href="javascript:void(0);" class="nav-link" onclick="commonUtil.handleFullscreen()"><span><i class="fa fa-window-maximize "></i> 全屏</span></a>
+            </li>
             <% if (!versionName.includes('免费')) {%>
             <li class="nav-item">
               <a href="user/buy" target="_blank"><img src="/web/building_saas/img/vip.png" data-toggle="tooltip" data-placement="bottom" data-original-title="专业版用户"></a>

+ 320 - 0
web/over_write/hunan_2020.js

@@ -0,0 +1,320 @@
+// 清单基数
+const progression = ['施工场地建设费', '养护单位项目管理费', '养护单位项目管理费(未单独设置项目部)', '工程监理费', '工程监理费(未实行社会监理)', '设计文件审查费', '工程设计费'];
+const deficiency = {};
+if (typeof baseFigureMap !== 'undefined') {
+    const { fixedFlag } = commonConstants;
+    const budgetMap = {
+        // 显示:除清单固定类别是“建筑安装工程费”的以外部分可显示
+        '养护工程费': {
+            base: 'YHGCF',
+            fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            filter: [fixedFlag.CONSTRUCTION_INSTALL_FEE],
+            pick: false,
+        },
+        // 显示:仅清单固定类别是“安全生产费”的可显示
+        '养护工程费(不含安全生产费)': {
+            base: 'YHGCFBHSC',
+            fixedFlag: null,
+            filter: [fixedFlag.SAFE_COST],
+            pick: true
+        },
+        // 显示:仅清单固定类别是“养护工程其他费用”部分可显示
+        '养护工程费(不含设备费)': {
+            base: 'YHGCFBHSB',
+            fixedFlag: null,
+            filter: [fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true
+        },
+        // 显示:除清单固定类别是“建筑安装工程费”的以外部分可显示
+        '定额养护工程费': {
+            base: 'DEYHGCF',
+            fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            filter: [fixedFlag.CONSTRUCTION_INSTALL_FEE],
+            pick: false
+        },
+        // 显示:仅清单固定类别是“养护工程其他费用”、“施工场地建设费”的可显示
+        '定额养护工程费(不含专项费用)': {
+            base: 'DEYHGCFBHZXFY',
+            fixedFlag: null,
+            filter: [fixedFlag.MAINTENANCE_EXPENSES, fixedFlag.CONSTRUCTION_PLANT_COST],
+            pick: true,
+        },
+        // 显示:除清单固定类别是“建筑安装工程费”、“土地使用及拆迁补偿费”的以外部分可显示
+        '土地使用及拆迁补偿费': {
+            base: 'TDSYJCQBCF',
+            fixedFlag: fixedFlag.LAND_USED_DEMOLITION,
+            filter: [fixedFlag.CONSTRUCTION_INSTALL_FEE, fixedFlag.LAND_USED_DEMOLITION],
+            pick: false,
+        },
+        // 显示:除清单固定类别是“建筑安装工程费”、“土地使用及拆迁补偿费”、“工程建设其他费用”的以外部分可显示。
+        '工程建设其他费用': {
+            base: 'GCJSQTFY',
+            fixedFlag: fixedFlag.MAINTENANCE_EXPENSES,
+            filter: [fixedFlag.CONSTRUCTION_INSTALL_FEE, fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: false,
+        },
+        // 显示:仅清单固定类别是“施工场地建设费”的可显示。
+        '施工场地建设费': {
+            isProgressive: true,
+            base: 'SGCDJSF',
+            fixedFlag: null,
+            filter: [fixedFlag.CONSTRUCTION_PLANT_COST],
+            pick: true,
+        },
+        // 显示:仅清单固定类别是“养护工程其他费用”部分可显示。
+        '养护单位项目管理费': {
+            isProgressive: true,
+            base: 'YHDWXMGLF',
+            fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            filter: [fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true,
+        },
+        // 显示:仅清单固定类别是“养护工程其他费用”部分可显示。
+        '养护单位项目管理费(未单独设置项目部)': {
+            isProgressive: true,
+            base: 'YHDWXMGLFWSXMB',
+            fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            filter: [fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true,
+        },
+        // 显示:仅清单固定类别是“养护工程其他费用”部分可显示。
+        '工程监理费': {
+            isProgressive: true,
+            base: 'GCJLF',
+            fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            filter: [fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true,
+        },
+        // 显示:仅清单固定类别是“养护工程其他费用”部分可显示。
+        '工程监理费(未实行社会监理)': {
+            isProgressive: true,
+            base: 'GCJLFWSXSHJL',
+            fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            filter: [fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true,
+        },
+        // 显示:只有清单固定类别是“养护工程其他费用”部分可显示。
+        '设计文件审查费': {
+            isProgressive: true,
+            base: 'SJWJSCF',
+            fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            filter: [fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true,
+        },
+        // 显示:只有清单固定类别是“养护工程其他费用”部分可显示。
+        '工程设计费': {
+            isProgressive: true,
+            base: 'GCSJF',
+            fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            filter: [fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true,
+        },
+        // 显示:仅“价差预备费”可显示
+        '价差预备费': {
+            base: 'JCYBF',
+            fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            filter: [fixedFlag.SPREAD_BUDGET_FEE],
+            pick: true,
+        },
+    };
+    const boqMap = {
+        //仅允许用于固定类别是“第100章至700章清单”以外的清单
+        '各章清单合计': {
+            base: 'GZQDHJ',
+            fixedFlag: fixedFlag.ONE_SEVEN_BILLS,
+            filter: [fixedFlag.ONE_SEVEN_BILLS],
+            pick: false
+        },
+        //仅允许用于固定类别是“第100章至700章清单”以外的清单
+        '专项暂定合计': {
+            base: 'ZXZDHJ',
+            fixedFlag: null,
+            filter: [fixedFlag.ONE_SEVEN_BILLS],
+            pick: false
+        },
+        /*
+        *  清单固定行[第100章至700章清单]下的[第100章清单]需要允许清单可使用基数{100章以外合计}
+        *  因此{100章以外合计}不设置关联的清单固定行
+        * */
+        //仅允许用于固定类别为“100章清单”引用
+        '100章以外清单合计': {
+            base: 'YBZYHQDHJ',
+            fixedFlag: null,
+            filter: [fixedFlag.ONE_HUNDRED_BILLS],
+            pick: true
+        }
+    };
+    baseFigureMap.budget = budgetMap;
+    baseFigureMap.boq = boqMap;
+}
+
+if (typeof baseFigureTemplate !== 'undefined') {
+    const { fixedFlag } = commonConstants;
+    baseFigureTemplate.budget = {
+        // 养护工程费 算法:取清单固定类别是“建筑安装工程费”的金额。
+        YHGCF(tender) {
+            return cbTools.getBaseFee(fixedFlag.CONSTRUCTION_INSTALL_FEE, tender, 'common');
+        },
+        // 养护工程费(不含安全生产费) 算法:取清单固定类别是“建筑安装工程费”的金额,扣除“安全生产费”的金额。
+        YHGCFBHSC(tender) {
+            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, [fixedFlag.SAFE_COST], tender, 'common');
+        },
+        // 养护工程费(不含设备费) 算法:取清单固定类别是“建筑安装工程费”的金额,扣除设备的金额。
+        YHGCFBHSB(tender) {
+            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, [fixedFlag.EQUIPMENT_ACQUISITION_FEE], tender, 'common');
+        },
+        // 定额养护工程费 取清单固定类别是“建筑安装工程费”的定额建安费。
+        DEYHGCF(tender) {
+            return cbTools.getBaseFee(fixedFlag.CONSTRUCTION_INSTALL_FEE, tender, 'rationCommon');
+        },
+        // 定额养护工程费(不含专项费用) 算法:取清单固定类别是“建筑安装工程费”的“定额建安费”,扣除“专项费用”的“定额建安费”。
+        DEYHGCFBHZXFY(tender) {
+            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, [fixedFlag.SPECIAL_COST], tender, 'rationCommon');
+        },
+        // 土地使用及拆迁补偿费 算法:取清单固定类别是“土地使用及拆迁补偿费”的金额。
+        TDSYJCQBCF(tender) {
+            return cbTools.getBaseFee(fixedFlag.LAND_USED_DEMOLITION, tender, 'common');
+        },
+        // 工程建设其他费用 算法:取清单固定类别是“养护工程其他费用”的金额。
+        GCJSQTFY(tender) {
+            return cbTools.getBaseFee(fixedFlag.MAINTENANCE_EXPENSES, tender, 'common');
+        },
+        // 施工场地建设费 算法:以{定额养护工程费(不含专项费用)}为基数,采用累进办法计算。
+        SGCDJSF(tender) {
+            const baseFee = this['DEYHGCFBHZXFY'](tender);
+            if (!tender) {
+                calcBase.baseProgressiveFee = baseFee;
+            }
+            return calculateUtil.getProgressiveFee(baseFee, '施工场地建设费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice, deficiency);
+        },
+        // 养护单位项目管理费 算法:以{定额养护工程费}为基数,采用累进办法计算。
+        YHDWXMGLF(tender) {
+            const baseFee = this['DEYHGCF'](tender);
+            if (!tender) {
+                calcBase.baseProgressiveFee = baseFee;
+            }
+            return calculateUtil.getProgressiveFee(baseFee, '养护单位项目管理费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice, deficiency);
+        },
+        // 养护单位项目管理费(未单独设置项目部) 算法:以{定额养护工程费}为基数,采用累进办法计算。
+        YHDWXMGLFWSXMB(tender) {
+            const baseFee = this['DEYHGCF'](tender);
+            if (!tender) {
+                calcBase.baseProgressiveFee = baseFee;
+            }
+            return calculateUtil.getProgressiveFee(baseFee, '养护单位项目管理费(未单独设置项目部)', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice, deficiency);
+        },
+        // 工程监理费 算法:以{定额养护工程费}为基数,采用累进办法计算。
+        GCJLF(tender) {
+            const baseFee = this['DEYHGCF'](tender);
+            if (!tender) {
+                calcBase.baseProgressiveFee = baseFee;
+            }
+            return calculateUtil.getProgressiveFee(baseFee, '工程监理费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice, deficiency);
+        },
+        // 工程监理费(未实行社会监理) 算法:以{定额养护工程费}为基数,采用累进办法计算。
+        GCJLFWSXSHJL(tender) {
+            const baseFee = this['DEYHGCF'](tender);
+            if (!tender) {
+                calcBase.baseProgressiveFee = baseFee;
+            }
+            return calculateUtil.getProgressiveFee(baseFee, '工程监理费(未实行社会监理)', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice, deficiency);
+        },
+        // 设计文件审查费 算法:以{定额养护工程费}为基数,采用累进办法计算。
+        SJWJSCF(tender) {
+            const baseFee = this['DEYHGCF'](tender);
+            if (!tender) {
+                calcBase.baseProgressiveFee = baseFee;
+            }
+            return calculateUtil.getProgressiveFee(baseFee, '设计文件审查费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice, deficiency);
+        },
+        // 工程设计费 算法:以{定额养护工程费}为基数,采用累进办法计算。
+        GCSJF(tender) {
+            const baseFee = this['DEYHGCF'](tender);
+            if (!tender) {
+                calcBase.baseProgressiveFee = baseFee;
+            }
+            return calculateUtil.getProgressiveFee(baseFee, '工程设计费', projectObj.project.property.progressiveInterval, decimalObj.bills.totalPrice, deficiency);
+        },
+        /*  价差预备费 算法:以建筑安装工程费为基数,按设计文件编制年始至养护项目工程竣工年终的年数和年工程造价增涨率计算。
+            价差预备费 P * [(1+i)^(n-1) -1]
+            P——建筑安装工程费总额(元);
+            i——年工程造价增涨率(%);
+            n——设计文件编制年至养护项目开工年+养护项目建设期限(年)。
+        */
+        JCYBF(tender) {
+            //建筑安装工程费作为基数
+            const installFee = this['JZAZGCF'](tender);
+            //年造价增涨
+            const costGrowthRate = calcBase.project.property.costGrowthRate
+                ? calcBase.project.property.costGrowthRate
+                : 0;
+            //增涨计费年限
+            const growthPeriod = projectObj.project.property.growthPeriod
+                ? calcBase.project.property.growthPeriod
+                : 0;
+            //= P * [(1+i)^(n-1) -1]
+            return (installFee * (Math.pow(1 + costGrowthRate, growthPeriod - 1) - 1)).toDecimal(decimalObj.bills.totalPrice);
+        }
+
+    };
+
+    baseFigureTemplate.boq = {
+        //{各章清单合计}
+        // 取清单固定类别是“第100章至700章清单”的金额
+        'GZQDHJ': function (tender) {
+            return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'common');
+        },
+        //{专项暂定合计}
+        // 汇总专项暂定列有值的清单的金额
+        'ZXZDHJ': function (tender) {
+            let rst = 0,
+                feeField = 'common',
+                subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
+            let billsData = calcBase.project.Bills.datas,
+                filterData = billsData.filter(function (data) {
+                    return data.specialProvisional;
+                });
+            for (let data of filterData) {
+                if (cbTools.isUnDef(data.feesIndex) || _.isEmpty(data.feesIndex) ||
+                    cbTools.isUnDef(data.feesIndex[feeField]) || cbTools.isUnDef(data.feesIndex[feeField][subFeeField])) {
+                    continue;
+                }
+                rst += data.feesIndex[feeField][subFeeField];
+            }
+            return rst.toDecimal(decimalObj.bills.totalPrice);
+        },
+        //{100章以外清单合计}
+        // 取清单固定清单[第100章至700章清单]的金额,但扣除清单100章下的金额。
+        // 如果是固定清单[第100章至700章清单]下100章以外清单引用此基数,要排除自身(目前只允许100章的清单使用,所以暂时不需要此判断)
+        'YBZYHQDHJ': function (tender) {
+            let oneToSeven = cbTools.findNodeByFlag(fixedFlag.ONE_SEVEN_BILLS);
+            if (!oneToSeven) {
+                return 0;
+            }
+            //100-700章固定节点的所有子节点
+            let allChildren = [];
+            function getChildren(nodes) {
+                allChildren = allChildren.concat(nodes);
+                for (let node of nodes) {
+                    if (node.children.length > 0) {
+                        getChildren(node.children);
+                    }
+                }
+            }
+            getChildren(oneToSeven.children);
+            //扣除的节点:100章的节点[100-200)
+            let deductNodes = allChildren.filter(cbTools.withingOneHundred);
+            //计算金额
+            let fullFeeField = tender ? 'common.tenderTotalFee' : 'common.totalFee';
+            return projectObj.project.calcProgram.getTotalFee([oneToSeven], deductNodes, fullFeeField).toDecimal(decimalObj.bills.totalPrice);
+        }
+    };
+}
+
+// CommonJS module
+if (typeof module !== 'undefined' && !module.nodeType) { // 防止module是前端的一个html标签
+    module.exports = {
+        progression,
+        deficiency
+    };
+}

+ 12 - 29
web/over_write/js/anhui_2019.js

@@ -96,7 +96,7 @@ if (typeof baseFigureMap !== 'undefined') {
         // 显示:仅清单固定类别是“安全生产费”的可显示。
         '建筑安装工程费(不含设备费)': {
             base: 'JZAZGCFBHSB',
-            fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            fixedFlag: null,
             filter: [fixedFlag.SAFE_COST],
             pick: true
         },
@@ -110,7 +110,7 @@ if (typeof baseFigureMap !== 'undefined') {
         // 显示:仅清单固定类别是“施工场地建设费”的可显示。
         '定额建筑安装工程费(不含定额设备购置费及专项管理费)': {
             base: 'DEJZAZGCFBHSBZXGLF',
-            fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            fixedFlag: null,
             filter: [fixedFlag.CONSTRUCTION_PLANT_COST],
             pick: true,
         },
@@ -230,53 +230,38 @@ if (typeof baseFigureTemplate !== 'undefined') {
     baseFigureTemplate.budget = {
         // 建筑安装工程费 算法:取清单固定类别是“建筑安装工程费”的金额。
         JZAZGCF(tender) {
-            const feeField = 'common';
-            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(fixedFlag.CONSTRUCTION_INSTALL_FEE, feeField, subFeeField);
+            return cbTools.getBaseFee(fixedFlag.CONSTRUCTION_INSTALL_FEE, tender, 'common');
         },
         // 建筑安装工程费(不含安全生产费) 算法:取清单固定类别是“建筑安装工程费”的金额,扣除“安全生产费”的金额。
         JZAZGCFBHSC(tender) {
-            const fullFeeField = tender ? 'common.tenderTotalFee' : 'common.totalFee';
-            const deductFlags = [fixedFlag.SAFE_COST];
-            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, fullFeeField).toDecimal(decimalObj.bills.totalPrice);
+            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, [fixedFlag.SAFE_COST], tender, 'common');
         },
         // 建筑安装工程费(不含设备费) 算法:取清单固定类别是“建筑安装工程费”的金额,扣除设备的金额。
         JZAZGCFBHSB(tender) {
-            const fullFeeField = tender ? 'common.tenderTotalFee' : 'common.totalFee';
-            const deductFlags = [fixedFlag.EQUIPMENT_ACQUISITION_FEE];
-            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, fullFeeField).toDecimal(decimalObj.bills.totalPrice);
+            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, [fixedFlag.EQUIPMENT_ACQUISITION_FEE], tender, 'common');
         },
         // 定额建筑安装工程费 算法:取清单固定类别是“建筑安装工程费”的定额建安费。
         DEJZAZGCF(tender) {
-            const feeField = 'rationCommon';
-            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(fixedFlag.CONSTRUCTION_INSTALL_FEE, feeField, subFeeField);
+            return cbTools.getBaseFee(fixedFlag.CONSTRUCTION_INSTALL_FEE, tender, 'rationCommon');
         },
         // 定额建筑安装工程费(不含定额设备购置费及专项管理费) 算法:取清单固定类别是“建筑安装工程费”的“定额建安费”,扣除设备的“定额设备费”,扣除“专项费用”的“定额建安费”。
         DEJZAZGCFBHSBZXGLF(tender) {
-            const fullFeeField = tender ? 'rationCommon.tenderTotalFee' : 'rationCommon.totalFee';
-            const deductFlags = [fixedFlag.EQUIPMENT_ACQUISITION_FEE, fixedFlag.SAFE_COST];
+            const deductFlags = [fixedFlag.EQUIPMENT_ACQUISITION_FEE, fixedFlag.SPECIAL_COST];
             //建安费扣除设备费
-            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, fullFeeField).toDecimal(decimalObj.bills.totalPrice);
+            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, tender, 'rationCommon');
         },
         // 定额建筑安装工程费(不含专项管理费) 算法:取清单固定类别是“建筑安装工程费”的“定额建安费”,扣除“专项费用”的“定额建安费”。
         DEJZAZGCFBHZXGLF(tender) {
-            const fullFeeField = tender ? 'rationCommon.tenderTotalFee' : 'rationCommon.totalFee';
-            const deductFlags = [fixedFlag.SAFE_COST];
             //建安费扣除设备费
-            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, fullFeeField).toDecimal(decimalObj.bills.totalPrice);
+            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, [fixedFlag.SPECIAL_COST], tender, 'rationCommon');
         },
         // 土地使用及拆迁补偿费 算法:取清单固定类别是“土地使用及拆迁补偿费”的金额。
         TDSYJCQBCF(tender) {
-            const feeField = 'common';
-            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(fixedFlag.LAND_USED_DEMOLITION, feeField, subFeeField);
+            return cbTools.getBaseFee(fixedFlag.LAND_USED_DEMOLITION, tender, 'common');
         },
         // 工程建设其他费用 算法:取清单固定类别是“养护工程其他费用”的金额。
         GCJSQTFY(tender) {
-            const feeField = 'common';
-            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(fixedFlag.MAINTENANCE_EXPENSES, feeField, subFeeField);
+            return cbTools.getBaseFee(fixedFlag.MAINTENANCE_EXPENSES, tender, 'common');
         },
         // 施工场地建设费 算法:以{定额建筑安装工程费(不含定额设备购置费及专项管理费) }为基数,采用累进办法计算。
         SGCDJSF(tender) {
@@ -353,9 +338,7 @@ if (typeof baseFigureTemplate !== 'undefined') {
         //{各章清单合计}
         // 取清单固定类别是“第100章至700章清单”的金额
         'GZQDHJ': function (tender) {
-            let feeField = 'common',
-                subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, feeField, subFeeField);
+            return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'common');
         },
         //{专项暂定合计}
         // 汇总专项暂定列有值的清单的金额

+ 8 - 21
web/over_write/js/neimeng_2019.js

@@ -206,44 +206,33 @@ if (typeof baseFigureTemplate !== 'undefined') {
         // 定额建筑安装工程费(定额设备购置费按40%计):取清单固定类别是“建筑安装工程”的定额建安费,其中定额设备购置费按40%计算
         DEJZAZGCFSBSS(tender) {
             const feeField = 'rationCommon';
-            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
             const deductFlags = [fixedFlag.EQUIPMENT_ACQUISITION_FEE];
             //建安费扣除定额设备购置费
-            const afterDeductFee = cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, `${feeField}.${subFeeField}`);
+            const afterDeductFee = cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, tender, feeField, false);
             //定额设备购置费
-            const equipmentAcFee = cbTools.getBillsFee(deductFlags[0], feeField, subFeeField);
+            const equipmentAcFee = cbTools.getBaseFee(deductFlags[0], tender, feeField);
             return (afterDeductFee + equipmentAcFee * 0.4).toDecimal(decimalObj.bills.totalPrice);
         },
         // 建筑安装工程费(不含设备费):取清单固定类别是“建筑安装工程”的金额,但要扣除清单固定类别是“设备购置费”的金额
         JZAZGCFBHSB(tender) {
-            const fullFeeField = tender ? 'common.tenderTotalFee' : 'common.totalFee';
-            const deductFlags = [fixedFlag.EQUIPMENT_ACQUISITION_FEE];
             //建安费扣除设备费
-            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, fullFeeField).toDecimal(decimalObj.bills.totalPrice);
+            return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, [fixedFlag.EQUIPMENT_ACQUISITION_FEE], tender, 'common');
         },
         // 建筑安装工程费:取清单固定类别是“建筑安装工程”的金额
         JZAZGCF(tender) {
-            const feeField = 'common';
-            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(fixedFlag.CONSTRUCTION_INSTALL_FEE, feeField, subFeeField);
+            return cbTools.getBaseFee(fixedFlag.CONSTRUCTION_INSTALL_FEE, tender, 'common');
         },
         // 土地使用及拆迁补偿费:取清单固定类别是“土地使用及拆迁补偿费”的金额
         TDSYJCQBCF(tender) {
-            const feeField = 'common';
-            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(fixedFlag.LAND_USED_DEMOLITION, feeField, subFeeField);
+            return cbTools.getBaseFee(fixedFlag.LAND_USED_DEMOLITION, tender, 'common');
         },
         // 养护工程其他费用:取清单固定类别是“养护工程其他费用”的金额
         YHGCQTFY(tender) {
-            const feeField = 'common';
-            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(fixedFlag.MAINTENANCE_EXPENSES, feeField, subFeeField);
+            return cbTools.getBaseFee(fixedFlag.MAINTENANCE_EXPENSES, tender, 'common');
         },
         // 预备费:取清单固定类别是“预备费”的金额
         YBF(tender) {
-            const feeField = 'common';
-            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(fixedFlag.BUDGET_FEE, feeField, subFeeField);
+            return cbTools.getBaseFee(fixedFlag.BUDGET_FEE, tender, 'common');
         },
         // 养护单位项目管理费:以累进办法计算,计算基数为“定额建筑安装工程费(定额设备购置费按40%计)
         YHDWXMGLF(tender) {
@@ -304,9 +293,7 @@ if (typeof baseFigureTemplate !== 'undefined') {
         //{各章清单合计}
         // 取清单固定类别是“第100章至700章清单”的金额
         'GZQDHJ': function (tender) {
-            let feeField = 'common',
-                subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, feeField, subFeeField);
+            return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'common');
         },
         //{专项暂定合计}
         // 汇总专项暂定列有值的清单的金额

+ 5 - 15
web/over_write/js/zhejiang_2005.js

@@ -126,21 +126,15 @@ if (typeof baseFigureTemplate !== 'undefined') {
     baseFigureTemplate.budget = {
         // 公路养护工程费:取清单固定类别是“建筑安装工程费”的金额
         GLYHGCF(tender) {
-            const feeField = 'common';
-            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(calcBase.fixedFlag.CONSTRUCTION_INSTALL_FEE, feeField, subFeeField);
+            return cbTools.getBaseFee(calcBase.fixedFlag.CONSTRUCTION_INSTALL_FEE, tender, 'common');
         },
         // 设备购置费用:取清单固定类别是“设备购置费”的金额
         SBGZFY(tender) {
-            const feeField = 'common';
-            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(calcBase.fixedFlag.EQUIPMENT_ACQUISITION_FEE, feeField, subFeeField);
+            return cbTools.getBaseFee(calcBase.fixedFlag.EQUIPMENT_ACQUISITION_FEE, tender, 'common');
         },
         // 公路养护工程其他费用:取清单固定类别是“养护工程其他费用”的金额。
         GLYHGCQTFY(tender) {
-            const feeField = 'common';
-            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(calcBase.fixedFlag.MAINTENANCE_EXPENSES, feeField, subFeeField);
+            return cbTools.getBaseFee(calcBase.fixedFlag.MAINTENANCE_EXPENSES, tender, 'common');
         },
         // 养护工程管理经费:取清单固定类别是“建筑安装工程费”金额为基数,采用累进办法计算
         YHGCGLJF(tender) {
@@ -152,9 +146,7 @@ if (typeof baseFigureTemplate !== 'undefined') {
         },
         // 一二三部分合计:取清单固定类别是“一二三部分合计”的金额
         YESBFHJ(tender) {
-            const feeField = 'common';
-            const subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(calcBase.fixedFlag.ONE_TO_THREE_TOTAL, feeField, subFeeField);
+            return cbTools.getBaseFee(calcBase.fixedFlag.ONE_TO_THREE_TOTAL, tender, 'common');
         },
         // 工程造价增涨预留费:以{公路养护工程费}为基数,= P * [(1+i)^(n-1) -1]
         // P:公路养护工程费总额(元)
@@ -178,9 +170,7 @@ if (typeof baseFigureTemplate !== 'undefined') {
         //{各章清单合计}
         // 取清单固定类别是“第100章至700章清单”的金额
         'GZQDHJ': function (tender) {
-            let feeField = 'common',
-                subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            return cbTools.getBillsFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, feeField, subFeeField);
+            return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'common');
         },
         //{专项暂定合计}
         // 汇总专项暂定列有值的清单的金额