Browse Source

Merge branch 'master' of http://smartcost.f3322.net:3000/SmartCost/YangHuCost

chenshilong 6 năm trước cách đây
mục cha
commit
60cab2d663
36 tập tin đã thay đổi với 687 bổ sung448 xóa
  1. 2 2
      config/config.js
  2. 3 1
      modules/common/const/bills_fixed.js
  3. 2 2
      modules/main/facade/ration_facade.js
  4. 30 0
      modules/pm/controllers/new_proj_controller.js
  5. 19 1
      modules/pm/facade/pm_facade.js
  6. 2 2
      modules/pm/models/project_property_template.js
  7. 31 12
      modules/ration_glj/facade/glj_calculate_facade.js
  8. 21 12
      modules/reports/rpt_component/jpc_cross_tab.js
  9. 1 1
      public/web/gljUtil.js
  10. 7 1
      public/web/id_tree.js
  11. 1 1
      public/web/rpt_value_define.js
  12. 22 7
      public/web/sheet/sheet_common.js
  13. 14 29
      test/unit/reports/test_rpt_test_template.js
  14. 10 10
      web/building_saas/main/html/main.html
  15. 2 2
      web/building_saas/main/js/controllers/block_controller.js
  16. 8 0
      web/building_saas/main/js/models/cache_tree.js
  17. 281 144
      web/building_saas/main/js/models/calc_base.js
  18. 15 1
      web/building_saas/main/js/models/calc_program.js
  19. 6 0
      web/building_saas/main/js/models/fee_rate.js
  20. 3 1
      web/building_saas/main/js/models/main_consts.js
  21. 12 65
      web/building_saas/main/js/views/calc_base_view.js
  22. 15 2
      web/building_saas/main/js/views/calc_program_manage.js
  23. 15 2
      web/building_saas/main/js/views/fee_rate_view.js
  24. 2 2
      web/building_saas/main/js/views/glj_col.js
  25. 12 10
      web/building_saas/main/js/views/glj_view.js
  26. 13 10
      web/building_saas/main/js/views/main_tree_col.js
  27. 3 7
      web/building_saas/main/js/views/project_property_basicInfo.js
  28. 32 71
      web/building_saas/main/js/views/project_view.js
  29. 7 2
      web/building_saas/main/js/views/quantity_edit_view.js
  30. 0 2
      web/building_saas/main/js/views/sub_view.js
  31. 13 18
      web/building_saas/pm/js/pm_newMain.js
  32. 15 12
      web/building_saas/pm/js/pm_share.js
  33. 44 8
      web/building_saas/report/html/rpt_print.html
  34. 4 1
      web/building_saas/report/js/jpc_output.js
  35. 7 3
      web/building_saas/report/js/rpt_main.js
  36. 13 4
      web/building_saas/report/js/rpt_print.js

+ 2 - 2
config/config.js

@@ -39,8 +39,8 @@ module.exports = {
             connectTimeoutMS: 50000,
             useMongoClient: true
         }},
-    ab:{  server: "112.74.42.187",
-        port: "27017",
+    prod_s:{  server: "112.74.42.187",
+        port: "28066",
         options:{
             user:'smartcost',
             pass:'SmartCost3850888',

+ 3 - 1
modules/common/const/bills_fixed.js

@@ -55,7 +55,9 @@ const fixedFlag = {
     //暂列金额
     PROVISIONAL:26,
     //安全生产费
-    SAFE_COST:27
+    SAFE_COST:27,
+    //100章清单
+    ONE_HUNDRED_BILLS: 28
 };
 
 export default fixedFlag;

+ 2 - 2
modules/main/facade/ration_facade.js

@@ -620,10 +620,10 @@ async function CalculateQuantity (ration,billsItemID,projectID) {
     let decimalObject =await decimal_facade.getProjectDecimal(projectID,project);
     let quantity_decimal = (decimalObject&&decimalObject.ration&&decimalObject.ration.quantity)?decimalObject.ration.quantity:3;
     let pbill = await bill_model.model.findOne({projectID:projectID,ID:billsItemID});
-    let  t_unit = ration.unit?ration.unit.replace(/^\d+/,""):"";
+   /* let  t_unit = ration.unit?ration.unit.replace(/^\d+/,""):""; 2019-02-01 养护去掉清单与定额单位的这个判断
     if(t_unit!=pbill.unit){//如果定额工程量的单位去除前面的数字后不等于清单单位,定额工程量保持不变
         return ;
-    }
+    }*/
     let billsQuantity = pbill.quantity ? pbill.quantity : 0;
     let bill_decimal = await decimal_facade.getBillsQuantityDecimal(projectID,pbill.unit,project);
     billsQuantity=scMathUtil.roundForObj(billsQuantity,bill_decimal);

+ 30 - 0
modules/pm/controllers/new_proj_controller.js

@@ -15,6 +15,31 @@ import EngineeringLibModel from "../../users/models/engineering_lib_model";
 
 module.exports = {
     copyTemplateData: async function (property, newProjID, callback) {
+        //转换ID引用,原本@ID ID为原ID,需要替换为新的uuid
+        function parseCalcBase(calcBase, uuidMapping) {
+            let rst = '';
+            let reg = /@\d+/g,
+                numberData = Array.from(new Set(calcBase.match(reg))); //eg: @1
+            let regForOpr = /[\+,\-,\*,\/]/g,
+                oprData = calcBase.match(regForOpr); //eg: +
+            let regForID = /\d+/g;
+            let uuidArr = [];
+            for (let data of numberData) {
+                let orgID = data.match(regForID);
+                if (orgID && orgID[0] && uuidMapping[orgID[0]]) {
+                    uuidArr.push(uuidMapping[orgID[0]]);
+                }
+            }
+            for (let i = 0; i < uuidArr.length; i++) {
+                let uid = uuidArr[i],
+                    opr = oprData[i - 1];
+                if (opr) {
+                    rst += opr;
+                }
+                rst += `@${uid}`;
+            }
+            return rst;
+        }
         async.parallel([
             async function (cb) {
                 // 获取清单模板数据
@@ -27,11 +52,16 @@ module.exports = {
                 for(let bill of billsDatas){
                     uuidMaping[bill.ID] = uuidV1();
                 }
+                let needParseReg = /@/g;
                 billsDatas.forEach(function (template) {
                     template.projectID = newProjID;
                     template.ID = uuidMaping[template.ID] ? uuidMaping[template.ID] : -1;
                     template.ParentID = uuidMaping[template.ParentID] ? uuidMaping[template.ParentID] : -1;
                     template.NextSiblingID = uuidMaping[template.NextSiblingID] ? uuidMaping[template.NextSiblingID] : -1;
+                    //需要转换ID引用
+                    if (template.calcBase && needParseReg.test(template.calcBase)) {
+                        template.calcBase = parseCalcBase(template.calcBase, uuidMaping);
+                    }
                 });
                 billsData.insertData(billsDatas, callback);
             },

+ 19 - 1
modules/pm/facade/pm_facade.js

@@ -25,7 +25,8 @@ module.exports={
     isFirst: isFirst,
     getShareInfo: getShareInfo,
     prepareInitialData: prepareInitialData,
-    changeFile:changeFile
+    changeFile:changeFile,
+    copyForSectionError: copyForSectionError,
 };
 
 
@@ -903,6 +904,23 @@ async function updateUsedList(userId, compilation) {
 async function copyCompleRationSection(userId, compilationId) {
     await sectionTreeDao.copyDataFromTemplate(userId, compilationId);
 }
+
+/*
+ * 解决问题:
+ * 发布了编办之后,才设置了人材机库模板,导致已登录的用户不存在人材机库的分类树
+ * 为该编办下,人材机分类模板为空的用户,拷贝模板数据
+ * */
+async function copyForSectionError(compilation) {
+    let users = await userModel.find({}, '_id');
+    for (let user of users) {
+        let userId = user._id.toString();
+        let existCount = await compleGljSectionModel.find({userId}).count();
+        if (existCount === 0) {
+            await copyCompleGljSection(userId, compilation);
+        }
+    }
+}
+
 //拷贝补充人材机分类树
 async function copyCompleGljSection(userId, compilationId) {
     let templateData = await compleGljSectionTModel.find({compilationId: compilationId});

+ 2 - 2
modules/pm/models/project_property_template.js

@@ -5,8 +5,8 @@
 //默认的小数位数,用于定义用户可编辑的字段(入库),用户不可编辑的字段在前端defaultDecima._def中定义即可
 const defaultDecimal = {
     bills: {unitPrice: 2, totalPrice: 0},
-    ration: {quantity: 4, unitPrice: 2, totalPrice: 0},
-    glj: {quantity: 4, unitPriceHasMix: 2, unitPrice: 3},
+    ration: {quantity: 3, unitPrice: 2, totalPrice: 0},
+    glj: {quantity: 3, unitPriceHasMix: 2, unitPrice: 3},
     feeRate: 3,
     quantity_detail: 4,
     material:5,//三材系数

+ 31 - 12
modules/ration_glj/facade/glj_calculate_facade.js

@@ -66,7 +66,7 @@ async function calculateQuantity(query,noNeedCal,refreshRationName = false){
              }
          }
          for(let glj of gljList){//先把混凝土,砂浆,配合比有自定义消耗的挑出来
-             if(gljUtil.isConcreteType(glj.type)&& !noCustomQuantiyt(glj)) await getMixRatioMap(glj,mixRatioMap);
+             if(gljUtil.isConcreteType(glj.type)&& !noCustomQuantiyt(glj)) await getMixRatioMap(glj,gljList,coeList,assList,mixRatioMap);
          }
 
         gljList = sortRationGLJ(gljList);
@@ -140,11 +140,11 @@ function sortRationGLJ(list) {
     return list;
 }
 
-function sortBeforeCalc(list) {
-    list = _.sortByAll(list, [function (item) {
-       return gljUtil.isConcreteType(item.type)?1:2;
-    }]);
-    return list;
+async function calcWhenNoCustomQuantiyt(decimal,glj,gljList,coeList,assList) {
+    let quantity = glj.rationItemQuantity ? scMathUtil.roundTo(parseFloat(glj.rationItemQuantity),-decimal):0;
+    quantity =scMathUtil.roundTo(await calculateAss(quantity,assList,glj),-decimal);
+    quantity = calculateQuantityByCoes(quantity,coeList,glj,gljList,decimal);
+    return quantity;
 }
 
 
@@ -164,9 +164,11 @@ async function calculateQuantityPerGLJ(glj,gljList,coeList,assList,adjustState,m
     try {
         if(noNeedCal==null){//计算顺序:辅助定额,附注条件,自定义消耗(如果有就不用前计算两项),自定义乘系数
             if(noCustomQuantiyt(glj)){
-                quantity =scMathUtil.roundTo(parseFloat(glj.rationItemQuantity),-decimal);
-                quantity =scMathUtil.roundTo(await calculateAss(quantity,assList,glj),-decimal);
-                quantity = calculateQuantityByCoes(quantity,coeList,glj,gljList,decimal);
+                quantity = await calcWhenNoCustomQuantiyt(decimal,glj,gljList,coeList,assList);
+                let mIndex = gljUtil.getIndex(glj);
+                if(mixRatioMap[mIndex]){//组成物
+                    quantity = calcQuantityForMix(quantity,mixRatioMap[mIndex],decimal);
+                }
             }else {//对于有自定义消耗量的,并且是混凝土,浆砂,配合比 这三种类型,其组成物的消耗量要跟据自定义消耗量做相应的换算
                 quantity = glj.customQuantity;
                 result.doc.customQuantity = glj.customQuantity;
@@ -176,6 +178,7 @@ async function calculateQuantityPerGLJ(glj,gljList,coeList,assList,adjustState,m
                 quantity = scMathUtil.roundToString(quantity,decimal);
                 quantity = calculateQuantityByCustomerCoes(quantity,customerCoe,glj,decimal);
             }
+            if(quantity < 0) quantity = 0;
             result.doc.quantity =scMathUtil.roundToString(quantity,decimal);
             //2019-01-03 需求修改中间过程的价格不参与计算
             //glj.quantity = quantity;//这里保存中间过程计算出来的消耗量,后面处理“+*”操作符时要用到
@@ -187,16 +190,32 @@ async function calculateQuantityPerGLJ(glj,gljList,coeList,assList,adjustState,m
     }
 }
 
-async function getMixRatioMap(glj,mixRatioMap) {//生成组成物对应的父工料机的映射表
+function calcQuantityForMix(quantity,plist,decimal) {
+    for(let pglj of plist){
+        let consumption = scMathUtil.roundForObj(pglj.consumption,decimal);
+        let customQuantity = scMathUtil.roundForObj(pglj.customQuantity,decimal);
+        let temQuantity =  scMathUtil.roundForObj(consumption * pglj.assCoeQuantity,6);
+        let pcustomQuantity = scMathUtil.roundForObj(consumption * customQuantity,6);
+        quantity = scMathUtil.roundForObj(quantity - temQuantity,6);
+        quantity = scMathUtil.roundForObj(quantity + pcustomQuantity,decimal);
+    }
+    return quantity;
+}
+
+
+async function getMixRatioMap(glj,gljList,coeList,assList,mixRatioMap) {//生成组成物对应的父工料机的映射表
+    let decimalObject = await decimal_facade.getProjectDecimal(glj.projectID);
+    let decimal = (decimalObject&&decimalObject.glj&&decimalObject.glj.quantity)?decimalObject.glj.quantity:3;
+    let assCoeQuantity = await calcWhenNoCustomQuantiyt(decimal,glj,gljList,coeList,assList);//计算要用父工料机经过辅助子目换算后的量
     let unitPriceFileId = await ProjectModel.getUnitPriceFileId(glj.projectID);
     let connect_key = gljUtil.getIndex(glj);
     let mixList =  await mixRatioModel.find({"unit_price_file_id":unitPriceFileId,'connect_key':connect_key})
     for(let m of mixList){
         let mkey = gljUtil.getIndex(m);
         if(mixRatioMap[mkey]){
-            mixRatioMap[mkey].push({customQuantity:glj.customQuantity,rationItemQuantity:glj.rationItemQuantity,consumption:m.consumption});
+            mixRatioMap[mkey].push({customQuantity:glj.customQuantity,assCoeQuantity:assCoeQuantity,consumption:m.consumption});
         }else {
-            mixRatioMap[mkey] = [{customQuantity:glj.customQuantity,rationItemQuantity:glj.rationItemQuantity,consumption:m.consumption}];
+            mixRatioMap[mkey] = [{customQuantity:glj.customQuantity,assCoeQuantity:assCoeQuantity,consumption:m.consumption}];
         }
     }
 }

+ 21 - 12
modules/reports/rpt_component/jpc_cross_tab.js

@@ -109,7 +109,7 @@ JpcCrossTabSrv.prototype.createNew = function(){
             let sIDX = 0;
             //1. prepare and sort by tab-field
             let fields = [];
-            JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, tab[JV.TAB_CROSS_FIELDS], fields, rstFieldsIdx);
+            JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, tab[JV.PROP_CROSS_FIELDS], fields, rstFieldsIdx);
             let data_details = dataObj[JV.DATA_DETAIL_DATA];
             JpcCrossTabHelper.sortTabFields(fields, rstFieldsIdx, data_details, dataSeq);
             //2. distinguish sort tab fields value
@@ -148,7 +148,7 @@ JpcCrossTabSrv.prototype.createNew = function(){
         let result = [];
         let tab = rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_CONTENT];
         if (tab) {
-            JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, tab[JV.TAB_CROSS_FIELDS], null, rstFieldsIdx);
+            JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, tab[JV.PROP_CROSS_FIELDS], null, rstFieldsIdx);
         }
         for (let i = 0; i < rowSeq.length; i++) {
             let rl = rowSeq[i], cl = colSeq[i];
@@ -201,7 +201,7 @@ JpcCrossTabSrv.prototype.createNew = function(){
         me.sortedRowSequence = private_SortAndOptimize(rptTpl, dataObj, dataSeq, JV.NODE_CROSS_ROW, me.row_fields_idx);
         me.sortedColSequence = private_SortAndOptimize(rptTpl, dataObj, dataSeq, JV.NODE_CROSS_COL, me.col_fields_idx);
         me.sortedContentSequence = private_SortForDisplayContent(rptTpl, me.sortedRowSequence, me.sortedColSequence, me.content_fields_idx);
-        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_COL_SUM][JV.TAB_CROSS_FIELDS], null, me.col_sum_fields_idx);
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_COL_SUM][JV.PROP_CROSS_FIELDS], null, me.col_sum_fields_idx);
         //pre-sum the data(for col sum display)
         let data_details = dataObj[JV.DATA_DETAIL_DATA],
             data_fields = [];
@@ -240,8 +240,8 @@ JpcCrossTabSrv.prototype.createNew = function(){
             maxRowRec = JpcCrossTabHelper.getMaxRowsPerPage(bands, rptTpl);
             maxColRec = JpcCrossTabHelper.getMaxColsPerPage(bands, rptTpl);
         }
-        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_EXT][JV.TAB_CROSS_FIELDS], null, me.row_extension_fields_idx);
-        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_SUM_EXT][JV.TAB_CROSS_FIELDS], null, me.row_sum_extension_fields_idx);
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_EXT][JV.PROP_CROSS_FIELDS], null, me.row_extension_fields_idx);
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_SUM_EXT][JV.PROP_CROSS_FIELDS], null, me.row_sum_extension_fields_idx);
         if (me.paging_option === JV.PAGING_OPTION_INFINITY) {
             /*
             rst = segCnt;
@@ -323,7 +323,10 @@ JpcCrossTabSrv.prototype.createNew = function(){
     };
     JpcCrossTabResult.outputAsPreviewPage = function (rptTpl, bands, controls, $CURRENT_RPT) {
         let me = this, rst = [];
-        //...
+        let pageStatus = [true, true, true, true, true, true, true, true];
+        me.pageStatusLst.push(pageStatus);
+        // JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_CONTENT][JV.PROP_FLOW_FIELDS], null, me.disp_fields_idx, false);
+        JpcBandHelper.setBandArea(bands, rptTpl, pageStatus, true, false);
         return rst;
     };
     JpcCrossTabResult.outputAsSimpleJSONPage = function(rptTpl, dataObj, page, bands, controls, $CURRENT_RPT, customizeCfg) {
@@ -384,6 +387,12 @@ JpcCrossTabSrv.prototype.createNew = function(){
         }
         return rst;
     };
+    JpcCrossTabResult.outputPreviewRowTab = function(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg) {
+        //
+    };
+    JpcCrossTabResult.outputPreviewColTab = function(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg) {
+        //
+    };
     JpcCrossTabResult.outputRowTab = function(rptTpl, dataObj, page, bands, unitFactor, controls, $CURRENT_RPT, customizeCfg) {
         let me = this, rst = [];
         let tab = rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW];
@@ -391,7 +400,7 @@ JpcCrossTabSrv.prototype.createNew = function(){
         if (band) {
             let pageStatus = me.pageStatusLst[page - 1];
             if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
-                let tab_fields = tab[JV.TAB_CROSS_FIELDS];
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS];
                 let data_details = dataObj[JV.DATA_DETAIL_DATA];
                 let valuesIdx = me.dispValueIdxLst_Row[page - 1];
                 let serialsIdx = me.dispSerialIdxLst_Row[page - 1];
@@ -431,7 +440,7 @@ JpcCrossTabSrv.prototype.createNew = function(){
         if (band) {
             let pageStatus = me.pageStatusLst[page - 1];
             if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
-                let tab_fields = tab[JV.TAB_CROSS_FIELDS];
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS];
                 let data_details = dataObj[JV.DATA_DETAIL_DATA];
                 let valuesIdx = me.dispValueIdxLst_Col[page - 1];
                 let flexiblePrecisionRefObj = null, flexibleRefField = null, precision_ref_data = null;
@@ -482,7 +491,7 @@ JpcCrossTabSrv.prototype.createNew = function(){
         if (band) {
             let pageStatus = me.pageStatusLst[page - 1];
             if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
-                let tab_fields = tab[JV.TAB_CROSS_FIELDS];
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS];
                 let data_details = dataObj[JV.DATA_DETAIL_DATA];
                 let contentValuesIdx = me.dispValueIdxLst_Content[page - 1];
                 let flexiblePrecisionRefObj = null, flexibleRefField = null, precision_ref_data = null;
@@ -524,7 +533,7 @@ JpcCrossTabSrv.prototype.createNew = function(){
         if (band) {
             let pageStatus = me.pageStatusLst[page - 1];
             if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
-                let tab_fields = tab[JV.TAB_CROSS_FIELDS];
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS];
                 for (let i = 0; i < me.dispSumValueLst_Col[page - 1].length; i++) {
                     if (me.dispSumValueLst_Col[page - 1][i] !== null) {
                         for (let j = 0; j < me.dispSumValueLst_Col[page - 1][i].length; j++) {
@@ -562,7 +571,7 @@ JpcCrossTabSrv.prototype.createNew = function(){
         if (band) {
             let pageStatus = me.pageStatusLst[page - 1];
             if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
-                let tab_fields = tab[JV.TAB_CROSS_FIELDS],
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS],
                     data_details = dataObj[JV.DATA_DETAIL_DATA],
                     valuesIdx = me.dispValueIdxLst_Col[page - 1];
                 for (let i = 0; i < me.row_extension_fields_idx.length; i++) {
@@ -598,7 +607,7 @@ JpcCrossTabSrv.prototype.createNew = function(){
         if (band) {
             let pageStatus = me.pageStatusLst[page - 1];
             if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true && pageStatus[JV.STATUS_CROSS_ROW_END] === true) {
-                let tab_fields = tab[JV.TAB_CROSS_FIELDS],
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS],
                     data_details = dataObj[JV.DATA_DETAIL_DATA],
                     data_fields = [];
                 for (let i = 0; i < me.row_sum_extension_fields_idx.length; i++) {

+ 1 - 1
public/web/gljUtil.js

@@ -182,7 +182,7 @@ let gljUtil = {
         let quantity_decimal = decimalObj.glj.quantity;
         let process_decimal = decimalObj.process;
         let priceCoe = this.isDef(tenderCoe)?tenderCoe:1;
-        if (this.notEditType.indexOf(glj.unit_price.type)!=-1&&glj.ratio_data.length>0) {//对于混凝土、配合比、砂浆、机械台班等有组成物的材料,价格需根据组成物计算得出。
+        if (!this.isConcreteType(glj.unit_price.type)&& this.notEditType.indexOf(glj.unit_price.type)!=-1&&glj.ratio_data.length>0) {//对于机械台班等有组成物的材料,价格需根据组成物计算得出(排除混凝土、配合比、砂浆这几个类型直接为0)
             let p =0;
             for(let ratio of glj.ratio_data){
                 let tem =  _.find(projectGLJDatas.gljList,{

+ 7 - 1
public/web/id_tree.js

@@ -277,7 +277,13 @@ var idTree = {
                     tools.addUpdateDataForNextSibling(data, this.preSibling, this.tree.setting.rootId);
                 }
                 tools.addUpdateDataForNextSibling(data, this.parent, this.getID());
-                data.push({type: 'update', data: this.tree.getDataTemplate(this.getID(), this.parent.getParentID(), this.parent.getNextSiblingID())});
+                let updateData = this.tree.getDataTemplate(this.getID(), this.parent.getParentID(), this.parent.getNextSiblingID());
+                //如果设置了专项暂定,则需要清空
+                if (this.data.specialProvisional) {
+                    this.data.specialProvisional = '';
+                }
+                updateData.specialProvisional = '';
+                data.push({type: 'update', data: updateData});
             }
             return data;
         };

+ 1 - 1
public/web/rpt_value_define.js

@@ -130,6 +130,7 @@ const JV = {
     PROP_DISCRETE_FIELDS: "discrete_field_s",
     PROP_FLOW_FIELDS: "flow_field_s",
     PROP_BILL_FIELDS: "bill_field_s",
+    PROP_CROSS_FIELDS: "cross_field_s",
     PROP_GROUP_FIELDS: "group_field_s", //用来分组的指标(如按清单、定额etc...)
     PROP_GROUP_LINES: "group_lines",    //显示分组行,因分组的特殊性,分组的数据当成流水数据一样(行高相同),group_lines里的每一条数据占用流水的一整行,里面再细分(指标/text)
     PROP_GROUP_SUM_KEYS: "SumKey_S",
@@ -177,7 +178,6 @@ const JV = {
     PROP_IS_ID: "isID",
     PROP_ID_SEQ: "IDSeq",
 
-    TAB_CROSS_FIELDS: "cross_field_s",
     TAB_FIELD_PROP_SORT: "Sort",
     TAB_FIELD_PROP_SORT_VAL_NOSORT: "no_sort",
     TAB_FIELD_PROP_SORT_VAL_ASC: "ascend",

+ 22 - 7
public/web/sheet/sheet_common.js

@@ -17,7 +17,6 @@ var sheetCommonObj = {
         spreadBook.options.showDragFillSmartTag = false;
         return spreadBook;
     },
-
     initSheet: function(sheet, setting, rowCount) {
         var me = this;
         var spreadNS = GC.Spread.Sheets;
@@ -109,7 +108,7 @@ var sheetCommonObj = {
             area.vAlign(GC.Spread.Sheets.VerticalAlign.center);
         }
     },
-    showData: function(sheet, setting, data,distTypeTree) {
+    showData: function(sheet, setting, data,distTypeTree,callback) {//这个callback是为了在showdata后还做了引起重画表格的操作,在callback里调用能提高效率
         var me = this, ch = GC.Spread.Sheets.SheetArea.viewport;
         sheet.suspendPaint();
         sheet.suspendEvent();
@@ -159,6 +158,7 @@ var sheetCommonObj = {
             }
         }
         this.lockCells(sheet,setting);
+        if(callback) callback();
         sheet.resumeEvent();
         sheet.resumePaint();
         //me.shieldAllCells(sheet);
@@ -192,6 +192,9 @@ var sheetCommonObj = {
             if(setting.header[col].cellType === "replaceButton"){
                 this.setReplaceButton(row,col,sheet,setting.header[col]);
             }
+            if(setting.header[col].cellType === "cusButton"){
+                this.setCusButton(row,col,sheet,setting);
+            }
 
             if(setting.header[col].cellType === "tipsCell"){
                 this.setTipsCell(row,col,sheet,setting.header[col]);
@@ -544,6 +547,18 @@ var sheetCommonObj = {
         };
         return new selectButton();
     },
+    setCusButton:function (row,col,sheet,setting) {
+        let functionName = setting.header[col].callback;
+        let readOnly = setting.disable[setting.header[col].disable];
+        if(typeof(readOnly) == 'function' ){
+            readOnly = readOnly(row,col);
+        }
+        if(functionName){
+            sheet.setCellType(row, col,this.getCusButtonCellType(setting.callback[functionName],readOnly));
+        }
+        //sheet.setCellType(row, col,this.getSelectButton(header.headerWidth),GC.Spread.Sheets.SheetArea.viewport);
+    },
+
     getCusButtonCellType:function (callback,readOnly=false) {
         var ns = GC.Spread.Sheets;
         function CusButtonCellType() {
@@ -558,8 +573,10 @@ var sheetCommonObj = {
                     var imageWidth = 25;
                     var imageX = x + w - imageWidth- imageMagin, imageY = y + h / 2 - imageHeight / 2;
                     ctx.save();
-                    ctx.fillStyle = style.backColor;
-                    ctx.fillRect(x,y,w,h);
+                    if(style.backColor){
+                        ctx.fillStyle = style.backColor;
+                        ctx.fillRect(x,y,w,h);
+                    }
                     ctx.drawImage(image, imageX, imageY,imageWidth,imageHeight);
                     ctx.beginPath();
                     ctx.arc(imageX+imageWidth/2,imageY+imageHeight/2,1,0,360,false);
@@ -569,9 +586,9 @@ var sheetCommonObj = {
                     ctx.fill();//画实心圆
                     ctx.closePath();
                     ctx.restore();
+                    w = w - imageWidth - imageMagin;
                     //这里的左对齐的,当显示的字长度超过空白地方时,要改成右对齐
                     if(style.hAlign == 0){
-                        w = w - imageWidth - imageMagin;
                         if(value){
                             let textWidth = ctx.measureText(value).width;
                             let spaceWidth = w;
@@ -579,8 +596,6 @@ var sheetCommonObj = {
                                 style.hAlign = 2;
                             }
                         }
-                    }else if(style.hAlign == 2){//如果是右对齐的,往左边挪,给按钮留出空间
-                        x = x - imageWidth - imageMagin;
                     }
 
                 }

+ 14 - 29
test/unit/reports/test_rpt_test_template.js

@@ -26,41 +26,24 @@ cfgCacheUtil.setupDftCache();
 let fsUtil = require("../../../public/fsUtil");
 
 let demoPrjId = - 1;
-// let demoRptId = 279;
-// let demoRptId = 275; //测试模板-流水式
-// let demoRptId = 337; //19表
-// let demoRptId = 361; //封1
-// let demoRptId = 279; //表04
-// let demoRptId = 448; //封3
-let demoRptId = 450; //09
-// let demoRptId = 704; //05
-// let demoRptId = 626; //06
-// let demoRptId = 451; //09-1
-// let demoRptId = 452; //04
-// let demoRptId = 612; //09-3
-// let demoRptId = 530; //封2
-// let demoRptId = 386; //04
-// let demoRptId = 389; //10
-// let demoRptId = 280; //11-1 暂列金
-// let demoRptId = 514; //12
-// let demoRptId = 2260; //测试基本信息
-// let demoRptId = 613; //09-4 2018
-// let demoRptId = 726; //定制:清单子目表
+// let demoRptId = 38; //5.2.2表
+let demoRptId = 6; //封面
+
 let pagesize = "A4";
 //288: 11-2表(新)
 //279: 04
 
-// let userId_Leng = "59cdf14a0034a1000ba52b97"; //小冷User Id 换成_id了 QQ号
-// let userId_Leng = "5acac1e885bf55000bd055ba"; //小冷User Id2
+let userId_Leng = "5c3ffa9aa0a92732f41216e0"; //小冷User Id (养护的)
 // let userId_me = "5b6a60b1c4ba33000dd417c0"; //我的
-let userId_HaiZhu = "5b5a66c4a3c23e000dccdd77"; //海珠user id
+// let userId_HaiZhu = "5b5a66c4a3c23e000dccdd77"; //海珠user id
 // demoPrjId = 720; //QA: DW3
 //demoPrjId = 1626; //QA:
 // demoPrjId = 2260; //QA:
-demoPrjId = 4529; //QA:
+// demoPrjId = 410; //QA:
+demoPrjId = 313; //PROD:
 // demoPrjId = 4107; //UAT:
 //*/
-let userId_Dft = userId_HaiZhu;
+let userId_Dft = userId_Leng;
 // let userId_Dft = "5a025c4c15074d134c2b9689";
 /*/
  let userId_Dft = "595328da1934dc327cad08eb";
@@ -84,15 +67,17 @@ test('测试 - 测试模板啦: ', function (t) {
         let rptDataUtil = new rptDataExtractor();
         rptDataUtil.initialize(rptTpl._doc);
         let filter = rptDataUtil.getDataRequestFilter();
+        // filter.push('ration');  //临时用
+        // filter.push('feeRate'); //临时用2
         console.log(filter);
         //正常应该根据报表模板定义的数据类型来请求数据
         rptTplDataFacade.prepareProjectData(userId_Dft, demoPrjId, filter, function (err, msg, rawDataObj) {
             if (!err) {
                 try {
-                    // fsUtil.writeObjToFile(rawDataObj, "D:/GitHome/ConstructionCost/tmp/rptTplRawDataObject_测试模板.jsp");
+                    fsUtil.writeObjToFile(rawDataObj, "D:/GitHome/YangHuCost/tmp/rptTplRawDataObject_测试模板.jsp");
                     let tplData = rptDataUtil.assembleData(rawDataObj);
-                    // fsUtil.writeObjToFile(rawDataObj, "D:/GitHome/ConstructionCost/tmp/rptTplRawDataAfterCacl_测试模板.jsp");
-                    // fsUtil.writeObjToFile(tplData, "D:/GitHome/ConstructionCost/tmp/rptTplAssembledData_测试模板.jsp");
+                    // fsUtil.writeObjToFile(rawDataObj, "D:/GitHome/YangHuCost/tmp/rptTplRawDataAfterCacl_测试模板.jsp");
+                    // fsUtil.writeObjToFile(tplData, "D:/GitHome/YangHuCost/tmp/rptTplAssembledData_测试模板.jsp");
                     //it's time to build the report!!!
                     let printCom = JpcEx.createNew();
                     rptTpl[JV.NODE_MAIN_INFO][JV.NODE_PAGE_INFO][JV.PROP_PAGE_SIZE] = pagesize;
@@ -104,7 +89,7 @@ test('测试 - 测试模板啦: ', function (t) {
                     let customizeCfg = {"fillZero": true};
                     let pageRst = printCom.outputAsSimpleJSONPageArray(rptTpl, tplData, 1, maxPages, defProperties, customizeCfg);
                     if (pageRst) {
-                        fsUtil.writeObjToFile(pageRst, "D:/GitHome/ConstructionCost/tmp/testBuiltPageResult_测试模板.jsp");
+                        // fsUtil.writeObjToFile(pageRst, "D:/GitHome/YangHuCost/tmp/testBuiltPageResult_测试模板.jsp");
                         // rpt_xl_util.exportExcel(pageRst, pagesize, "local_test_rpt_excel", true, null, null, function(uuidName){
                         //     console.log("excel uuid: " + uuidName);
                         // });

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

@@ -88,13 +88,13 @@
                       <span id="openProjSet" class="btn btn-light btn-sm" data-toggle="tooltip" data-original-title="项目属性" data-placement="bottom">
                         <a href="javascript:void(0);"><i class="fa fa-cog"></i></a>
                     </span>
-                    <span class="btn btn-light btn-sm" id="importSpan" data-toggle="tooltip" data-original-title="导入" data-placement="bottom">
+                    <!--<span class="btn btn-light btn-sm" id="importSpan" data-toggle="tooltip" data-original-title="导入" data-placement="bottom">
                         <a id="importDropDown" class="dropdown-toggle" href="#"><i class="fa fa-cloud-upload"></i></a>
                         <div class="dropdown-menu">
                             <a id="uploadLj" class="dropdown-item" href="#import" data-toggle="modal" data-target="#import">导入报表Excel清单</a>
                             <a id="uploadGld" class="dropdown-item" href="#import" data-toggle="modal" data-target="#import">导入广联达算量Excel清单</a>
                         </div>
-                    </span>
+                    </span>-->
                     <a href="javascript:void(0)" class="btn btn-light btn-sm" id="insertRation" data-toggle="tooltip" data-placement="bottom" data-original-title="插入定额"><i class="fa fa-sign-in" aria-hidden="true"></i></a>
                     <!--2018-11-15 隐藏删除按钮   <a href="javascript:void(0)" class="btn btn-light btn-sm" id="delete" data-toggle="tooltip" data-placement="bottom" data-original-title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>-->
                     <a href="javascript:void(0)" class="btn btn-light btn-sm" id="upLevel" data-toggle="tooltip" data-placement="bottom" data-original-title="升级"><i class="fa fa-arrow-left" aria-hidden="true"></i></a>
@@ -113,13 +113,13 @@
                                   <a class="dropdown-item btn-sm" href="javascript:void(0);"  id="displayZDCXM">最底层细目</a>
                                   <a class="dropdown-item btn-sm" href="javascript:void(0);"  id="displayDE">定额</a>
                               </div>
-                              <a href="javascript:void(0);" id="ZLFB_btn" class="dropdown-item" data-placement="bottom"><i class="fa fa-retweet" aria-hidden="true"></i> 整理分部</a>
+                              <!--<a href="javascript:void(0);" id="ZLFB_btn" class="dropdown-item" data-placement="bottom"><i class="fa fa-retweet" aria-hidden="true"></i> 整理分部</a>
                               <% if (projectData.property.lockBills == true) { %>
                               <a href="javascript:void(0)"  class="dropdown-item" name="lockBills"> <i class="fa fa-unlock-alt" aria-hidden="true"></i> 解锁清单</a>
                               <% } else { %>
                               <a href="javascript:void(0)"  class="dropdown-item" name="lockBills"> <i class="fa fa-lock" aria-hidden="true"></i> 锁定清单</a>
                               <% } %>
-                              <a id="switchTznr" href="javascript:void(0);"  class="dropdown-item"><i class="fa fa-eye" aria-hidden="true"></i> 显示特征</a>
+                              <a id="switchTznr" href="javascript:void(0);"  class="dropdown-item"><i class="fa fa-eye" aria-hidden="true"></i> 显示特征</a>-->
                               <a id = "menu_calc_program_manage"  href="javascript:void(0);" class="dropdown-item"><i class="fa fa-calculator" aria-hidden="true"></i> 总计算程序</a>
                           </div>
                       </div>
@@ -214,9 +214,9 @@
                                  <!-- <li class="nav-item">   2018-11-08  新需求,隐藏说明信息
                                       <a class="nav-link" data-toggle="tab" href="#comments" role="tab" id="linkComments">说明信息</a>
                                   </li>-->
-                                  <li class="nav-item" id="MBZM_div">
+                                  <!--<li class="nav-item" id="MBZM_div">
                                       <a class="nav-link sub-item" id="linkMBZM" data-toggle="tab" href="#subSpread" role="tab">模板子目</a>
-                                  </li>
+                                  </li>-->
                               </ul>
                               <!-- Tab panes -->
                               <div class="tab-content" id="tabCon">
@@ -631,8 +631,8 @@
                     <div class="row">
                         <div class="col-3">
                             <ul class="nav flex-column nav-pills" role="tablist">
-                                <li class="nav-item"><a class="nav-link active" data-toggle="pill" href="#poj-settings-basicInfo" role="tab" id="tab_poj-settings-basicInfo">建设项目基本信息</a></li>
-                                <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-projFeature" id="tab_poj-settings-projFeature" role="tab">工程特征</a></li>
+                                <!--<li class="nav-item"><a class="nav-link active" data-toggle="pill" href="#poj-settings-basicInfo" role="tab" id="tab_poj-settings-basicInfo">建设项目基本信息</a></li>-->
+                                <li class="nav-item"><a class="nav-link active" data-toggle="pill" href="#poj-settings-projFeature" id="tab_poj-settings-projFeature" role="tab">工程特征</a></li>
                                 <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-indicativeInfo" id="tab_poj-settings-indicativeInfo" role="tab">指标信息</a></li>
                                 <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-4" id="about-calc" role="tab">关于计算</a></li>
                                 <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-billsQuanDecimal" id="tab_poj-settings-bqDecimal" role="tab">清单工程量精度</a></li>
@@ -645,12 +645,12 @@
                         <div class="col-9">
                             <div class="tab-content">
                                 <!--基本信息-->
-                                <div class="tab-pane fade show active" id="poj-settings-basicInfo" role="tabpanel">
+                                <div class="tab-pane fade" id="poj-settings-basicInfo" role="tabpanel">
                                     <div class="modal-auto-height" style="overflow: hidden;" id="basicInfoSpread">
                                     </div>
                                 </div>
                                 <!--工程特征-->
-                                <div class="tab-pane fade" id="poj-settings-projFeature" role="tabpanel">
+                                <div class="tab-pane fade show active" id="poj-settings-projFeature" role="tabpanel">
                                     <div class="modal-auto-height" style="overflow: hidden" id="projFeatureSpread">
                                     </div>
                                 </div>

+ 2 - 2
web/building_saas/main/js/controllers/block_controller.js

@@ -586,10 +586,10 @@ let BlockController = {
         let EXPString = ration.quantityEXP+"";
         if(EXPString.indexOf("QDL") != -1){
             if(EXPString=="QDL"){//定额的工程量是直接通过清单量填进来的;
-                let  t_unit = ration.unit?ration.unit.replace(/^\d+/,""):"";
+                /*let  t_unit = ration.unit?ration.unit.replace(/^\d+/,""):""; 2019-02-01 养护去掉清单与定额单位的这个判断
                 if(t_unit!=billsUnit){//如果定额的单位去除前面的数字后不等于清单单位,定额工程量保持不变
                     return ;
-                }
+                }*/
                 let times = parseInt(ration.unit);
                 if(isNaN(times)){
                     times = 1;

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

@@ -294,6 +294,14 @@ var cacheTree = {
             }
             return success;
         };
+        //获取节点的祖先节点(最顶父节点)
+        Node.prototype.getAncestor = function () {
+            let node = this;
+            while (node.parent) {
+                node = node.parent;
+            }
+            return node;
+        };
 
         var Tree = function (owner) {
             this.owner = owner;

+ 281 - 144
web/building_saas/main/js/models/calc_base.js

@@ -14,7 +14,7 @@ let cbTools = {
         return this.isDef(v) && !isNaN(v) && v !== Infinity;
     },
     isFlag: function (v) {
-        return this.isDef(v) && this.isDef(v.flagsIndex) && this.isDef(v.flagsIndex.fixed);
+        return this.isDef(v) && this.isDef(v.flagsIndex) && this.isDef(v.flagsIndex.fixed) && this.isDef(v.flagsIndex.fixed.flag);
     },
     returnV: function (v, r) {
         if(this.isDef(v)){
@@ -85,6 +85,7 @@ let cbTools = {
         if(this.isUnDef(exp) || exp === ''){
             return rst;
         }
+        let findChildNodes = [];//直接引用的节点,这些节点可能存在子节点,子节点才有公式,因此获取这些节点的子公式节点
         //获取表达式中的基数和行引用
         let figureF = cbParser.getFigureF(cbParser.getFigure(exp), cbParser.getUID(cbParser.getFIDArr(exp)));
         //首先提取出多处引用的进行排序
@@ -94,35 +95,32 @@ let cbTools = {
                 let bill = this.isDef(calcBase.baseFigures[figure.value]['fixedBill']) ? calcBase.baseFigures[figure.value]['fixedBill']['bill'] : null;
                 let figureMultiRef = calcBase.baseFigures[figure.value]['multiRef'];
                 if(this.isDef(figureMultiRef)){
-                    let findChildNodes = [];
                     for(let flag of figureMultiRef){
                         let refNode = this.findBill(flag) ? this.getNodeByID(this.findBill(flag).ID) : null;
-                        if(refNode){
+                        if(refNode && !ids.includes(refNode.data.ID)){
                             findChildNodes.push(refNode);
+                            ids.push(refNode.data.ID);
                         }
                     }
-                    let childrenNodes = calcTools.getChildrenFormulaNodes(node, formulaNodesArr, findChildNodes);
-                    for(let cNode of childrenNodes){
-                        ids.push(cNode.data.ID);
-                    }
-                    rst = rst.concat(childrenNodes);
-                }
-                else if(this.isDef(bill) && ids.indexOf(bill.ID) === -1){
+                } else if(this.isDef(bill) && ids.indexOf(bill.ID) === -1){
                     let node = this.getNodeByID(bill.ID);
-                    if(this.isDef(node)){
+                    if(this.isDef(node) && !ids.includes(node.data.ID)){
+                        findChildNodes.push(node);
                         ids.push(node.data.ID);
-                        rst.push(node);
                     }
                 }
-            }
-            else if(figure.type === 'id'){
+            } else if(figure.type === 'id'){
                 let node = this.getNodeByID(figure.value);
-                if(this.isDef(node) && ids.indexOf(node.data.ID) === -1){
+                if (this.isDef(node) && !ids.includes(node.data.ID)) {
+                    findChildNodes.push(node);
                     ids.push(node.data.ID);
-                    rst.push(node);
                 }
             }
         }
+        if (findChildNodes.length > 0) {
+            let childrenNodes = calcTools.getChildrenFormulaNodes(node, formulaNodesArr, findChildNodes);
+            rst = rst.concat(childrenNodes);
+        }
         return rst;
     },
     //需要用到计算基数的时候,先找出所有的固定清单,避免每个基数都要去遍历寻找清单
@@ -151,74 +149,125 @@ let cbTools = {
             }
         }
     },
-    //生成清单基数计算分类模板
-    setBaseFigureClass: function (baseFigures, mapObj) {
-        for(let figureClass in figureClassTemplate){
-            mapObj[figureClass] = Object.create(null);
-        }
-        let needFixedBillsClass = ['FBFX', 'CXSM', 'QTXM', 'GF', 'SJ'];
-        //不需要关联节点的、但是下挂在固定清单分类下的基数
-        let noneFixedBillsFigures = ['JZMJ'];
-        //安全文明施工专项费用只有税金和工程造价能用
-        for(let figure in baseFigures){
-            if(!noneFixedBillsFigures.includes(baseFigures[figure]['base'])){
-                //过滤相关清单固定行不存在的
-                if(needFixedBillsClass.includes(baseFigures[figure]['class']) && !baseFigures[figure]['fixedBill']){
-                    continue;
+    //设置清单固定行下可用的基数映射
+    //@param {Object}baseFigures(当前项目可用总基数配置表) {Object}mapping(可用基数映射,初始为空object,目标:{flag: Array(baseList)}) eg: {'1': ['xx费']}
+    setValidBaseMapping: function (baseFigures, mapping) {
+        //清单固定行数组[1, 2...]
+        let allFlags = [];
+        //清单固定行与子清单固定行映射
+        let subFlagMapping = {};
+        for (let attr in fixedFlag) {
+            let flag = fixedFlag[attr];
+            allFlags.push(flag);
+            let subFlagList = this.getSubFlagList(flag);
+            subFlagMapping[flag] = subFlagList;
+        }
+        for(let baseName in baseFigures) {
+            let calcBase = baseFigures[baseName],
+                filter = calcBase.filter,
+                pick = calcBase.pick; //挑选或过滤
+            if (!filter) {
+                continue;
+            }
+            //pick为true,则filter中的清单固定行可使用此基数(及其子清单固定行),
+            //pick为false除去filter中的清单固定行(及其子清单固定行),其他可使用此基数(包括新增的大项费用)
+            let allFilter = []; //filter及其子项
+            for (let flag of filter) {
+                if (subFlagMapping[flag].length > 0) {
+                    allFilter = allFilter.concat(subFlagMapping[flag]);
                 }
             }
-            for(let figureClass in figureClassTemplate){
-                let figureClassFilter = figureClassTemplate[figureClass]['filter'];
-                if(!figureClassFilter.includes(baseFigures[figure]['base'])){
-                    mapObj[figureClass][figure] = baseFigures[figure];
+            allFilter = allFilter.concat(filter);
+            allFilter = Array.from(new Set(allFilter));
+            //获取可使用此基数的清单固定行
+            let validFlags = pick ? allFilter : allFlags.filter(function (flag) {
+                return !allFilter.includes(flag);
+            });
+            //其他节点可使用的基数(新增的大项费用),即基数配置表中过滤条件为“只允许非固定类别是xxx”的
+            //允许非固定类别xx可用,则新增的大项费用也可用,新增的大项费用flag为null
+            if (!pick) {
+                if (mapping['other']) {
+                    mapping['other'].push(baseName);
+                } else {
+                    mapping['other'] = [baseName];
                 }
             }
-        }
-    },
-    getFigure: function (node) {
-        /*
-        * {专项暂定合计}需要特殊处理,专项暂定有值的节点不可用此基数,否则会引起循环计算
-        * */
-        if (node.data.specialProvisional) {
-            let filterMap = {};
-            for (let baseName in calcBase.baseFigures) {
-                if (baseName !== '专项暂定合计') {
-                    filterMap[baseName] = calcBase.baseFigures[baseName];
+            //设置清单固定行可使用此基数
+            for (let flag of validFlags) {
+                if (mapping[flag]) {
+                    mapping[flag].push(baseName)
+                } else {
+                    mapping[flag] = [baseName];
                 }
             }
-            return filterMap;
-        } else {
-            return calcBase.baseFigures;
         }
-
     },
-    getBaseBill: function (node) {
-        let calcBase = projectObj.project.calcBase;
-        let parent = node.parent;
-        if(this.isFlag(node.data) && (node.data.flagsIndex.fixed.flag === calcBase.fixedFlag.SUB_ENGINERRING
-            || node.data.flagsIndex.fixed.flag === calcBase.fixedFlag.CONSTRUCTION_TECH)){
-            return node;
-        }
-        else if(this.isFlag(node.data) && node.data.flagsIndex.fixed.flag === calcBase.fixedFlag.CONSTRUCTION_ORGANIZATION){
-            return node;
+    //该节点可使用的基数列表
+    getValidFigures: function (node) {
+        let filterMap = {},
+            avaBaseNames = [];
+        //该节点所属的固定行
+        let belongFlag = this.getBelongFlag(node);
+        //没有所属固定行,则属于新增的大项费用
+        //获取可使用的基数
+        if (!belongFlag) {
+            avaBaseNames = calcBase.flagValidBase['other'];
+        } else {
+            avaBaseNames = calcBase.flagValidBase[belongFlag] ? calcBase.flagValidBase[belongFlag] : [];
         }
-        else if(this.isFlag(node.data) && node.data.flagsIndex.fixed.flag === calcBase.fixedFlag.OTHER){
-            return node;
+        for (let baseName of avaBaseNames) {
+            let base = calcBase.baseFigures[baseName];
+            if (baseName) {
+                filterMap[baseName] = base;
+            }
         }
-        else if(this.isFlag(node.data) && node.data.flagsIndex.fixed.flag === calcBase.fixedFlag.CHARGE){
-            return node;
+       return filterMap
+
+    },
+    //根据清单固定行,获取子固定行
+    getSubFlagList: function (flag) {
+        let flagList = [];
+        let node = this.findNodeByFlag(flag);
+        if (!node) {
+            return flagList;
+        }
+        let allChildren = [];
+        function getChildren(nodes) {
+            allChildren = allChildren.concat(nodes);
+            for (let node of nodes) {
+                if (node.children.length > 0) {
+                    getChildren(node.children);
+                }
+            }
         }
-        else if(this.isFlag(node.data) && node.data.flagsIndex.fixed.flag === calcBase.fixedFlag.TAX){
-            return node;
+        getChildren(node.children);
+        for (let child of allChildren) {
+            if (child.data && this.isFlag(child.data)) {
+                flagList.push(child.data.flagsIndex.fixed.flag);
+            }
         }
-        else {
-            if(!parent){
-                return node;
+        return flagList
+    },
+    //获取节点所属的清单固定行
+    getBelongFlag: function (node) {
+        while (node) {
+            if (node.data && this.isFlag(node.data)) {
+                return node.data.flagsIndex.fixed.flag;
             }
-            else {
-                return this.getBaseBill(parent);
+            node = node.parent;
+        }
+        return null;
+    },
+    //获取节点所属的清单固定列表
+    getBelongFlagList: function (node) {
+        let rst = [];
+        while (node) {
+            if (node.data && this.isFlag(node.data)) {
+                rst.push(node.data.flagsIndex.fixed.flag);
             }
+            node = node.parent;
         }
+        return rst;
     },
     //获取清单(有基数计算)引用了的其他清单,(循环引用栈中的一块)
     getStackBlock: function (billID) {
@@ -237,7 +286,6 @@ let cbTools = {
                 if(bases[i]['type'] === 'base' && cbTools.isDef(calcBase.baseFigures[bases[i]['value']])){
                     let figureMultiRef= calcBase.baseFigures[bases[i]['value']]['multiRef'];
                     let cycleCalcRef = calcBase.baseFigures[bases[i]['value']]['cycleCalcRef'];
-                    //重构后:
                     if(cbTools.isDef(figureMultiRef)){
                         if(cbTools.isDef(cycleCalcRef)){
                             figureMultiRef = cycleCalcRef;
@@ -429,6 +477,17 @@ let cbTools = {
             fee += (baseFee - min) * within.feeRate * 0.01;
         }
         return fee.toDecimal(decimalObj.bills.totalPrice);
+    },
+    //获取清单100章下的节点(只需要找最底层的,排除了底层,父项金额即排除了子项)
+    //@param {Object}node(判断的节点,最底层清单节点)
+    //@return {Boolean}
+    withingOneHundred: function (node) {
+        if (!node || node.sourceType !== calcBase.project.Bills.getSourceType() || node.source.children.length > 0) {
+            return false;
+        }
+        //节点所属的清单固定行为第100章清单
+        let belongFlags = cbTools.getBelongFlagList(node);
+        return belongFlags.includes(fixedFlag.ONE_HUNDRED_BILLS);
     }
 };
 
@@ -445,13 +504,13 @@ let baseFigureTemplate = {
                 deductFlags = [fixedFlag.EQUIPMENT_ACQUISITION_FEE, fixedFlag.SPECIAL_COST];
             return cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, fullFeeField).toDecimal(decimalObj.bills.totalPrice);
         },
-        //{定额建筑安装工程(其中定额设备购置费按 40%计)} (定额建筑安装工程设备四十)
+        //{定额建筑安装工程(其中定额设备购置费按 40%计)} (不含专项费用) (定额建筑安装工程设备四十)
         //扣除设备购置费,再加上设备购置费的40%,扣除汇总算法不四舍五入,相当于汇总当中定额设备购置费就按照了40%计
         'DEJZAZGCSBSS': function (tender) {
             let feeField = 'rationCommon',
                 subFeeField = tender ? 'tenderTotalFee' : 'totalFee',
-                deductFlags = [fixedFlag.EQUIPMENT_ACQUISITION_FEE];
-            //建安费扣除定额设备购置费
+                deductFlags = [fixedFlag.EQUIPMENT_ACQUISITION_FEE, fixedFlag.SPECIAL_COST];
+            //建安费扣除定额设备购置费、专项费用
             let afterDeductFee = cbTools.getFeeWithDeduction(fixedFlag.CONSTRUCTION_INSTALL_FEE, deductFlags, `${feeField}.${subFeeField}`);
             //定额设备购置费
             let equipmentAcFee = cbTools.getBillsFee(deductFlags[0], feeField, subFeeField);
@@ -604,33 +663,9 @@ let baseFigureTemplate = {
             return rst.toDecimal(decimalObj.bills.totalPrice);
         },
         //{100章以外清单合计}
-        // 取清单固定类别是“第100章至700章清单”的金额,但扣除清单100章下的金额。
+        // 取清单固定清单[第100章至700章清单]的金额,但扣除清单100章下的金额。
+        // 如果是固定清单[第100章至700章清单]下100章以外清单引用此基数,要排除自身(目前只允许100章的清单使用,所以暂时不需要此判断)
         'YBZYHQDHJ': function (tender) {
-            //获取清单100章下的节点(只需要找最底层的,排除了底层,父项金额即排除了子项)
-            /*
-            * 是否是100章下的节点
-            * 判定规则:
-            *  1.递归往上找父项,直至找到一个其父项没有编码的节点(基准节点)
-            *  2.判断递归找到的节点编码规则:
-            *    ①其编码只有前三位是连续的数值 eg: 100s(属于) 101(属于) 10(不属于) 10001(不属于)
-            *    ②其前三位连续的数值编码在区间[100, 200)中
-            * */
-            function isWithin(node) {
-                if (!node || node.sourceType !== calcBase.project.Bills.getSourceType() || node.source.children.length > 0) {
-                    return false;
-                }
-                //找判断基准节点
-                while (node.parent && node.parent.data.code) {
-                    node = node.parent;
-                }
-                //判断编码规则
-                let reg = /^\d{3,}/;
-                if (cbTools.isUnDef(node.data.code)) {
-                    return false;
-                }
-                let matchCode = node.data.code.match(reg);
-                return matchCode && matchCode[0].length === 3 && matchCode[0] >= 100 && matchCode[0] < 200;
-            }
             let oneToSeven = cbTools.findNodeByFlag(fixedFlag.ONE_SEVEN_BILLS);
             if (!oneToSeven) {
                 return 0;
@@ -646,7 +681,8 @@ let baseFigureTemplate = {
                 }
             }
             getChildren(oneToSeven.children);
-            let deductNodes = allChildren.filter(isWithin);
+            //扣除的节点: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);
@@ -654,52 +690,151 @@ let baseFigureTemplate = {
     }
 };
 
-let figureClassTemplate = {
-};
-
-//基数的值不是通过清单节点获得的,则该基数的fixedBill为空,如价差、甲供、分包; class:分类,用于基数选择界面分类显示
+//基数的值不是通过直接引用某清单节点获得的,则该基数的fixedBill为空,如价差、甲供、分包; class:分类,用于基数选择界面分类显示
 //基数本身不与清单节点关联、但是其由与清单关联的节点四则运算得到,则拥有字段multiRef: [flags...]
+/*
+ * 基数的过滤filter ,根据这个配置最终可以转换成清单固定行可使用的相应基数
+ * 筛选和过滤由pick决定
+ * 控制基数可被哪些清单固定行下的节点使用 //挑选 pick === true
+ * 控制基数不可被哪些清单固定行下的节点使用 //过滤 pick === false
+ * */
+//暂时特殊处理专项费用需要引用{定额建筑安装工程费(其中定额设备购置费按40%计)}等跟专项费用父项有关联的基数:将fixedFlag设置为null
 let baseFigureMap = {
     /*
      * 预算项目
      * */
     'budget': {
-//与清单直接关联=======
-        '定额建筑安装工程费(不含定额设备购置费及专项费用)': {base: 'DEJZAZGCFBHSBZX', fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE}, //设备费及专项是其子节点,所以不需要用mulRef
-        '定额建筑安装工程(其中定额设备购置费按40%计)': {base: 'DEJZAZGCSBSS', fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE},
-        '建筑安装工程费(不含安全生产费)': {base: 'JZAZGCFBHSC', fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE},
-        '建筑安装工程费(不含设备费)': {base: 'JZAZGCFBHSB', fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE},
-        '建筑安装工程费': {base: 'JZAZGCF', fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE},
-        '土地使用及拆迁补偿费': {base: 'TDSYJCQBCF', fixedFlag: fixedFlag.LAND_USED_DEMOLITION},
-        '养护工程其他费': {base: 'YHGCQTF', fixedFlag: fixedFlag.MAINTENANCE_EXPENSES},
-        '预备费': {base: 'YBF', fixedFlag: fixedFlag.BUDGET_FEE},
-        '施工场地建设费': {base: 'SGCDJSF', fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE},
-        '养护单位(业主)管理费': {base: 'YHDWYZGLF',
-            multiRef: [fixedFlag.CONSTRUCTION_INSTALL_FEE]},
-        '信息化费': {base: 'XXHF',
-            multiRef: [fixedFlag.CONSTRUCTION_INSTALL_FEE]},
-        '路线工程监理费': {base: 'LXGCJLF',
-            multiRef: [fixedFlag.CONSTRUCTION_INSTALL_FEE]},
-        '独立桥梁隧道工程监理费': {base: 'QLSDGCJLF',
-            multiRef: [fixedFlag.CONSTRUCTION_INSTALL_FEE]},
-        '设计文件审查费': {base: 'SJWJSCF',
-            multiRef: [fixedFlag.CONSTRUCTION_INSTALL_FEE]},
-        '路线勘察设计费': {base: 'LXKCSJF',
-            multiRef: [fixedFlag.CONSTRUCTION_INSTALL_FEE]},
-        '独立桥梁隧道维修加固勘察设计费': {base: 'QLSDKCSJF',
-            multiRef: [fixedFlag.CONSTRUCTION_INSTALL_FEE]},
-        '招标代理及标底(最高投标限价)编制费': {base: 'ZBDLJBDBZF',
-            multiRef: [fixedFlag.CONSTRUCTION_INSTALL_FEE]},
-        '价差预备费': {base: 'JCYBF', fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE}
+        //只允许固定类别是“专项费用”、“土地使用及拆迁补偿费“、“养护工程其他费”下的清单使用
+        '定额建筑安装工程费(不含定额设备购置费及专项费用)': {
+            base: 'DEJZAZGCFBHSBZX', fixedFlag: null,
+            filter: [fixedFlag.SPECIAL_COST, fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true
+        },
+        '定额建筑安装工程费(其中定额设备购置费按40%计)': {
+            base: 'DEJZAZGCSBSS', fixedFlag: null,
+            filter: [fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true
+        },
+        //只允许固定类别是“安全生产费”
+        '建筑安装工程费(不含安全生产费)': {
+            base: 'JZAZGCFBHSC', fixedFlag: null,
+            filter: [fixedFlag.SAFE_COST],
+            pick: true
+        },
+        //只允许固定类别是“土地使用及拆迁补偿费“、“养护工程其他费”下的清单使用
+        '建筑安装工程费(不含设备费)': {
+            base: 'JZAZGCFBHSB', fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            filter: [fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true
+        },
+        //只允许非固定类别是“建筑安装工程费”下的清单引用
+        '建筑安装工程费': {
+            base: 'JZAZGCF', fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            filter: [fixedFlag.CONSTRUCTION_INSTALL_FEE],
+            pick: false
+        },
+        //只允许非固定类别是“建筑安装工程费”、非固定类别是“土地使用及拆迁补偿费”下的清单引用
+        '土地使用及拆迁补偿费': {
+            base: 'TDSYJCQBCF', fixedFlag: fixedFlag.LAND_USED_DEMOLITION,
+            filter: [fixedFlag.CONSTRUCTION_INSTALL_FEE, fixedFlag.LAND_USED_DEMOLITION],
+            pick: false,
+        },
+        //只允许非固定类别是“建筑安装工程费”、非固定类别是“土地使用及拆迁补偿费”、非固定类别是“养护工程其他费”下的清单引用
+        '养护工程其他费': {
+            base: 'YHGCQTF', fixedFlag: fixedFlag.MAINTENANCE_EXPENSES,
+            filter: [fixedFlag.CONSTRUCTION_INSTALL_FEE, fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: false
+        },
+        //只允许非固定类别是“建筑安装工程费”、非固定类别是“土地使用及拆迁补偿费”、非固定类别是“养护工程其他费”、非固定类别是“预备费”下的清单引用。
+        '预备费': {
+            base: 'YBF', fixedFlag: fixedFlag.BUDGET_FEE,
+            filter: [fixedFlag.CONSTRUCTION_INSTALL_FEE, fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES, fixedFlag.BUDGET_FEE],
+            pick: false
+        },
+        //只允许固定类别是“专项费用”、“土地使用及拆迁补偿费“、“养护工程其他费”下的清单使用
+        '施工场地建设费': {
+            base: 'SGCDJSF', fixedFlag: null,
+            filter: [fixedFlag.SPECIAL_COST, fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true
+        },
+        //只允许固定类别是“专项费用”、“土地使用及拆迁补偿费“、“养护工程其他费”下的清单使用
+        '养护单位(业主)管理费': {
+            base: 'YHDWYZGLF', fixedFlag: null,
+            filter: [fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true
+        },
+        //只允许固定类别是“专项费用”、“土地使用及拆迁补偿费“、“养护工程其他费”下的清单使用
+        '信息化费': {
+            base: 'XXHF', fixedFlag: null,
+            filter: [fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true
+        },
+        //只允许固定类别是“专项费用”、“土地使用及拆迁补偿费“、“养护工程其他费”下的清单使用
+        '路线工程监理费': {
+            base: 'LXGCJLF', fixedFlag: null,
+            filter: [fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true
+        },
+        //只允许固定类别是“专项费用”、“土地使用及拆迁补偿费“、“养护工程其他费”下的清单使用
+        '独立桥梁隧道工程监理费': {
+            base: 'QLSDGCJLF', fixedFlag: null,
+            filter: [ fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true
+        },
+        //只允许固定类别是“专项费用”、“土地使用及拆迁补偿费“、“养护工程其他费”下的清单使用
+        '设计文件审查费': {
+            base: 'SJWJSCF', fixedFlag: null,
+            filter: [fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true
+        },
+        //只允许固定类别是“专项费用”、“土地使用及拆迁补偿费“、“养护工程其他费”下的清单使用
+        '路线勘察设计费': {
+            base: 'LXKCSJF', fixedFlag: null,
+            filter: [fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true
+        },
+        //只允许固定类别是“专项费用”、“土地使用及拆迁补偿费“、“养护工程其他费”下的清单使用
+        '独立桥梁隧道维修加固勘察设计费': {
+            base: 'QLSDKCSJF', fixedFlag: null,
+            filter: [fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true
+        },
+        '招标代理及标底(最高投标限价)编制费': {
+            base: 'ZBDLJBDBZF', fixedFlag: null,
+            filter: [fixedFlag.LAND_USED_DEMOLITION, fixedFlag.MAINTENANCE_EXPENSES],
+            pick: true
+        },
+        //只允许固定类别是“价差预备费”的清单使用
+        '价差预备费': {
+            base: 'JCYBF', fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+            filter: [fixedFlag.SPREAD_BUDGET_FEE],
+            pick: true
+        }
     },
 
     /*
     * 工程量清单项目
     * */
     'boq': {
-        '各章清单合计': {base: 'GZQDHJ', fixedFlag: fixedFlag.ONE_SEVEN_BILLS},
-        '专项暂定合计': {base: 'ZXZDHJ', fixedFlag: null},
-        '100章以外清单合计': {base: 'YBZYHQDHJ', fixedFlag: fixedFlag.ONE_SEVEN_BILLS},
+        //仅允许用于固定类别是“第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
+        },
     }
 };
 
@@ -856,7 +991,7 @@ let cbAnalyzer = {
         if(!this.arithmeticLegal(exp)){
             throw '表达式含有无效字符';
         }
-        if(!this.baseLegal(cbTools.getFigure(node), exp)){
+        if(!this.baseLegal(cbTools.getValidFigures(node), exp)){
             throw '清单基数不合法';
         }
         if(!this.fLegal(calcBase.project.mainTree.items, exp)){
@@ -913,7 +1048,7 @@ let cbParser = {
     },
     //获取表达式中的中文式
     getCN: function(expr){
-        let cnRex = /\d*[\u4e00-\u9fa5]{1,}\({0,}[\u4e00-\u9fa5]{0,}\){0,}[\u4e00-\u9fa5]{0,}/g;
+        let cnRex = /\d*[\u4e00-\u9fa5]{1,}\({0,}[\u4e00-\u9fa5]{0,}\d*%*[\u4e00-\u9fa5]{0,}\){0,}[\u4e00-\u9fa5]{0,}/g;
         return _.filter(expr.match(cnRex), function (data) {
             return data
         });
@@ -949,14 +1084,15 @@ let cbParser = {
     },
     //表达式中的百分数转换成小数
     percentToNum: function (exp) {
-        let rex = /\d+(\.\d+)?%/g;
+        let rex = /[\+,\-,\*,\/]{1}\d+(\.\d+)?%[\u4e00-\u9fa5]{0}/g;
         let percents = exp.match(rex);
         let numRex = /\d+(\.\d+)?/g;
         if(cbTools.isDef(percents)){
             for(let i = 0, len = percents.length; i < len; i++){
-                let percentNum = percents[i].match(numRex);
+                let percentNum = percents[i].match(numRex),
+                    oprtor = percents[i].replace(`${percentNum}%`, '');
                 if(cbTools.isDef(percentNum) && percentNum.length === 1){
-                    exp = exp.replace(new RegExp(percents[i], 'g'), percentNum[0]/100);
+                    exp = exp.replace(new RegExp(`\\${percents[i]}`, 'g'), `${oprtor}${percentNum[0]/100}`);
                 }
             }
         }
@@ -1084,6 +1220,7 @@ let cbCalctor = {
 };
 
 let calcBase = {
+    //正在执行计算的节点(计算基数的排除本项可能会用到)
     errMsg: '表达式不正确',
     success: false,
     //清单固定行
@@ -1091,6 +1228,8 @@ let calcBase = {
     fixedBills: Object.create(null),
     //清单基数
     baseFigures: Object.create(null),
+    //清单固定行可用基数对应 {flag: Number, baseList: Array}
+    flagValidBase: Object.create(null),
     //清单可选基数映射,分两类:组织措施项目:排除父项和计算的父项; 其他项目、规费、税金、工程造价,及新增部分:显示所有计算基数
     baseFigureClass: Object.create(null),
     //初始化
@@ -1105,17 +1244,15 @@ let calcBase = {
             me.baseFigures = baseFigureMap.boq;
         }
         cbTools.setBaseBills(me.baseFigures, me.fixedBills);
-        //cbTools.setBaseFigureClass(me.baseFigures, me.baseFigureClass);
+        //设置清单固定行可用基数映射
+        cbTools.setValidBaseMapping(me.baseFigures, me.flagValidBase);
     },
     getBase: function (figure) {
         return cbCalctor.base(figure);
 
     },
     getBaseByClass: function (node) {
-        return cbTools.getFigure(node);
-    },
-    getBaseBill: function (node) {
-        return cbTools.getBaseBill(node);
+        return cbTools.getValidFigures(node);
     },
     calculate: function (node, reCalc = null) {
             let me = calcBase,

+ 15 - 1
web/building_saas/main/js/models/calc_program.js

@@ -143,7 +143,17 @@ let calcTools = {
         sheet.setSelection(idx, 0, rowsCount, colsCount);
         sheet.showRow(idx, GC.Spread.Sheets.VerticalPosition.center);
     },
-
+    clearFees: function (treeNode) {
+        if (!treeNode || !treeNode.data.feesIndex) {
+            return;
+        }
+        let clearFeeField = ['unitFee', 'totalFee', 'tenderUnitFee', 'tenderTotalFee'];
+        for (let feeField in treeNode.data.feesIndex) {
+            for (let subFee of clearFeeField) {
+                treeNode.data.feesIndex[feeField][subFee] = 0;
+            }
+        }
+    },
     initFees: function (treeNode){
         if (!treeNode.data.fees) {
             treeNode.data.fees = [];
@@ -1756,6 +1766,10 @@ class CalcProgram {
             ttf = ttf.toDecimal(decimalObj.bills.totalPrice);
             deleteUselessFees(treeNode);
             calcTools.checkFeeField(treeNode, {'fieldName': 'common', 'unitFee': uf, 'totalFee': tf, 'tenderUnitFee': tuf, 'tenderTotalFee': ttf});
+            //第一部分建安费下,使用基数计算的清单,定额建安费应=金额
+            if (cbTools.getBelongFlagList(treeNode).includes(fixedFlag.CONSTRUCTION_INSTALL_FEE)) {
+                calcTools.checkFeeField(treeNode, {'fieldName': 'rationCommon', 'unitFee': uf, 'totalFee': tf, 'tenderUnitFee': tuf, 'tenderTotalFee': ttf});
+            }
 
             // 总造价清单还要做单项工程、建设项目的四大项金额汇总
             if (calcTools.isTotalCostBill(treeNode)){

+ 6 - 0
web/building_saas/main/js/models/fee_rate.js

@@ -344,6 +344,12 @@ var FeeRate = {
                     me.datas.libID=data.libID;
                     me.datas.libName=data.libName;
                     me.datas.feeRateID=data.feeRateID;
+                    if(newVal==feeRate.libID){//如果是本身,则是恢复标准值,设置回旧的树节点折叠属性
+                       let oldRates =  me.datas.rates;
+                       for(let i =0;i< oldRates.length;i++){
+                           data.rates[i].collapsed = oldRates[i].collapsed;
+                       }
+                    }
                     me.datas.rates=data.rates;
                     projectObj.project.markUpdateProject({projectID:projectObj.project.ID(),feeRateID:me.getActivateFeeRateFileID()},'feeRate');
                     me.onFeeRateFileChange();

+ 3 - 1
web/building_saas/main/js/models/main_consts.js

@@ -256,7 +256,9 @@ const fixedFlag = {
     //暂列金额
     PROVISIONAL:26,
     //安全生产费
-    SAFE_COST:27
+    SAFE_COST:27,
+    //100章清单
+    ONE_HUNDRED_BILLS: 28
 };
 
 const gljKeyArray =['code','name','specs','unit','type'];

+ 12 - 65
web/building_saas/main/js/views/calc_base_view.js

@@ -140,8 +140,7 @@ let calcBaseView = {
     ifEdit: function (type, row) {
         if (type == 'ration'){
             return true;
-        }
-        else{
+        } else{
             let selected = projectObj.project.mainTree.items[row];
             return selected && MainTreeCol.readOnly.forCalcBase(selected)?false:true;
         }
@@ -344,70 +343,18 @@ let calcBaseView = {
             }
         });
     },
-
-    getCalcBaseCellType:function (type) {
-        var ns = GC.Spread.Sheets;
-        function CalcBaseCellType() {
-            var init=false;
+    onCalcBaseButtonClick:function (hitinfo,type='bills') {
+        let me = calcBaseView;
+        let node = projectObj.project.mainTree.items[hitinfo.row] ? projectObj.project.mainTree.items[hitinfo.row] : null;
+        if(hitinfo.sheet.getParent() === projectObj.mainSpread){
+            projectObj.mainController.setTreeSelected(node);
         }
-        CalcBaseCellType.prototype = new ns.CellTypes.Text();
-        CalcBaseCellType.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
-            if(calcBaseView.editingCell && !projectReadOnly && calcBaseView.ifEdit(type, options.row)){
-                if(options.sheet.getActiveRowIndex()==options.row&&options.sheet.getActiveColumnIndex()==options.col){
-                    var image = document.getElementById('f_btn'),imageMagin = 3;
-                    var imageHeight = 15;
-                    var imageWidth = 25;
-                    var imageX = x + w - imageWidth- imageMagin, imageY = y + h / 2 - imageHeight / 2;
-                    ctx.save();
-                    ctx.fillStyle = style.backColor;
-                    ctx.fillRect(x,y,w,h);
-                    ctx.drawImage(image, imageX, imageY,imageWidth,imageHeight);
-                    ctx.beginPath();
-                    ctx.arc(imageX+imageWidth/2,imageY+imageHeight/2,1,0,360,false);
-                    ctx.arc(imageX+imageWidth/2-4,imageY+imageHeight/2,1,0,360,false);
-                    ctx.arc(imageX+imageWidth/2+4,imageY+imageHeight/2,1,0,360,false);
-                    ctx.fillStyle="black";//填充颜色,默认是黑色
-                    ctx.fill();//画实心圆
-                    ctx.closePath();
-                    ctx.restore();
-                    x = x - imageWidth - imageMagin;
-                }
-            }
-            GC.Spread.Sheets.CellTypes.Text.prototype.paint.apply(this, arguments);
-        };
-        CalcBaseCellType.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
-            return {
-                x: x,
-                y: y,
-                row: context.row,
-                col: context.col,
-                cellStyle: cellStyle,
-                cellRect: cellRect,
-                sheetArea: context.sheetArea
-            };
-        };
-        CalcBaseCellType.prototype.processMouseDown = function (hitinfo) {
-            let me = calcBaseView;
-            if(hitinfo.sheet.getActiveRowIndex()==hitinfo.row&&hitinfo.sheet.getActiveColumnIndex()==hitinfo.col){
-                var offset=hitinfo.cellRect.x+hitinfo.cellRect.width-6;
-                var imageWidth = 25;
-                if(hitinfo.x<offset&&hitinfo.x>offset-imageWidth){
-                    if(!projectReadOnly && me.ifEdit(type, hitinfo.row)){
-                        hitinfo.sheet.setActiveCell(hitinfo.row, hitinfo.col);
-                        if(hitinfo.sheet.getParent() === projectObj.mainSpread){
-                            let node = projectObj.project.mainTree.items[hitinfo.row] ? projectObj.project.mainTree.items[hitinfo.row] : null;
-                            projectObj.mainController.setTreeSelected(node);
-                        }
-                        calcBaseView.confirmBtn.attr('toggle', 'calcBase');
-                        changeCalcBaseFeeRate('calcBase');
-                        $('#tabCalcBase').tab('show');
-                        calcBaseView.initCalctor(type);
-                    }
-                }
-            }
-        };
-        return new CalcBaseCellType();
-    },
+        hitinfo.sheet.setActiveCell(hitinfo.row, hitinfo.col);
+        calcBaseView.confirmBtn.attr('toggle', 'calcBase');
+        changeCalcBaseFeeRate('calcBase');
+        $('#tabCalcBase').tab('show');
+        calcBaseView.initCalctor(type);
+    }
 };
 
 $(document).ready(function () {

+ 15 - 2
web/building_saas/main/js/views/calc_program_manage.js

@@ -25,7 +25,7 @@ let calcProgramManage = {
             // {headerName:"ID",headerWidth:80,dataCode:"ID", hAlign: "center"},
             {headerName:"费用代号",headerWidth:CP_Col_Width.code, dataCode:"code", dataType: "String"},
             {headerName:"费用名称",headerWidth:CP_Col_Width.name, dataCode:"name", dataType: "String"},
-            {headerName:"计算基数",headerWidth:CP_Col_Width.dispExprUser, dataCode:"dispExprUser", dataType: "String"},
+            {headerName:"计算基数",headerWidth:CP_Col_Width.dispExprUser, dataCode:"dispExprUser", dataType: "String",cellType:'cusButton',callback:'calcBase',disable:'calcBase'},
             {headerName:"费率",headerWidth:CP_Col_Width.feeRate, dataCode:"feeRate", dataType: "Number",hAlign: "right",decimalField:"feeRate"},
             {headerName:"费用类别",headerWidth:CP_Col_Width.displayFieldName, dataCode:"displayFieldName", dataType: "String", hAlign: "center"},
             {headerName:"基数说明",headerWidth:CP_Col_Width.statement, dataCode:"statement", dataType: "String"},
@@ -36,6 +36,17 @@ let calcProgramManage = {
             lockColumns:[2,5],
             colHeaderHeight: CP_Col_Width.colHeader,
             rowHeaderWidth: CP_Col_Width.rowHeader
+        },
+        //callback 和disable都是和 cusButton配套使用的
+        callback:{
+            calcBase:function (hitinfo) {
+                calcBaseView.onCalcBaseButtonClick(hitinfo,'ration');
+            }
+        },
+        disable:{
+            calcBase:function (row,col) {
+                return projectReadOnly || !calcBaseView.ifEdit('ration', row)
+            }
         }
     },
 
@@ -67,11 +78,13 @@ let calcProgramManage = {
         me.detailSheet.bind(GC.Spread.Sheets.Events.RangeChanged, me.onRangeChanged);
         me.detailSheet.bind(GC.Spread.Sheets.Events.EnterCell, me.onDetailEnterCell);
         me.detailSheet.bind(GC.Spread.Sheets.Events.ClipboardPasting, me.onClipboardPasting);
+        me.detailSheet.bind(GC.Spread.Sheets.Events.SelectionChanged, function (e,info) {
+            info.sheet.repaint();
+        });
         sheetCommonObj.showData(me.mainSheet, me.mainSetting, me.datas);
 
         me.detailSheet.name('calc_detail');
         feeRateObject.setFeeRateCellCol(me.detailSheet, _.findIndex(me.detailSetting.header,{'dataCode':'feeRate'}));
-        me.detailSheet.getRange(-1, _.findIndex(me.detailSetting.header, {'dataCode': 'dispExprUser'}), -1, 1).cellType(calcBaseView.getCalcBaseCellType('ration'));
         sheetCommonObj.showData(me.detailSheet, me.detailSetting, me.datas[0].calcItems);
         me.getfeeRateColor(me.datas[0].calcItems);
         customRowHeader(me.detailSheet, me.datas[0].calcItems.length);

+ 15 - 2
web/building_saas/main/js/views/fee_rate_view.js

@@ -173,13 +173,24 @@ var feeRateObject={
                     let treeType = sheetCommonObj.getTreeNodeCellType(data,row,parentMap);
                     sheet.getCell(row, 0).cellType(treeType);
                     visibleMap[data[row].ID] = treeType.collapsed;
-                    if(visibleMap[data[row].ParentID] ) sheet.getRange(row , -1, 1, -1).visible(!visibleMap[data[row].ParentID]);//显示或隐藏
+                    feeRateObject.setRowVisible(data,row,visibleMap,sheet);
                 }
             }
         }
         sheet.resumeEvent();
         sheet.resumePaint();
     },
+    setRowVisible:function (data,row,visibleMap,sheet) {
+        sheet.getRange(row , -1, 1, -1).visible(getVisible(data[row].ParentID));//显示或隐藏
+        function getVisible(ParentID) {
+            if(visibleMap[ParentID]) return false //如果父节点是缩起的,那就隐藏本身。
+            if(visibleMap[ParentID] == false){//如果父节点不是缩起的,要再往父节点找看
+                let pnode = _.find(data,{'ID':ParentID});
+                if(pnode) return getVisible(pnode.ParentID);//如果有父节点,递归调用
+                return true;//没有,返回显示
+            }
+        }
+    },
     getFeeRateLevel:function (rate,data) {
         if(rate.ParentID){
          let prate =  _.find(data,{'ID':rate.ParentID});
@@ -1006,7 +1017,9 @@ $(function(){
     $('#changeConfirm').bind('click', function (){
         var newVal=$('#standardSelect').val();
         var feeRateFile = projectObj.project.FeeRate.getActivateFeeRate();
-        if(newVal&&newVal!=feeRateFile.libID)  feeRateObject.changeFeeRateStandard(newVal);
+        feeRateObject.changeFeeRateStandard(newVal);
+        //这里改成不判断了,只是点击确定了就重选
+        //if(newVal&&newVal!=feeRateFile.libID)  feeRateObject.changeFeeRateStandard(newVal);
     });
 
     $('#saveAs').bind('click', function (){

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

@@ -14,8 +14,8 @@ let gljCol = {
             {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"
-            {headerName: "总消耗量", headerWidth: 80, dataCode: "totalQuantity", dataType: "Number", hAlign: "right", decimalField: "glj.quantity"},
-            {headerName: "暂估", headerWidth: 45, dataCode: "isEstimate", dataType: "String", hAlign: "center", vAlign: "center", cellType: "checkBox"}
+            {headerName: "总消耗量", headerWidth: 80, dataCode: "totalQuantity", dataType: "Number", hAlign: "right", decimalField: "glj.quantity"}
+            //{headerName: "暂估", headerWidth: 45, dataCode: "isEstimate", dataType: "String", hAlign: "center", vAlign: "center", cellType: "checkBox"}
         ],
         view: {
             lockColumns: [ "adjustPrice", "rationItemQuantity", "quantity", "totalQuantity", "isEstimate"],//这里以后改成dataCode好一点

+ 12 - 10
web/building_saas/main/js/views/glj_view.js

@@ -575,6 +575,7 @@ var gljOprObj = {
         }
     },
     showRationGLJSheetData: function (init) {
+        let me = this;
         let selected = this.sheet.getSelections();
         this.combineWithProjectGlj(this.sheetData);
         this.sheet.setRowCount(0);
@@ -582,16 +583,17 @@ var gljOprObj = {
         this.sumQuantity();//计算总消耗量
         this.addMixRatioToShow();//显示组成物信息
         this.initRationTree(init,this.getUnitPriceCodeMap());
-        sheetCommonObj.showData(this.sheet, this.setting, this.sheetData);
-        if(this.mainTreeSelectedChange == true){
-            this.sheet.setSelection(0,1,1,1);//默认选中第一行,第二列(名称列)
-            this.mainTreeSelectedChange = false;
-        }else if(selected){//定位光标到之前的位置
-            this.sheet.setSelection(selected[0].row,selected[0].col,selected[0].rowCount,selected[0].colCount);
-        }
-        //初始选择
-        this.preGljSelection = null;
-        this.sheetInitSelection({row: this.sheet.getActiveRowIndex(), col: this.sheet.getActiveColumnIndex()});
+        sheetCommonObj.showData(this.sheet, this.setting, this.sheetData,null,function () {
+            if(me.mainTreeSelectedChange == true){
+                me.sheet.setSelection(0,1,1,1);//默认选中第一行,第二列(名称列)
+                me.mainTreeSelectedChange = false;
+            }else if(selected){//定位光标到之前的位置
+                me.sheet.setSelection(selected[0].row,selected[0].col,selected[0].rowCount,selected[0].colCount);
+            }
+            //初始选择
+            me.preGljSelection = null;
+            me.sheetInitSelection({row: me.sheet.getActiveRowIndex(), col: me.sheet.getActiveColumnIndex()});
+        });
 
     },
     getSelectedRationGlj:function () {

+ 13 - 10
web/building_saas/main/js/views/main_tree_col.js

@@ -119,12 +119,11 @@ let MainTreeCol = {
         },
         commonTotalFee: function (node) {
             // 09-29 zhang
-            let Bills =projectObj.project.Bills;
             // 当前属于分部分项、施工技术措施项目,综合单价只读。
-            if(Bills.isFBFX(node)||Bills.isTechMeasure(node)){
+            /*if(Bills.isFBFX(node)||Bills.isTechMeasure(node)){
                 return true;
-            }
-            // 不属于分部分项、施工技术措施项目的部分,如果不是叶子清单,或有基数计算/定额/量价/人材机 只读
+            }*/
+            //如果不是叶子清单,或有基数计算/定额/量价/人材机 只读
             if(!calcTools.isLeafBill(node)||calcTools.isCalcBaseBill(node)||node.children.length > 0){
                 return true;
             }
@@ -173,8 +172,9 @@ let MainTreeCol = {
         },
         specialProvisional:function (node) {
             //是否使用了专项暂定合计基数,使用了的节点不可设置专项暂定(会造成循环计算)
-            let hasZDBase = node.data.calcBase && node.data.calcBase.includes('专项暂定合计');
-            return !(node.sourceType == ModuleNames.bills && node.source.children.length == 0 && !hasZDBase);
+            //只允许“第100章至700章清单”下的叶子清单可选
+            let belongFlagList = cbTools.getBelongFlagList(node);
+            return !(node.sourceType == ModuleNames.bills && node.source.children.length == 0 && belongFlagList.includes(fixedFlag.ONE_SEVEN_BILLS));
         },
         volumePrice: function (node) {
             return (node.data.type == rationType.volumePrice || node.data.type == rationType.gljRation);
@@ -304,8 +304,10 @@ let MainTreeCol = {
                 return  quantityEditObj.getQuantityEditCellType();
             }
         },
-        calcBase: function () {
-            return calcBaseView.getCalcBaseCellType('bills');
+        calcBase: function (node) {
+            let readOnly = projectReadOnly || !calcBaseView.ifEdit('bills', projectObj.project.mainTree.items.indexOf(node));
+            return sheetCommonObj.getCusButtonCellType(calcBaseView.onCalcBaseButtonClick,readOnly)
+            //return calcBaseView.getCalcBaseCellType('bills');
         },
 
         // CSL, 2017-11-28
@@ -360,8 +362,9 @@ let MainTreeCol = {
         evaluationProject:function (node) {
             if(node.sourceType == ModuleNames.ration) return new GC.Spread.Sheets.CellTypes.CheckBox();
         },
-        commonTotalFee: function () {
-            return projectObj.getCommonTotalFeeCellType();
+        commonTotalFee: function (node) {
+            let readOnly = projectReadOnly || !calcBaseView.ifEdit('bills', projectObj.project.mainTree.items.indexOf(node));
+            return sheetCommonObj.getCusButtonCellType(projectObj.onCommonTotalFeeButtonClick,readOnly) //projectObj.getCommonTotalFeeCellType();
         },
         specialProvisional: function (node) {
             if(!MainTreeCol.readOnly.specialProvisional(node)){

+ 3 - 7
web/building_saas/main/js/views/project_property_basicInfo.js

@@ -473,7 +473,8 @@ let basicInfoView = {
 };
 
 $(document).ready(function () {
-    $('#poj-set').on('shown.bs.modal', function (e) {
+    //暂时隐藏
+   /* $('#poj-set').on('shown.bs.modal', function (e) {
         //init Spread
         basicInfoView.initDatas(basicInfoView.orgDatas);
         basicInfoView.buildSheet();
@@ -494,14 +495,9 @@ $(document).ready(function () {
 
     $('#tab_poj-settings-basicInfo').on('shown.bs.tab', function () {
         basicInfoView.workBook.refresh();
-    });
+    });*/
     $('#openProjSet').click(function () {
         $('[data-toggle="tooltip"]').tooltip('hide');
         $('#poj-set').modal('show');
     });
-   /* $('#property_ok').bind('click', function () {
-        if(basicInfoView.toUpdate(basicInfoView.orgDatas, basicInfoView.datas)){
-            basicInfoView.a_updateInfo(basicInfoView.toSaveDatas(basicInfoView.datas));
-        }
-    });*/
 });

+ 32 - 71
web/building_saas/main/js/views/project_view.js

@@ -500,6 +500,8 @@ var projectObj = {
                         let activeCell = projectObj.mainSpread.getActiveSheet().getSelections()[0];
                         projectObj.mainController.refreshTreeNode([node]);
                         return;
+                    } else if (value === '') {//删除清单基数,要清空费用
+                        calcTools.clearFees(node);
                     }
                     // if (value) {value = parseFloat(value).toDecimal(decimalObj.decimal("totalPrice", node))};
                 };
@@ -1355,8 +1357,14 @@ var projectObj = {
                     },
                     visible: function(key, opt){//2018-11-08  新需求,这个按钮先隐藏,有需要再放开
                         let selected = project.mainTree.selected;
-                        return selected && selected.sourceType == ModuleNames.bills &&
-                            !_.isEmpty(selected.data.flagsIndex) && selected.data.flagsIndex.fixed.flag === fixedFlag.EQUIPMENT_ACQUISITION_FEE
+                        if (selected && selected.sourceType == ModuleNames.bills) {
+                            //属于的固定清单
+                            let belongFlag = cbTools.getBelongFlag(selected);
+                            if (belongFlag && belongFlag === fixedFlag.EQUIPMENT_ACQUISITION_FEE) {
+                                return true;
+                            }
+                        }
+                        return false;
                     }
                 },
                 "spr2": '--------',
@@ -1919,71 +1927,19 @@ var projectObj = {
         }
         return this.itemCol?!this.itemCol.visible:true;
     },
-    //综合合价cellType
-    getCommonTotalFeeCellType:function () {
-        let type = 'bills';
-        var ns = GC.Spread.Sheets;
-        function CommonTotalFeeCellType() {
-            var init=false;
-        }
-        CommonTotalFeeCellType.prototype = new ns.CellTypes.Text();
-        CommonTotalFeeCellType.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
-            if(!projectReadOnly && calcBaseView.ifEdit(type, options.row)){
-                if(options.sheet.getActiveRowIndex()==options.row&&options.sheet.getActiveColumnIndex()==options.col){
-                    var image = document.getElementById('f_btn'),imageMagin = 3;
-                    var imageHeight = 15;
-                    var imageWidth = 25;
-                    var imageX = x + w - imageWidth- imageMagin, imageY = y + h / 2 - imageHeight / 2;
-                    ctx.save();
-                    ctx.fillStyle = style.backColor;
-                    ctx.fillRect(x,y,w,h);
-                    ctx.drawImage(image, imageX, imageY,imageWidth,imageHeight);
-                    ctx.beginPath();
-                    ctx.arc(imageX+imageWidth/2,imageY+imageHeight/2,1,0,360,false);
-                    ctx.arc(imageX+imageWidth/2-4,imageY+imageHeight/2,1,0,360,false);
-                    ctx.arc(imageX+imageWidth/2+4,imageY+imageHeight/2,1,0,360,false);
-                    ctx.fillStyle="black";//填充颜色,默认是黑色
-                    ctx.fill();//画实心圆
-                    ctx.closePath();
-                    ctx.restore();
-                    x = x - imageWidth - imageMagin;
-                }
-            }
-            GC.Spread.Sheets.CellTypes.Text.prototype.paint.apply(this, arguments);
-        };
-        CommonTotalFeeCellType.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
-            return {
-                x: x,
-                y: y,
-                row: context.row,
-                col: context.col,
-                cellStyle: cellStyle,
-                cellRect: cellRect,
-                sheetArea: context.sheetArea
-            };
-        };
-        CommonTotalFeeCellType.prototype.processMouseDown = function (hitinfo) {
-            let me = calcBaseView;
-            if(hitinfo.sheet.getActiveRowIndex()==hitinfo.row&&hitinfo.sheet.getActiveColumnIndex()==hitinfo.col){
-                var offset=hitinfo.cellRect.x+hitinfo.cellRect.width-6;
-                var imageWidth = 25;
-                if(hitinfo.x<offset&&hitinfo.x>offset-imageWidth){
-                    if(!projectReadOnly && me.ifEdit(type, hitinfo.row)){
-                        if(hitinfo.sheet.getParent() === projectObj.mainSpread){
-                            let node = projectObj.project.mainTree.items[hitinfo.row] ? projectObj.project.mainTree.items[hitinfo.row] : null;
-                            projectObj.mainController.setTreeSelected(node);
-                        }
-                        hitinfo.sheet.setActiveCell(hitinfo.row, hitinfo.col);
-                        calcBaseView.confirmBtn.attr('toggle', 'commonTotalFee');
-                        changeCalcBaseFeeRate('commonTotalFee');
-                        $('#tabCalcBase').tab('show');
-                        calcBaseView.initCalctor(type);
-                        feeRateObject.showSelectModal(hitinfo);
-                    }
-                }
-            }
-        };
-        return new CommonTotalFeeCellType();
+    onCommonTotalFeeButtonClick:function(hitinfo){
+        let me = calcBaseView;
+        let node = projectObj.project.mainTree.items[hitinfo.row] ? projectObj.project.mainTree.items[hitinfo.row] : null;
+        if(hitinfo.sheet.getParent() === projectObj.mainSpread){
+            projectObj.mainController.setTreeSelected(node);
+        }
+        hitinfo.sheet.setActiveCell(hitinfo.row, hitinfo.col);
+        calcBaseView.confirmBtn.attr('toggle', 'commonTotalFee');
+        //changeCalcBaseFeeRate('commonTotalFee');//公路上不需要费率选项页
+        changeCalcBaseFeeRate('calcBase');
+        $('#tabCalcBase').tab('show');
+        calcBaseView.initCalctor('bills');
+            //feeRateObject.showSelectModal(hitinfo);
     },
     selectColAndFocus :function (newNode,field = 'code') {//选中单元格并设置焦点
         if(newNode){
@@ -2041,11 +1997,10 @@ $('#delete').click(function () {
 $('#upLevel').click(function () {
     var controller = projectObj.mainController, project = projectObj.project;
     var selected = controller.tree.selected, orgParent = selected.parent;
-
     if (selected && selected.sourceType === project.Bills.getSourceType()) {
         project.Bills.upLevelBills(selected.source);
         controller.upLevel();
-        controller.refreshTreeNode([orgParent]);
+        controller.refreshTreeNode([orgParent, selected]);
         projectObj.project.calcProgram.calcNodesAndSave([selected,orgParent]);
     }
 });
@@ -3228,8 +3183,10 @@ function changeCalcBaseFeeRate(toggle) {
         $('#calcBaseFeeRate').find('.modal-body').find('button:first').hide();
         $('#calcBaseFeeRate').find('.modal-body').find('ul:first').hide();
         $('#calcBaseExp').remove();
-        let $input = $('<input>').attr('id', 'calcBaseExp').addClass('form-control');
-        $('#expArea').prepend($input);
+        /*let $input = $('<input>').attr('id', 'calcBaseExp').addClass('form-control');
+        $('#expArea').prepend($input);*/
+        let $textarea = $('<textarea>').attr('id', 'calcBaseExp').prop('rows', 3).addClass('form-control').css('resize', 'none');
+        $('#expArea').prepend($textarea);
     }
     else if(toggle === 'feeRate'){
         $('#calcBaseFeeRate').find('.modal-header').show();
@@ -3281,6 +3238,10 @@ $('#calcBaseFeeRateConf').click(function () {
             $('#calcBaseFeeRate').modal('hide');
         }
     }
+    //删除清单基数,要清空费用
+    if (projectObj.project.calcBase.success && calcBaseValue === '') {
+        calcTools.clearFees(selected);
+    }
     if(!projectObj.project.calcBase.success && cusFeeRate==false){
         return;
     } else if((!validateFeeRate || selected.data.feeRateID === parseInt(feeRateObject.feeRateSelection.ID)) && needToSave) {

+ 7 - 2
web/building_saas/main/js/views/quantity_edit_view.js

@@ -80,7 +80,7 @@ let quantityEditObj = {
                     ctx.fill();//画实心圆
                     ctx.closePath();
                     ctx.restore();
-                    x = x - imageWidth - imageMagin;
+                    w = w - imageWidth - imageMagin;
                 }
             }
             GC.Spread.Sheets.CellTypes.Text.prototype.paint.apply(this, arguments);
@@ -165,7 +165,12 @@ let quantityEditObj = {
         let evalString = quantityEXP;
         if( node.sourceType == ModuleNames.ration && quantityEXP.indexOf('QDL')!=-1){
             let billNode = node.parent;
-            let bQuantity = billNode.data.quantity?scMathUtil.roundForObj(billNode.data.quantity,getDecimal("quantity",billNode)):0;
+            let bQuantity = 0;
+            if(billNode.updateData.quantity){
+                bQuantity = scMathUtil.roundForObj(billNode.updateData.quantity,getDecimal("quantity",billNode));
+            }else {
+                bQuantity = billNode.data.quantity?scMathUtil.roundForObj(billNode.data.quantity,getDecimal("quantity",billNode)):0;
+            }
             evalString = replaceAll("QDL","("+bQuantity+")",evalString);
         }
         try {

+ 0 - 2
web/building_saas/main/js/views/sub_view.js

@@ -49,8 +49,6 @@ let subObj = {
 
         if(projectReadOnly){
             disableSpread(subSpread);
-            disableSpread(contentOprObj.workBook);
-            disableSpread(characterOprObj.workBook);
         }
     },
     initNavItem:function (node) {

+ 13 - 18
web/building_saas/pm/js/pm_newMain.js

@@ -747,11 +747,11 @@ const projTreeObj = {
             }
         }
     },
-    openTender: function (node, timeoutTime) {
+    openTender: function (ID, parent, timeoutTime) {
         setTimeout(function () {
             let newTab = window.open('about:blank');
-            BeforeOpenProject(node.data.ID, {'fullFolder': GetFullFolder(node.parent)}, function () {
-                let mainUrl = `/main?project=${node.data.ID}`;
+            BeforeOpenProject(ID, {'fullFolder': GetFullFolder(parent)}, function () {
+                let mainUrl = `/main?project=${ID}`;
                 CommonAjax.get(mainUrl, [], function () {
                     newTab.location.replace(mainUrl); //不能后退
                 });
@@ -907,14 +907,7 @@ const projTreeObj = {
         TreeNodeCellType.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
             let info = {x: x, y: y, row: context.row, col: context.col, cellStyle: cellStyle, cellRect: cellRect, sheetArea: context.sheetArea};
             let node = tree.items[info.row];
-            let offset = -1;
-            let centerX = info.cellRect.x + offset + node.depth() * indent + node.depth() * levelIndent + indent / 2;
-            let text = context.sheet.getText(info.row, info.col);
-            let value = context.sheet.getValue(info.row, info.col);
-            let acStyle = context.sheet.getActualStyle(info.row, info.col),
-                zoom = context.sheet.zoom();
-            let textLength = this.getAutoFitWidth(value, text, acStyle, zoom, {sheet: context.sheet, row: info.row, col: info.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport});
-            if(info.x > centerX + halfBoxLength && info.x < centerX + halfBoxLength + imgWidth + indent/2+3 + textLength){
+            if (node && node.data.projType === projectType.tender) {
                 info.isReservedLocation = true;
             }
             return info;
@@ -935,21 +928,23 @@ const projTreeObj = {
             }
             if(hitinfo.sheet.name() === "projectSheet"){//只有项目管理界面才执行
                 //单项文件,进入造价书界面
-                if(node.data.projType === projectType.tender && withingClickArea()){
-                    /*let thisClick = Date.now(),
+                if(node.data.projType === projectType.tender){
+                    let thisClick = Date.now(),
                         open = false;
                     if (this.preNode === node && this.preClick && thisClick - this.preClick <= 300) {
-                        open = true;*/
+                        open = true;
                         let timeoutTime = 200;
                         if($('.slide-sidebar').hasClass('open')){
                             timeoutTime = 500;
                         }
-                        projTreeObj.openTender(node, timeoutTime);
-                    /*}
+                        projTreeObj.openTender(node.data.ID, node.parent, timeoutTime);
+                    }
                     this.preClick = open ? null : thisClick;
-                    this.preNode = open ? null : node;*/
+                    this.preNode = open ? null : node;
+                }
+                if (!node || node.children.length === 0) {
+                    return;
                 }
-                if (!node || node.children.length === 0) { return; }
             }
             //统一改成方框外1像素内都有效
             if (hitinfo.x >= centerX - halfBoxLength - 2 && hitinfo.x <= centerX + halfBoxLength + 2 &&

+ 15 - 12
web/building_saas/pm/js/pm_share.js

@@ -334,14 +334,7 @@ const pmShare = (function () {
         TreeNodeCellType.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
             let info = {x: x, y: y, row: context.row, col: context.col, cellStyle: cellStyle, cellRect: cellRect, sheetArea: context.sheetArea};
             let node = tree.items[info.row];
-            let offset = -1;
-            let centerX = info.cellRect.x + offset + node.depth() * indent + node.depth() * levelIndent + indent / 2;
-            let text = context.sheet.getText(info.row, info.col);
-            let value = context.sheet.getValue(info.row, info.col);
-            let acStyle = context.sheet.getActualStyle(info.row, info.col),
-                zoom = context.sheet.zoom();
-            let textLength = this.getAutoFitWidth(value, text, acStyle, zoom, {sheet: context.sheet, row: info.row, col: info.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport});
-            if(info.x > centerX + halfBoxLength && info.x < centerX + halfBoxLength + imgWidth + indent/2+3 + textLength){
+            if (node && node.data.projType === projectType.tender) {
                 info.isReservedLocation = true;
             }
             return info;
@@ -361,17 +354,27 @@ const pmShare = (function () {
                 return hitinfo.x > centerX + halfBoxLength && hitinfo.x < centerX + halfBoxLength + imgWidth + indent/2+3 + textLength;
             }
             //点击单位工程
-            if(node.data.projType === projectType.tender && withingClickArea()){
-                let newTab = window.open('about:blank');
+            if(node.data.projType === projectType.tender){
+                /*let newTab = window.open('about:blank');
                 //打开项目的实际ID
                 BeforeOpenProject(node.data.actualTreeInfo.ID, {'fullFolder': GetFullFolder(node.parent)}, function () {
                     let mainUrl = `/main?project=${node.data.actualTreeInfo.ID}`;
                     CommonAjax.get(mainUrl, [], function () {
                         newTab.location.href = mainUrl;
                     });
-                });
+                });*/
+                let thisClick = Date.now(),
+                    open = false;
+                if (this.preNode === node && this.preClick && thisClick - this.preClick <= 300) {
+                    open = true;
+                    projTreeObj.openTender(node.data.actualTreeInfo.ID, node.parent, 200);
+                }
+                this.preClick = open ? null : thisClick;
+                this.preNode = open ? null : node;
+            }
+            if (!node || node.children.length === 0) {
+                return;
             }
-            if (!node || node.children.length === 0) { return; }
             if (hitinfo.x >= centerX - halfBoxLength - 2 && hitinfo.x <= centerX + halfBoxLength + 2 &&
                 hitinfo.y >= centerY - halfBoxLength - 2 && hitinfo.y <= centerY + halfBoxLength + 2) {
                 node.setExpanded(!node.expanded);

+ 44 - 8
web/building_saas/report/html/rpt_print.html

@@ -47,32 +47,36 @@
                 if (idx === 0) {
                     $(document).attr("title", pageData[JV.NODE_PAGE_INFO][JV.NODE_MAIN_INFO_RPT_NAME]);
                 }
-                let svgArr = rptPrintHelper.buildSvgArr(pageData, G_OFFSET_X, G_OFFSET_Y);
+                let orgPixelSize = getPixelSize(pageData);
+                let actArea = getActualArea(pageData);
+                let svgArr = rptPrintHelper.buildSvgArr(pageData, actArea, G_OFFSET_X, G_OFFSET_Y);
                 //let orientation = (pageData[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE][0] < pageData[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE][1])?"纵向":"横向";
                 let orientation = "纵向";
-                showPreviewData(svgArr, scaleFactor, sessionStorage.pageSize, orientation);
+                showPreviewData(svgArr, actArea, scaleFactor, sessionStorage.pageSize, orientation, orgPixelSize);
             }
             window.print();
         } else if (sessionStorage.currentPageData) {
             let pageData = JSON.parse(sessionStorage.currentPageData);
             let scaleFactor = parseInt(sessionStorage.scaleFactor);
-            let svgArr = rptPrintHelper.buildSvgArr(pageData, G_OFFSET_X, G_OFFSET_Y);
+            let orgPixelSize = getPixelSize(pageData);
+            let actArea = getActualArea(pageData);
+            let svgArr = rptPrintHelper.buildSvgArr(pageData, actArea, G_OFFSET_X, G_OFFSET_Y);
             $(document).attr("title", pageData[JV.NODE_PAGE_INFO][JV.NODE_MAIN_INFO_RPT_NAME]);
             //showPreviewData(svgArr, scaleFactor, sessionStorage.pageSize, sessionStorage.orientation);
-            showPreviewData(svgArr, scaleFactor, sessionStorage.pageSize, "纵向");
+            showPreviewData(svgArr, actArea, scaleFactor, sessionStorage.pageSize, "纵向", orgPixelSize);
             window.print();
         } else if (sessionStorage.currentPageSvgData) {
             let svgArr = JSON.parse(sessionStorage.currentPageSvgData);
             let scaleFactor = 1;
             //showPreviewData(svgArr, scaleFactor, sessionStorage.pageSize, sessionStorage.orientation);
-            showPreviewData(svgArr, scaleFactor, sessionStorage.pageSize, "纵向");
+            showPreviewData(svgArr, null, scaleFactor, sessionStorage.pageSize, "纵向", null);
             window.print();
         } else {
             //alert("没有报表数据!");
         }
     }
 
-    function showPreviewData(svgArr, scaleFactor, pageSize, orientation) {
+    function showPreviewData(svgArr, actAreaArr, scaleFactor, pageSize, orientation, orgPixelSize) {
         let orgHeight = 793, orgWidth = 1122;
         if (pageSize === 'A3') {
             orgHeight = 1122;
@@ -89,16 +93,48 @@
         }
 
         for (let i = 0; i < svgArr.length; i++) {
+            let offsetHeight = 0, offsetWidth = 0;
+            if (actAreaArr) {
+                offsetWidth = actAreaArr[i].Left + (pageWidth - actAreaArr[i].Right) - 5;
+                offsetHeight = actAreaArr[i].Top + (pageHeight - actAreaArr[i].Bottom) - 5;
+                if (orgPixelSize[0] > orgPixelSize[1]) {
+                    //横向强制改纵向(系统是以纵向为准),那么计算offset的方式会有所不同
+                    offsetWidth = actAreaArr[i].Top + (pageWidth - actAreaArr[i].Bottom) - 5;
+                    offsetHeight = actAreaArr[i].Left + (pageHeight - actAreaArr[i].Right) - 5;
+                }
+            }
             let div = $('<div class="pageBreak"></div>');
             div.append($(svgArr[i].join("")));
             $(div).find("svg").each(function(cIdx,elementSvg){
-                elementSvg.setAttribute('height', pageHeight);
-                elementSvg.setAttribute('width', pageWidth);
+                elementSvg.setAttribute('height', pageHeight - offsetHeight);
+                elementSvg.setAttribute('width', pageWidth - offsetWidth);
             });
             $("body").append(div);
         }
     }
 
+    function getActualArea(pageData) {
+        let rst = [];
+        for (let item of pageData.items) {
+            let area = {Left: 10000, Right: 0, Top: 10000, Bottom: 0};
+            for (let cell of item.cells) {
+                if (cell.area.Left < area.Left) {
+                    area.Left = cell.area.Left;
+                }
+                if (cell.area.Right > area.Right) {
+                    area.Right = cell.area.Right;
+                }
+                if (cell.area.Top < area.Top) {
+                    area.Top = cell.area.Top;
+                }
+                if (cell.area.Bottom > area.Bottom) {
+                    area.Bottom = cell.area.Bottom;
+                }
+            }
+            rst.push(area);
+        }
+        return rst;
+    }
     function closing() {
         //
     }

+ 4 - 1
web/building_saas/report/js/jpc_output.js

@@ -376,7 +376,10 @@ let JpcCanvasOutput = {
         ctx.fillRect(10 + me.offsetX,size[1] + me.offsetY,size[0],10);
     },
     getReportSizeInPixel: function(rptTpl, resolution) {
-        let rst = rptTpl[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE].slice(0);
+        let rst = [8.27, 11.69];
+        if (rptTpl && rptTpl[JV.NODE_PAGE_INFO] && rptTpl[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE]) {
+            rst = rptTpl[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE].slice(0);
+        }
         rst[0] = Math.round(resolution[0] * rst[0]);
         rst[1] = Math.round(resolution[0] * rst[1]);
         return rst;

+ 7 - 3
web/building_saas/report/js/rpt_main.js

@@ -258,10 +258,11 @@ let zTreeOprObj = {
         hintBox.waitBox();
         CommonAjax.postEx("report_api/getReport", params, 15000, true,
             function(result){
+                hintBox.unWaitBox();
                 let pageRst = result;
-                if (pageRst) {
+                let canvas = document.getElementById("rptCanvas");
+                if (pageRst && pageRst.items && pageRst.items.length > 0) {
                     me.resetAfter(pageRst);
-                    let canvas = document.getElementById("rptCanvas");
                     me.currentRptPageRst = pageRst;
                     me.maxPages = pageRst.items.length;
                     me.currentPage = 1;
@@ -274,7 +275,10 @@ let zTreeOprObj = {
                         canvas.height = size[1] + 50;
                     }
                     me.showPage(1, canvas);
-                    hintBox.unWaitBox();
+                } else {
+                    //返回了无数据表
+                    JpcCanvasOutput.cleanCanvas(canvas);
+                    JpcCanvasOutput.drawPageBorder(me.currentRptPageRst, canvas, getScreenDPI());
                 }
             }, function(err){
                 hintBox.unWaitBox();

+ 13 - 4
web/building_saas/report/js/rpt_print.js

@@ -49,7 +49,7 @@ let rptPrintHelper = {
     previewSvgData: function() {
         //
     },
-    buildSvgArr: function (pagesData, offsetX, offsetY) {
+    buildSvgArr: function (pagesData, actAreaArr, offsetX, offsetY) {
         let styles = pagesData[JV.NODE_STYLE_COLLECTION],
             fonts = pagesData[JV.NODE_FONT_COLLECTION],
             controls = pagesData[JV.NODE_CONTROL_COLLECTION]
@@ -64,12 +64,20 @@ let rptPrintHelper = {
         }
         for (let idx = 0; idx < pagesData.items.length; idx++) {
             let page = pagesData.items[idx];
+            let actAreaOffsetX = 0, actAreaOffsetY = 0;
+            if (actAreaArr) {
+                actAreaOffsetX = actAreaArr[idx].Left;
+                actAreaOffsetY = actAreaArr[idx].Top;
+                if (pixelSize[0] > pixelSize[1]) {
+                }
+            }
             let svgPageArr = [];
             svgPageArr.push("<svg width='" + pixelSize[0] + "' height='" + pixelSize[1] + "'>");
             // let adjustY = 0.5 * ((idx + 1) % 2);
             let adjustY = 0.5;
             for (let cell of page.cells) {
-                svgPageArr.push(buildCellSvg(cell, fonts, styles, controls, page[JV.PROP_PAGE_MERGE_BORDER], pagesData[JV.BAND_PROP_MERGE_BAND], offsetX, offsetY, adjustY, canvas, isHtoV, pixelSize));
+                svgPageArr.push(buildCellSvg(cell, fonts, styles, controls, page[JV.PROP_PAGE_MERGE_BORDER], pagesData[JV.BAND_PROP_MERGE_BAND],
+                    offsetX - actAreaOffsetX, offsetY - actAreaOffsetY, adjustY, canvas, isHtoV, pixelSize, actAreaArr[idx]));
             }
             svgPageArr.push("</svg>");
             rst.push(svgPageArr);
@@ -98,7 +106,7 @@ function getActualBorderStyle(cell, styles, mergeBorderStyle, pageBorderArea, bo
     return rst;
 }
 
-function buildCellSvg(cell, fonts, styles, controls, pageMergeBorder, rptMergeBorder, offsetX, offsetY, adjustY, canvas, isHtoV, pixelSize) {
+function buildCellSvg(cell, fonts, styles, controls, pageMergeBorder, rptMergeBorder, offsetX, offsetY, adjustY, canvas, isHtoV, pixelSize, actArea) {
     let rst = [];
     let style = styles[cell[JV.PROP_STYLE]];
     let mergeBandStyle = null;
@@ -116,7 +124,8 @@ function buildCellSvg(cell, fonts, styles, controls, pageMergeBorder, rptMergeBo
     ;
     let HtoVStr = "";
     if (isHtoV) {
-        HtoVStr = ` transform="translate(`+ pixelSize[1] + `,0) rotate(90)"`;
+        //HtoVStr = ` transform="translate(`+ pixelSize[1] + `,0) rotate(90)"`;
+        HtoVStr = ` transform="translate(`+ (actArea.Bottom - actArea.Top + 5) + `,0) rotate(90)"`;
     }
     if (style) {
         let leftBS = getActualBorderStyle(cell, styles, mergeBandStyle, (pageMergeBorder)?pageMergeBorder:rptMergeBorder[JV.PROP_AREA], JV.PROP_LEFT);