Browse Source

Merge branch 'master' of http://192.168.1.12:3000/SmartCost/ConstructionCost

zhangyin 7 years ago
parent
commit
8149e46795
100 changed files with 5269 additions and 1340 deletions
  1. 4 2
      .gitignore
  2. 12 2
      config/gulpConfig.js
  3. 6 5
      lib/bootstrap/bootstrap.min.js
  4. 2 2
      lib/bootstrap/css/bootstrap.min.css
  5. 36 0
      lib/jquery-ui/jquery-ui-datepickerCN.js
  6. 1312 0
      lib/jquery-ui/jquery-ui.css
  7. 13 0
      lib/jquery-ui/jquery-ui.min.js
  8. 1 2
      modules/complementary_glj_lib/models/gljModel.js
  9. 1 0
      modules/complementary_glj_lib/models/schemas.js
  10. 71 49
      modules/glj/controllers/glj_controller.js
  11. 56 28
      modules/glj/models/glj_list_model.js
  12. 2 5
      modules/glj/models/schemas/glj.js
  13. 23 13
      modules/glj/models/unit_price_model.js
  14. 116 0
      modules/main/controllers/quantity_detail_controller.js
  15. 5 1
      modules/main/facade/calc_program_facade.js
  16. 33 0
      modules/main/facade/decimal_facade.js
  17. 2 1
      modules/main/facade/prj_properties_facade.js
  18. 216 297
      modules/ration_glj/facade/quantity_detail_facade.js
  19. 10 2
      modules/main/models/bills.js
  20. 6 5
      modules/main/models/proj_setting_model.js
  21. 6 3
      modules/main/models/project.js
  22. 2 2
      modules/main/models/project_consts.js
  23. 20 17
      modules/main/models/ration.js
  24. 2 2
      modules/main/models/schemas/proj_counter.js
  25. 12 12
      modules/main/models/schemas/proj_setting.js
  26. 8 2
      modules/main/routes/main_route.js
  27. 20 0
      modules/main/routes/quantity_detail_route.js
  28. 2 2
      modules/pm/controllers/copy_proj_controller.js
  29. 15 11
      modules/pm/models/project_model.js
  30. 0 51
      modules/pm/models/project_property_bills_quantity_decimal.js
  31. 126 0
      modules/pm/models/project_property_template.js
  32. 20 3
      modules/ration_glj/controllers/ration_glj_controller.js
  33. 16 13
      modules/ration_glj/facade/glj_calculate_facade.js
  34. 58 15
      modules/ration_glj/facade/ration_glj_facade.js
  35. 2 0
      modules/ration_glj/models/ration_glj.js
  36. 1 0
      modules/ration_glj/routes/ration_glj_route.js
  37. 53 1
      modules/reports/controllers/rpt_controller.js
  38. 2 2
      modules/reports/routes/report_router.js
  39. 43 14
      modules/reports/rpt_component/helper/jpc_helper_area.js
  40. 40 18
      modules/reports/rpt_component/jpc_bill_tab.js
  41. 19 13
      modules/reports/rpt_component/jpc_data.js
  42. 5 2
      modules/reports/rpt_component/jpc_ex.js
  43. 2 1
      modules/reports/rpt_component/jpc_flow_tab.js
  44. 68 15
      modules/reports/util/rpt_construct_data_util.js
  45. 42 12
      modules/reports/util/rpt_excel_util.js
  46. 132 50
      modules/reports/util/rpt_pdf_util.js
  47. 8 1
      modules/users/controllers/login_controller.js
  48. 1 0
      modules/users/models/user_model.js
  49. 2 0
      modules/volume_price/models/volume_price_model.js
  50. 6 0
      modules/volume_price/models/volume_price_schema.js
  51. 0 12
      public/calc_util.js
  52. 0 20
      public/common_util.js
  53. 0 28
      public/debug.js
  54. 1 1
      public/fsUtil.js
  55. 2 2
      public/web/PerfectLoad.js
  56. 20 0
      public/web/common_util.js
  57. 4 1
      public/web/rpt_value_define.js
  58. 13 0
      public/web/scMathUtil.js
  59. 12 2
      public/web/sheet/sheet_common.js
  60. 1 1
      public/web/tree_sheet/tree_sheet_helper.js
  61. 16 0
      public/web/tree_table/tree_table.js
  62. 5 5
      server.js
  63. 2 2
      test/tmp_data/bills_grid_setting.js
  64. 3 82
      test/tmp_data/test_ration_calc/ration_calc_base.js
  65. 115 0
      test/unit/reports/test_cover_01.js
  66. 6 6
      test/unit/reports/test_tpl_09_1.js
  67. 3 0
      tmp/forDeployment.js
  68. 13 0
      web/building_saas/css/main.css
  69. 4 1
      web/building_saas/glj/html/glj_index.html
  70. 0 1
      web/building_saas/glj/js/composition_spread.js
  71. 43 17
      web/building_saas/glj/js/project_glj.js
  72. 6 4
      web/building_saas/glj/js/project_glj_spread.js
  73. BIN
      web/building_saas/img/FirstPageSimple.cur
  74. BIN
      web/building_saas/img/LastPageSimple.cur
  75. BIN
      web/building_saas/img/NextPageSimple.cur
  76. BIN
      web/building_saas/img/PreviousPageSimple.cur
  77. BIN
      web/building_saas/img/baobiao.png
  78. 46 13
      web/building_saas/js/global.js
  79. 189 75
      web/building_saas/main/html/main.html
  80. 3 3
      web/building_saas/main/js/calc/bills_calc.js
  81. 2 1
      web/building_saas/main/js/calc/calc_fees.js
  82. 14 20
      web/building_saas/main/js/controllers/project_controller.js
  83. 107 1
      web/building_saas/main/js/main.js
  84. 8 18
      web/building_saas/main/js/models/bills.js
  85. 613 0
      web/building_saas/main/js/models/calc_base.js
  86. 373 114
      web/building_saas/main/js/models/calc_program.js
  87. 36 21
      web/building_saas/main/js/models/fee_rate.js
  88. 99 10
      web/building_saas/main/js/models/main_consts.js
  89. 38 22
      web/building_saas/main/js/models/project.js
  90. 23 2
      web/building_saas/main/js/models/project_glj.js
  91. 106 68
      web/building_saas/main/js/models/quantity_detail.js
  92. 15 17
      web/building_saas/main/js/models/ration.js
  93. 110 19
      web/building_saas/main/js/models/ration_glj.js
  94. 4 2
      web/building_saas/main/js/models/volume_price.js
  95. 301 0
      web/building_saas/main/js/views/calc_base_view.js
  96. 2 2
      web/building_saas/main/js/views/calc_program_manage.js
  97. 6 4
      web/building_saas/main/js/views/calc_program_view.js
  98. 15 10
      web/building_saas/main/js/views/fee_rate_view.js
  99. 230 57
      web/building_saas/main/js/views/glj_view.js
  100. 0 0
      web/building_saas/main/js/views/glj_view_contextMenu.js

+ 4 - 2
.gitignore

@@ -2,5 +2,7 @@ node_modules/
 .git/
 dist/
 .idea/
-tmp/
-test/unit/logs
+tmp/*.xlsx
+tmp/*.pdf
+test/unit/logs
+*.log

+ 12 - 2
config/gulpConfig.js

@@ -7,6 +7,7 @@ module.exports = {
     common_jspaths:[
         'lib/jquery/jquery-3.2.1.min.js',
         'lib/popper/popper.min.js',
+        'lib/jquery-ui/jquery-ui.min.js',
         'lib/bootstrap/bootstrap.min.js',
         'web/building_saas/js/*.js',
         'public/web/scMathUtil.js',
@@ -31,6 +32,7 @@ module.exports = {
     ],
     main_css:[
         'lib/ztree/css/zTreeStyle.css',
+        'lib/jquery-ui/jquery-ui.css',
         'lib/spreadjs/sheets/css/gc.spread.sheets.excel2013lightGray.10.0.1.css',
         'lib/spreadjs/views/gc.spread.views.dataview.10.0.0.css',
         'lib/jquery-contextmenu/jquery.contextMenu.css'
@@ -38,9 +40,12 @@ module.exports = {
     main_jspaths:[
         'lib/JSExpressionEval_src/*.js',
         '!lib/JSExpressionEval_src/JsHashMap.js',
+        'lib/jquery-ui/jquery-ui-datepickerCN.js',
         'lib/jquery-contextmenu/*.js',
         'lib/lodash/lodash.js',
+        // 'test/tmp_data/test_ration_calc/ration_calc_base.js',
         'web/building_saas/main/js/models/main_consts.js',
+        'public/web/common_util.js',
         'web/building_saas/glj/js/project_glj.js',
         'web/building_saas/glj/js/composition.js',
         'web/building_saas/glj/js/common_spread.js',
@@ -51,6 +56,7 @@ module.exports = {
         'public/web/uuid.js',
         'public/web/sheet/sheet_common.js',
         'web/building_saas/main/js/models/calc_program.js',
+        'web/building_saas/main/js/models/calc_base.js',
         'web/building_saas/main/js/views/calc_program_manage.js',
         'public/web/common_ajax.js',
         'public/web/url_util.js',
@@ -71,10 +77,9 @@ module.exports = {
         'web/building_saas/main/js/models/ration_glj.js',
         'web/building_saas/main/js/models/ration_coe.js',
         'web/building_saas/main/js/models/ration_ass.js',
-        'web/building_saas/main/js/models/volume_price.js',
+        // 'web/building_saas/main/js/models/volume_price.js',
         'web/building_saas/main/js/models/labour_coe.js',
         'public/web/id_tree.js',
-        'test/tmp_data/test_ration_calc/ration_calc_base.js',
         'web/building_saas/main/js/models/cache_tree.js',
         'web/building_saas/main/js/calc/calc_fees.js',
         'web/building_saas/main/js/calc/ration_calc.js',
@@ -87,7 +92,11 @@ module.exports = {
         'web/building_saas/main/js/views/project_info.js',
         'web/building_saas/main/js/views/project_view.js',
         'web/building_saas/main/js/views/options_view.js',
+        'web/building_saas/main/js/views/project_property_bills_quantity_decimal.js',
         'web/building_saas/main/js/views/project_property_decimal_view.js',
+        'web/building_saas/main/js/views/project_property_basicInfo.js',
+        'web/building_saas/main/js/views/project_property_projFeature.js',
+        'web/building_saas/main/js/views/project_property_display_view.js',
         'web/building_saas/main/js/main_ajax.js',
         'web/building_saas/main/js/main.js',
         'web/building_saas/main/js/controllers/project_controller.js',
@@ -110,6 +119,7 @@ module.exports = {
         'web/building_saas/main/js/views/sub_view.js',
         'web/building_saas/main/js/views/fee_rate_view.js',
         'web/building_saas/main/js/views/sub_fee_rate_views.js',
+        'web/building_saas/main/js/views/calc_base_view.js',
         'web/building_saas/main/js/views/project_property_labour_coe_view.js'
     ]
 }

File diff suppressed because it is too large
+ 6 - 5
lib/bootstrap/bootstrap.min.js


File diff suppressed because it is too large
+ 2 - 2
lib/bootstrap/css/bootstrap.min.css


+ 36 - 0
lib/jquery-ui/jquery-ui-datepickerCN.js

@@ -0,0 +1,36 @@
+/**
+ * Created by Zhong on 2017/11/27.
+ */
+jQuery(function($){
+    $.datepicker.regional['zh-CN'] = {
+        clearText: '清除',
+        clearStatus: '清除已选日期',
+        closeText: '关闭',
+        closeStatus: '不改变当前选择',
+        prevText: '< 上月',
+        prevStatus: '显示上月',
+        prevBigText: '<<',
+        prevBigStatus: '显示上一年',
+        nextText: '下月>',
+        nextStatus: '显示下月',
+        nextBigText: '>>',
+        nextBigStatus: '显示下一年',
+        currentText: '今天',
+        currentStatus: '显示本月',
+        monthNames: ['一月','二月','三月','四月','五月','六月', '七月','八月','九月','十月','十一月','十二月'],
+        monthNamesShort: ['一月','二月','三月','四月','五月','六月', '七月','八月','九月','十月','十一月','十二月'],
+        monthStatus: '选择月份',
+        yearStatus: '选择年份',
+        weekHeader: '周',
+        weekStatus: '年内周次',
+        dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'],
+        dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'],
+        dayNamesMin: ['日','一','二','三','四','五','六'],
+        dayStatus: '设置 DD 为一周起始',
+        dateStatus: '选择 m月 d日, DD',
+        dateFormat: 'yy-mm-dd',
+        firstDay: 1,
+        initStatus: '请选择日期',
+        isRTL: false};
+    $.datepicker.setDefaults($.datepicker.regional['zh-CN']);
+});

File diff suppressed because it is too large
+ 1312 - 0
lib/jquery-ui/jquery-ui.css


File diff suppressed because it is too large
+ 13 - 0
lib/jquery-ui/jquery-ui.min.js


+ 1 - 2
modules/complementary_glj_lib/models/gljModel.js

@@ -262,11 +262,10 @@ class GljDao {
      */
     async getComponent(gljId) {
         let result = [];
-        let libGljData = await complementaryGljModel.find({ID: gljId});
+        let libGljData = await complementaryGljModel.findOne({ID: gljId});
         if (libGljData === null || libGljData.component.length <= 0) {
             return result;
         }
-
         // 标准工料机库
         let componentIdListStd = [];
         // 补充工料机库

+ 1 - 0
modules/complementary_glj_lib/models/schemas.js

@@ -52,6 +52,7 @@ let stdGljSchema = new Schema({
     gljType: Number,
     shortName: String,
     unit: String,
+    adjCoe: Number,
     component: [stdGjlComponentSchema]
 },{versionKey: false});
 

+ 71 - 49
modules/glj/controllers/glj_controller.js

@@ -12,6 +12,7 @@ import UnitPriceModel from "../models/unit_price_model";
 import MixRatioModel from "../models/mix_ratio_model";
 import UnitPriceFileModel from "../models/unit_price_file_model";
 let logger = require("../../../logs/log_helper").logger;
+let consts = require('../../main/models/project_consts');
 
 const ProjectModel = require('../../pm/models/project_model').project;
 class GLJController extends BaseController {
@@ -37,59 +38,23 @@ class GLJController extends BaseController {
         // 标段id
         let projectId = request.body.project_id;
         projectId = parseInt(projectId);
-        let responseData = {
-            err: 0,
-            msg: '',
-            data: {}
-        };
-        try {
-            if (isNaN(projectId) || projectId <= 0) {
-                throw '标段id有误';
-            }
-
-            // 获取标段对应的单价文件id
-            let unitPriceFileId = await ProjectModel.getUnitPriceFileId(projectId);
-            if (unitPriceFileId <= 0) {
-                throw '没有对应的单价文件';
-            }
-
-            // 获取使用该单价文件的项目数据
-            let tenderData = await ProjectModel.getTenderByUnitPriceFileId(unitPriceFileId);
-            let usedTenderList = [];
-            let usedUnitPriceInfo = {};
-            if (tenderData !== null) {
-                for (let tmp of tenderData) {
-                    usedTenderList.push(tmp.name);
-                    usedUnitPriceInfo.name = tmp.property.unitPriceFile.name;
-                    usedUnitPriceInfo.id = tmp.property.unitPriceFile.id;
-                }
-            }
-
-            // 先获取对应标段的项目工料机数据
-            let gljListModel = new GLJListModel();
-            let [gljList, mixRatioConnectData,mixRationMap] = await gljListModel.getListByProjectId(projectId, unitPriceFileId);
-
-            responseData.data.gljList = gljList;
-            responseData.data.mixRatioConnectData = mixRatioConnectData;
-            responseData.data.mixRatioMap = mixRationMap;
-            responseData.data.usedTenderList = usedTenderList;
-            responseData.data.constData = {
-                materialIdList: gljListModel.materialIdList,
-                ownCompositionTypes: gljListModel.ownCompositionTypes,
-                hostname: request.hostname,
-                roomId: unitPriceFileId,
-                GLJTypeConst: JSON.stringify(GLJTypeConst),
-                usedUnitPriceInfo: usedUnitPriceInfo
-            };
-        } catch (error) {
-            responseData.err = 1;
-            responseData.msg = error;
-        }
-
+        let responseData = await getGLJListByProjectID(projectId);
+        responseData.hostname = request.hostname;
         response.json(responseData);
     }
 
     /**
+     * project getData 接口
+     * @param projectID
+     * @param callback
+     */
+    getData(projectID, callback){
+        getGLJListByProjectID(projectID).then(function(result){
+            callback(result.err,consts.projectConst.PROJECTGLJ,result.data);
+        })
+    }
+
+    /**
      * 更新数据
      *
      * @param {object} request
@@ -588,4 +553,61 @@ class GLJController extends BaseController {
 
 }
 
+/**
+ * 取项目工料机列表
+ * @param projectId
+ * @returns {Promise.<void>}
+ */
+async function getGLJListByProjectID(projectId){
+    let responseData = {
+        err: 0,
+        msg: '',
+        data: {}
+    };
+    try {
+        if (isNaN(projectId) || projectId <= 0) {
+            throw '标段id有误';
+        }
+
+        // 获取标段对应的单价文件id
+        let unitPriceFileId = await ProjectModel.getUnitPriceFileId(projectId);
+        if (unitPriceFileId <= 0) {
+            throw '没有对应的单价文件';
+        }
+
+        // 获取使用该单价文件的项目数据
+        let tenderData = await ProjectModel.getTenderByUnitPriceFileId(unitPriceFileId);
+        let usedTenderList = [];
+        let usedUnitPriceInfo = {};
+        if (tenderData !== null) {
+            for (let tmp of tenderData) {
+                usedTenderList.push(tmp.name);
+                usedUnitPriceInfo.name = tmp.property.unitPriceFile.name;
+                usedUnitPriceInfo.id = tmp.property.unitPriceFile.id;
+            }
+        }
+
+        // 先获取对应标段的项目工料机数据
+        let gljListModel = new GLJListModel();
+        let [gljList, mixRatioConnectData,mixRationMap] = await gljListModel.getListByProjectId(projectId, unitPriceFileId);
+
+        responseData.data.gljList = gljList;
+        responseData.data.mixRatioConnectData = mixRatioConnectData;
+        responseData.data.mixRatioMap = mixRationMap;
+        responseData.data.usedTenderList = usedTenderList;
+        responseData.data.constData = {
+            materialIdList: gljListModel.materialIdList,
+            ownCompositionTypes: gljListModel.ownCompositionTypes,
+            roomId: unitPriceFileId,
+            GLJTypeConst: JSON.stringify(GLJTypeConst),
+            usedUnitPriceInfo: usedUnitPriceInfo
+        };
+    } catch (error) {
+        console.log(error);
+        responseData.err = 1;
+        responseData.msg = error;
+    }
+    return responseData;
+}
+
 export default GLJController;

+ 56 - 28
modules/glj/models/glj_list_model.js

@@ -17,6 +17,7 @@ import MixRatioModel from "./mix_ratio_model";
 import GljModel from "../../complementary_glj_lib/models/gljModel";
 const ProjectModel = require('../../pm/models/project_model').project;
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
+import decimal_facade from "../../main/facade/decimal_facade";
 
 class GLJListModel extends BaseModel {
 
@@ -75,8 +76,11 @@ class GLJListModel extends BaseModel {
      */
     async getListByProjectId(projectId, unitPriceFileId) {
         let gljData = null;
+        let decimal =await decimal_facade.getProjectDecimal(projectId);
+        let quantity_decimal = decimal&&decimal.glj.quantity?decimal.glj.quantity:6;//取消耗量保留小数据位,默认6位
         let mixRatioConnectData = {};
         let mixRationMap={};
+        let keyMap={};
         try {
             // 首先获取对应标段下所有的项目工料机数据
             let condition = {project_id: projectId};
@@ -96,6 +100,8 @@ class GLJListModel extends BaseModel {
             let gljIdList = [];
             for(let tmp of gljData) {
                 gljIdList.push(tmp.id);
+                let c_key = this.getIndex(tmp,['code','name','specs','unit','type']);
+                keyMap[tmp.id] = c_key; //工料机ID和连接key的对照表;
             }
             // 从定额工料机库中获取消耗量
             condition = {
@@ -104,22 +110,25 @@ class GLJListModel extends BaseModel {
             };
             let quantityData = await RationGLJFacade.getQuantityByProjectGLJ(condition);
             let quantityList = {};
-            // 整理数据
+            // 整理数据 得到总定额消耗量
             for (let tmp of quantityData) {
                 let tmpNum = parseFloat(tmp.rationQuantity);
                 tmpNum = isNaN(tmpNum) ||tmpNum==0? 1 : tmpNum;
-                if (quantityList[tmp.projectGLJID] === undefined) {
-                    quantityList[tmp.projectGLJID] = tmp.quantity * tmpNum;
+                let tmp_con_key = keyMap[tmp.projectGLJID];
+
+                if (quantityList[tmp_con_key] === undefined) {
+                    quantityList[tmp_con_key] = scMathUtil.roundTo(tmp.quantity * tmpNum,-quantity_decimal);
                 } else {
-                    quantityList[tmp.projectGLJID] += tmp.quantity * tmpNum;
+                    quantityList[tmp_con_key] = scMathUtil.roundTo(quantityList[tmp_con_key]+tmp.quantity * tmpNum,-quantity_decimal);
                 }
             }
             // 整理获取有组成物的项目工料机的数据
             let connect_keys = [];
             for(let tmp of gljData) {
-                // 有组成物的类型且消耗量大于0才查找
-                if (quantityList[tmp.id] !== undefined) {
-                   let key = this.getIndex(tmp,['code','name','specs','unit','type']);
+                // 有组成物的类型才查找
+                let key = keyMap[tmp.id];
+                if (quantityList[key]!=undefined&&(tmp.type === GLJTypeConst.CONCRETE || tmp.type === GLJTypeConst.MORTAR ||
+                    tmp.type === GLJTypeConst.MIX_RATIO || tmp.type === GLJTypeConst.GENERAL_MACHINE)){
                     connect_keys.push(key);
                 }
             }
@@ -132,11 +141,18 @@ class GLJListModel extends BaseModel {
                 let mixRatioList = await mixRatioModel.findDataByCondition(condition, null, false);
                 for (let tmp of mixRatioList) {
                    let t_index = tmp.connect_key;
-                    let consumption=parseFloat(tmp.consumption);
-                    totalComposition[t_index] = totalComposition[t_index] === undefined ? consumption :
+                   let consumption=parseFloat(tmp.consumption);
+                   let m_index = this.getIndex(tmp,['code','name','specs','unit','type']);
+                   let r_quantity = quantityList[t_index]?quantityList[t_index]:0;
+                   if(quantityList[m_index]!==undefined){
+                       quantityList[m_index]=quantityList[m_index]+r_quantity*consumption;
+                   }else {
+                       quantityList[m_index] = r_quantity*consumption;
+                   }
+                    quantityList[m_index] = scMathUtil.roundTo(quantityList[m_index],-quantity_decimal);
+                /*    totalComposition[t_index] = totalComposition[t_index] === undefined ? consumption :
                         totalComposition[t_index] + consumption;
-                    totalComposition[t_index] = scMathUtil.roundTo(totalComposition[t_index], -4);
-
+                    totalComposition[t_index] = scMathUtil.roundTo(totalComposition[t_index], -quantity_decimal);*/
                     if (mixRatioData[t_index] !== undefined) {
                         mixRatioData[t_index].push(tmp);
                     } else {
@@ -209,11 +225,11 @@ class GLJListModel extends BaseModel {
             let gljId = glj.glj_id + '';
             let projectGljId = glj.id + '';
             // 消耗量赋值
-            glj.quantity = quantityList[projectGljId] !== undefined ? quantityList[projectGljId] : 0;
+         /*   glj.quantity = quantityList[projectGljId] !== undefined ? quantityList[projectGljId] : 0;
             glj.quantity = totalComposition[g_index] !== undefined ? totalComposition[g_index] : glj.quantity;
             glj.quantity = compositionConsumption[g_index] !== undefined ?  glj.quantity + compositionConsumption[g_index] : glj.quantity;
-            glj.quantity = scMathUtil.roundTo(parseFloat(glj.quantity), -3);
-
+            glj.quantity = scMathUtil.roundTo(parseFloat(glj.quantity), -3);*/
+            glj.quantity = quantityList[g_index]?quantityList[g_index]:0;
             // 组成物数据
             gljList[index].ratio_data = mixRatioData[g_index] !== undefined ? mixRatioData[g_index] : [];
             //因为schema中设置base_price 为string 类型,所以要通过中间变量转换为数字再做计算,不然会自动变成字符串类型
@@ -227,7 +243,7 @@ class GLJListModel extends BaseModel {
         glj.unit_price.base_price = glj_basePrice;
         glj.unit_price.market_price = scMathUtil.roundTo(parseFloat(glj.unit_price.market_price), -2);
         // 计算调整基价
-        switch (glj.unit_price.type + '') {
+     /*   switch (glj.unit_price.type + '') {
             // 人工: 调整基价=基价单价*调整系数
             case GLJTypeConst.LABOUR:
                 glj.adjust_price = scMathUtil.roundTo(parseFloat(glj.adjustment * glj_basePrice), -2);
@@ -239,7 +255,7 @@ class GLJListModel extends BaseModel {
             // 材料、主材、设备
             default:
                 glj.adjust_price = glj.unit_price.base_price;
-        }
+        }*/
     }
 
     /**
@@ -281,6 +297,7 @@ class GLJListModel extends BaseModel {
                 throw '没有对应的单价文件';
             }
             let CompositionGLJ=[];
+            let unitPriceModel = new UnitPriceModel();
             // 判断类型,如果是混凝土、砂浆或者配合比则查找对应的组成物(前提是没有对应的项目工料机数据)
             if (data.type === GLJTypeConst.CONCRETE || data.type === GLJTypeConst.MORTAR ||
                 data.type === GLJTypeConst.MIX_RATIO || data.type === GLJTypeConst.GENERAL_MACHINE) {
@@ -289,11 +306,20 @@ class GLJListModel extends BaseModel {
                     await this.compositionInit(data, unitPriceFileId);
                 }
                 CompositionGLJ=await this.getCompositionGLJByData(data,unitPriceFileId);
+                if(isAddProjectGLJ==false&&CompositionGLJ.length==0){//如果不是新增,并且是有组成物的类型但又没有发现组成物的情况下,有可能是错误数据,重新在库中查找一下组成物,有则插入
+                    await this.compositionInit(data, unitPriceFileId);
+                    CompositionGLJ=await this.getCompositionGLJByData(data,unitPriceFileId);
+
+                    if(CompositionGLJ.length>0){//如果这次发现又有组成物了,则把旧的单价数据删除,在后面的操作中会重新增加
+                        let de_condition ={unit_price_file_id: unitPriceFileId,code:data.code,name:data.name,unit:data.unit,type:data.type};
+                        data.specs!=null&&data.specs!=undefined&&data.specs!=""?de_condition.specs = data.specs:de_condition;
+                        await unitPriceModel.db.delete(de_condition);
+                    }
+                }
             }
             projectGljData.subList=CompositionGLJ;
 
             // 新增单价文件
-            let unitPriceModel = new UnitPriceModel();
             let [unitPriceInsertData, isAdd] = await unitPriceModel.addUnitPrice(data, unitPriceFileId);
 
             if (!unitPriceInsertData) {
@@ -350,7 +376,7 @@ class GLJListModel extends BaseModel {
         //查找单价信息,有则返回,没有则新增并返回
         let unitPriceFileId = unitPriceFile.id;
         let unitPriceModel = new UnitPriceModel();
-        let [unitPriceData, isAdd] = await unitPriceModel.addUnitPrice(data, unitPriceFileId);
+        let [unitPriceData, isAdd] = await unitPriceModel.addUnitPrice(data, unitPriceFileId,"modify");
         let gljData={};
         if(isAdd){ //如果是新增,则新增一条新的项目工料机
             data.code = unitPriceData.code;
@@ -448,7 +474,7 @@ class GLJListModel extends BaseModel {
             let unitPriceModel = new UnitPriceModel();
 
             let gljCount = gljListData.length;
-            let [unitPriceData, isAdd] = await unitPriceModel.addUnitPrice(updateData, unitPriceFileId, gljCount);
+            let [unitPriceData, isAdd] = await unitPriceModel.addUnitPrice(updateData, unitPriceFileId,"modify", gljCount);
 
             // 判断是否已存在对应数据
             let includeField = [
@@ -534,7 +560,6 @@ class GLJListModel extends BaseModel {
         // 查找对应组成物的项目工料机数据
         let indexs=['code','name','specs','unit','type'];
         let [projectGljList, compositionGljList] = await this.getCompositionGLJList(gljId, projectId, indexs, fromTable);
-
         // 整理配合比待插入数据
         let mixRatioInsertData = [];
         for (let tmp of compositionGljList) {
@@ -556,13 +581,16 @@ class GLJListModel extends BaseModel {
 
         // 插入配合比表
         // 因为有可能项目工料机与单价数据已存在,但配合比数据不存在,所以先插入配合比,后续判断如果存在项目工料机则可以省下数据库操作
-        let mixRatioModel = new MixRatioModel();
-        let addMixRatioResult = await mixRatioModel.add(mixRatioInsertData);
-        if (!addMixRatioResult) {
-            throw '组成物插入单价数据失败!';
+        if(mixRatioInsertData.length>0){
+            let mixRatioModel = new MixRatioModel();
+            let addMixRatioResult = await mixRatioModel.add(mixRatioInsertData);
+            if (!addMixRatioResult) {
+                throw '组成物插入单价数据失败!';
+            }
         }
+        let pglj_length = projectGljList instanceof Array ? projectGljList.length:Object.getOwnPropertyNames(projectGljList).length;
         // 如果已经存在则后续操作停止
-        if(Object.getOwnPropertyNames(projectGljList).length === compositionGljList.length) {
+        if(pglj_length === compositionGljList.length) {
             return
         }
 
@@ -583,6 +611,7 @@ class GLJListModel extends BaseModel {
                 specs: tmp.specs,
                 unit: tmp.unit === undefined ? '' : tmp.unit,
                 type: tmp.gljType,
+                adjCoe:tmp.adjCoe,
                 original_code:tmp.code
             };
             gljInsertData.push(gljData);
@@ -675,9 +704,8 @@ class GLJListModel extends BaseModel {
         // 获取对应的组成物数据
         let gljListModel = fromTable === 'std' ? new STDGLJLibGLJListModel() : new GljModel();
         let componentGljList = await gljListModel.getComponent(gljId);
-
         if (componentGljList.length <= 0) {
-            throw '不存在对应的组成物';
+            return [{},[]];
         }
 
         let codeList = [];
@@ -726,7 +754,7 @@ class GLJListModel extends BaseModel {
         let typeList = [];
         let unitList = [];
         if(mixRatios.length<=0){
-            throw  '不存在对应的组成物';
+            return [[],[],[]];
         }
         let mixRatioData={};
         for(let tmp of mixRatios) {

+ 2 - 5
modules/glj/models/schemas/glj.js

@@ -62,11 +62,8 @@ let modelSchema = {
         type: Number,
         default: 0
     },
-    // 调整系数
-    adjustment: {
-        type: Number,
-        default: 1
-    },
+    // 调整系数ID
+    adjCoe: Number,
 
     // 规格型号
     specs: {

+ 23 - 13
modules/glj/models/unit_price_model.js

@@ -80,7 +80,7 @@ class UnitPriceModel extends BaseModel {
      * @param {Number} gljCount
      * @return {Promise} 返回数据以及是否新增
      */
-    async addUnitPrice(data, unitPriceFileId, gljCount = 0) {
+    async addUnitPrice(data, unitPriceFileId,operation='add', gljCount = 0) {
         if (data.original_code===undefined||data.code === undefined || data.project_id === undefined || data.name === undefined
             || data.market_price === undefined) {
             return [null, false];
@@ -88,7 +88,12 @@ class UnitPriceModel extends BaseModel {
         // 先查找是否有原始code相同的记录
         let unitPriceData = await this.findDataByCondition({original_code: data.original_code, unit_price_file_id: unitPriceFileId}, null, false);
         // 如果有记录,判断是否存在一样的名称,单位...等,有则直接返回数据
-        let unitPrice =  this.isPropertyInclude(unitPriceData,['name','specs','unit','type'],data);
+        let unitPrice=null;
+        if(operation=='add'){//新增操作时,要把code也一起判断,是否完全一样
+            unitPrice =  this.isPropertyInclude(unitPriceData,['code','name','specs','unit','type'],data);
+        }else {//修改操作时,code不用加入判断,因为code是需要改变的
+            unitPrice =  this.isPropertyInclude(unitPriceData,['name','specs','unit','type'],data);
+        }
         if(unitPrice){
             return [unitPrice, false];
         }
@@ -111,11 +116,12 @@ class UnitPriceModel extends BaseModel {
             unit:data.unit,
             type: data.type,
             short_name: data.shortName !== undefined ? data.shortName : '',
-            glj_id: data.glj_id
+            glj_id: data.glj_id,
+            is_add:0
         };
-
-        // 如果原始编码能找到,但不存在一样的编号,名称,单位.更改 等,更改code和添加新增标记
-        if (unitPriceData&&unitPriceData.length>0) {
+        if(data.from=='cpt'){//如果是来自补充工料机,则都添加新增标记
+            insertData.is_add=1;
+        }else if (unitPriceData&&unitPriceData.length>0) {// 如果原始编码能找到,但不存在一样的编号,名称,单位.型号等,更改code和添加新增标记
             insertData.code = data.original_code+"-"+unitPriceData.length;
             insertData.is_add=1;
         }
@@ -322,21 +328,27 @@ class UnitPriceModel extends BaseModel {
         currentUnitList = JSON.parse(currentUnitList);
 
         let codeList = [];
+        let nameList =[];
         for (let tmp of currentUnitList) {
             if (codeList.indexOf(tmp.code) >= 0) {
                 continue;
             }
             codeList.push(tmp.code);
+            if(nameList.indexOf(tmp.name)>=0){
+                continue
+            }
+            nameList.push(tmp.name);
         }
 
-        // 查找即将更替的单价文件是否存在对应的工料机数据
-        let condition = {unit_price_file_id: changeUnitPriceId, code: {"$in": codeList}};
-        let targetUnitList = await this.findDataByCondition(condition, null, false, 'code');
+        // 查找即将更替的单价文件是否存在对应的工料机数据 -- (这里只根据code和名称初步过滤,因为其它的几项更改的概率不大,在下一步的比较中再精确匹配)
+        let condition = {unit_price_file_id: changeUnitPriceId, code: {"$in": codeList},name:{"$in": nameList}};
+        let targetUnitList = await this.findDataByCondition(condition, null, false, ['code','name','specs','unit','type']);
 
         // 如果没有重叠的数据则原有的数据都复制一份
         let insertData = [];
         for (let tmp of currentUnitList) {
-            if (targetUnitList !== null && targetUnitList[tmp.code] !== undefined) {
+            let t_index = this.getIndex(tmp,['code','name','specs','unit','type']);
+            if (targetUnitList !== null && targetUnitList[t_index] !== undefined) {
                 continue;
             }
             // 删除原有id信息
@@ -345,9 +357,7 @@ class UnitPriceModel extends BaseModel {
             tmp.unit_price_file_id = changeUnitPriceId;
             insertData.push(tmp);
         }
-
-        return insertData.length > 0 ? this.add(currentUnitList) : true;
-
+        return insertData.length > 0 ? this.add(insertData) : true;
     }
 
 

+ 116 - 0
modules/main/controllers/quantity_detail_controller.js

@@ -0,0 +1,116 @@
+/**
+ * Created by zhang on 2017/11/24.
+ */
+
+var quantity_detail_data = require('../facade/quantity_detail_facade');
+let logger = require("../../../logs/log_helper").logger;
+module.exports={
+    save:save,
+    update:update,
+    updateRegex:updateRegex,
+    insertRecode:insertRecode,
+    deleteRecode:deleteRecode,
+    swapRow:swapRow
+}
+
+async function save(req, res) {
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let datas= await quantity_detail_data.saveQuantityDetail(data);
+        result.data=datas;
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+    res.json(result);
+}
+
+async function update(req, res) {
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let datas= await quantity_detail_data.update(data);
+        result.data=datas;
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+    res.json(result);
+}
+
+async function updateRegex(req, res) {
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let datas= await quantity_detail_data.updateRegex(data);
+        result.data=datas;
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+    res.json(result);
+}
+
+async function insertRecode(req, res) {
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let datas= await quantity_detail_data.insertRecode(data);
+        result.data=datas;
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+    res.json(result);
+}
+
+async function deleteRecode(req, res) {
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let datas= await quantity_detail_data.deleteRecode(data);
+        result.data=datas;
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+    res.json(result);
+}
+
+async function swapRow(req, res) {
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let datas= await quantity_detail_data.swapRow(data);
+        result.data=datas;
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+    res.json(result);
+}

+ 5 - 1
modules/main/facade/calc_program_facade.js

@@ -85,7 +85,11 @@ function getData(projectID, callback) {
 
 // 统一的 save() 方法供project调用
 function save (user_id, datas, callback) {
-    projectCalcProgramsModel.update({"projectID": 553}, {"libName":"goo—test"}, callback(null, {data: 'test'}));
+    let returnData ={
+        moduleName:'calc_program',
+        data:{}
+    };
+    projectCalcProgramsModel.update({"projectID": 553}, {"libName":"goo—test"}, callback(null, returnData));
 }
 
 function saveCalcItem(dataObj, callback) {

+ 33 - 0
modules/main/facade/decimal_facade.js

@@ -0,0 +1,33 @@
+/**
+ * Created by zhang on 2017/11/22.
+ */
+let projectsModel = require("../../pm/models/project_schema");
+let _ = require('lodash');
+
+module.exports ={
+    getProjectDecimal:getProjectDecimal,
+    getBillsQuantityDecimal:getBillsQuantityDecimal
+}
+
+async function getProjectDecimal(projectID) {
+    let decimal = null;
+    let project =await projectsModel.findOne({ID:projectID});
+    if(project){
+        decimal = project.property.decimal
+    }
+    return decimal;
+}
+
+async function getBillsQuantityDecimal(projectID,unit) {
+    let decimal = null;
+    let project =await projectsModel.findOne({ID:projectID});
+    if(project){
+        let billsQuantityDecimal = project.property.billsQuantityDecimal;
+        let el = _.find(billsQuantityDecimal,{'unit':unit});
+        if(!el){
+            el = billsQuantityDecimal[0];
+        }
+        decimal = el.decimal
+    }
+    return decimal;
+}

+ 2 - 1
modules/main/facade/prj_properties_facade.js

@@ -5,6 +5,7 @@ let mongoose = require("mongoose");
 let prj_prop_mdl = mongoose.model("project_property");
 let std_prj_prop_mdl = mongoose.model("cfg_prj_property");
 
+
 module.exports = {
     createPrjProperties: createPrjProperties,
     removePrjProperties : removePrjProperties,
@@ -126,4 +127,4 @@ function addOrChangePropertyWithCallBack(prjId, newItem, cb) {
 
 function removeOnePropertyWithCallBack(prjId, key, cb) {
     prj_prop_mdl.update({"projectID": prjId}, {"$pull": {"properties": {"key": key}}}, cb);
-}
+}

+ 216 - 297
modules/ration_glj/facade/quantity_detail_facade.js

@@ -15,6 +15,7 @@ const uuidV1 = require('uuid/v1');
 let ration_model = mongoose.model('ration');
 let bill_model=mongoose.model("bills");
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
+let decimal_facade = require('./decimal_facade');
 
 module.exports={
     save:save,
@@ -22,58 +23,143 @@ module.exports={
     deleteByRation:deleteByRation,
     deleteByBill:deleteByBill,
     quantityEditChecking:quantityEditChecking,
+    saveQuantityDetail:saveQuantityDetail,
+    update:update,
+    updateRegex:updateRegex,
+    insertRecode:insertRecode,
+    deleteRecode:deleteRecode,
+    swapRow:swapRow
 };
 
 let operationMap={
-    'ut_create':create_quantity_detail,
     'ut_update':update_quantity_detail,
-    'ut_delete':delete_quantity_detail
 };
 
 let updateFunctionMap = {
-    'normalUpdate':normalUpdate,
-    'updateQuantityRegex':updateQuantityRegex,
     'insertRecode':insertRecode
 }
 
-function create_quantity_detail(user_id,datas) {
-    return function (callback) {
-        let doc = datas.doc;
-        doc.ID = uuidV1();
-        if(doc.hasOwnProperty('regex')){
-            insertRecodeWithReg(doc).then(function (resultObject) {
-                if(resultObject.err){
-                    callback(null,{
-                        moduleName:consts.projectConst.QUANTITY_DETAIL,
-                        err:{
-                            message:result.err.message
-                        }
-                    });
-                } else {
-                    callback(null,resultObject.return_list);
-                }
-            })
-        }else {
-            createNormalRecode(doc,callback)
-        }
+async function saveQuantityDetail(datas) {
+    console.log(datas);
+    let doc = datas;
+    doc.ID = uuidV1();
+    if(doc.refreshQuantity==false){ //如果选择了不替换工程量,则清空
+        await cleanQuantityDetail(doc);
+        return
+    }
+    if(doc.hasOwnProperty('regex')){
+       return await insertRecodeWithReg(doc);
+    }else {
+        return await createNormalRecode(doc);
+    }
+}
 
+async function update(datas) {
+    let doc = datas.doc;
+    if(doc.hasOwnProperty('isSummation')){
+        return await doIsSummationUpdate(datas.query,doc);
+    }else {
+       await quantity_detail_model.update(datas.query,doc);
+       return datas;
     }
 }
 
-function insertRecode(user_id,datas) {
-    return function (callback) {
-        let doc = datas.doc;
-        doc.ID = uuidV1();
-        doInsertRecode(doc).then(function (result) {
-            //console.log(result);
-            if(result.err){
-                callback(result.err,'')
-            }else {
-                callback(null,result.returndata)
-            }
+async function swapRow(datas) {
+    for(let task of datas){
+        await update(task);
+    }
+    return {refreshList:datas};
+}
+
+async function updateRegex(datas) {
+    let resultObjec ={};
 
-        })
+    let detailList = [];
+    let node ={};
+    let decimal =await decimal_facade.getProjectDecimal(datas.query.projectID);
+    if(datas.query.hasOwnProperty('rationID')){
+        detailList = await quantity_detail_model.find({'projectID':datas.query.projectID,'rationID':datas.query.rationID}).sort('seq').exec();
+        node.type = consts.projectConst.RATION;
+        node.ID = datas.query.rationID;
+    }else {
+        detailList = await quantity_detail_model.find({'projectID':datas.query.projectID,'billID':datas.query.billID}).sort('seq').exec();
+        node.type = consts.projectConst.BILLS;
+        node.ID=datas.query.billID;
     }
+    let regex;
+    let result;
+    if(datas.doc.regex==null){
+        result=0;
+        datas.doc.referenceIndexs=[];
+    }else {
+        regex = datas.doc.regex.toUpperCase();
+        let referenceIndexs = datas.doc.referenceIndexs;
+        result = getEvalResult(referenceIndexs,detailList,regex,decimal);
+    }
+    detailList[datas.query.index].result =result;
+    detailList[datas.query.index].regex=datas.doc.regex;
+    detailList[datas.query.index].referenceIndexs =datas.doc.referenceIndexs;
+    let updateTasks =[];
+    datas.doc.result=result;
+    updateTasks.push({
+        query:{
+            ID:datas.query.ID,
+            projectID:datas.query.projectID
+        },
+        doc:datas.doc
+    })
+    updateReferenceRecode(datas.query.index+1,detailList,updateTasks);
+    await quantity_detail_model.bulkWrite(generateUpdateTaks(updateTasks));
+    resultObjec.refreshList=updateTasks;
+    if(datas.query.refreshQuantity==true){
+        let data = {};
+        data.quantity = await summateResults(datas.query,detailList,decimal);
+        data.isFromDetail = 1;
+        node.data = data;
+        resultObjec.node = node;
+    }
+    return resultObjec;
+
+}
+
+
+async function cleanQuantityDetail(doc) {
+    let query = {
+        projectID:doc.projectID
+    }
+    if(doc.hasOwnProperty('rationID')){
+        query.rationID = doc.rationID;
+    }else {
+        query.billID = doc.billID;
+    }
+    await quantity_detail_model.deleteMany(query);
+}
+
+
+async function insertRecode(doc){
+    let query = {
+        projectID:doc.projectID,
+        seq : { $gte: doc.seq }
+    }
+    if(doc.hasOwnProperty('rationID')){
+        query.rationID = doc.rationID;
+    }else {
+        query.billID = doc.billID;
+    }
+    let quantity_detail_List = await getDatailList(doc,{data:{}});
+    let update_task = getUpdateReferenceTask(quantity_detail_List,doc.seq,1);
+    await quantity_detail_model.update(query,{$inc:{seq:1}},{multi: true});
+    if(update_task.length>0){
+        await quantity_detail_model.bulkWrite(generateUpdateTaks(update_task));
+    }
+    doc.ID = uuidV1();
+    let newrecode = await quantity_detail_model.create(doc);
+    let data={
+        doc:newrecode,
+        resort:true,
+        update_task:update_task
+    }
+    return data;
 }
 
 async function doInsertRecode(doc) {
@@ -150,229 +236,78 @@ function getUpdateReferenceTask(quantity_detail_List,seq,re) {
 }
 
 
-function createNormalRecode(doc,callback) {
-    quantity_detail_model.create(doc,(err,result)=>{
-        if(err){
-            callback(err,null);
-        }else {
-            console.log(result);
-            let returndata ={
-                updateTpye:commonConsts.UT_CREATE,
-                moduleName:consts.projectConst.QUANTITY_DETAIL,
-                data:result
-            }
-            callback(null,returndata)
-        }
-    });
+async function createNormalRecode(doc) {
+    let result ={};
+    result.newRecord= await quantity_detail_model.create(doc);
+    return result;
 }
 
 
 async function insertRecodeWithReg (doc) {
-    let returnObjec={
-        err:null,
-        return_list:[]
-    }
+    let returnObjec={}
     try {
-        let returnData={
-            moduleName:'',
-            data:{
-                updateTpye:commonConsts.UT_UPDATE,
-            }
-        }
+        let decimal =await decimal_facade.getProjectDecimal(doc.projectID);
         let regex = doc.regex.toUpperCase();
         let referenceIndexs = doc.referenceIndexs;
-        let detailList = await getDatailList(doc,returnData);
-        doc.result =getEvalResult(referenceIndexs,detailList,regex);
+        let detailList = await getDatailList(doc,returnObjec);
+        doc.result =getEvalResult(referenceIndexs,detailList,regex,decimal);
         let refreshQuantity =false;
         if(doc.refreshQuantity==true){
             refreshQuantity = true;
         }
         delete doc.refreshQuantity;
-        let newRecode = await quantity_detail_model.create(doc) ;
-        detailList.push(newRecode);
+        let newRecord = await quantity_detail_model.create(doc) ;
+        detailList.push(newRecord);
         if(refreshQuantity==true){
-            returnData.data.quantity = await summateResuts(doc,detailList);
-            returnData.data.quantityRefresh = true;
-            returnObjec.return_list.push(returnData);
+            let data = {};
+            data.quantity = await summateResults(doc,detailList,decimal);
+            data.isFromDetail = 1;
+            returnObjec.node.data = data;
         }
-        returnObjec.return_list.push({
-            updateTpye:commonConsts.UT_CREATE,
-            moduleName:consts.projectConst.QUANTITY_DETAIL,
-            data:newRecode
-        });
+        returnObjec.newRecord = newRecord;
         return returnObjec;
     }catch (error){
-        returnObjec.err = new Error('输入的表达式有误,请重新输入!');
-        console.log(error)
-        return returnObjec;
-    }
-
-
-}
-
-
-
-function normalUpdate(user_id,datas) {
-    return function(callback) {
-        if(datas.doc.hasOwnProperty('isSummation')){
-            doIsSummationUpdate(datas.query,datas.doc).then(function (sresult) {
-                let returndata ={
-                    moduleName:consts.projectConst.QUANTITY_DETAIL,
-                    data:{
-                        updateTpye:commonConsts.UT_UPDATE,
-                        query:datas.query,
-                        doc:datas.doc
-                    }
-                }
-                let retrunArr = [];
-                retrunArr.push(returndata);
-                if(sresult){
-                    retrunArr.push(sresult);
-                }
-                callback(null,retrunArr);
-            })
-        }else {
-            updateRecored(datas.query,datas.doc,callback);
-        }
-    }
-}
-function updateQuantityRegex(user_id,datas) {
-    return function(callback){
-        doRegexUpdate(datas).then(function (result) {
-            if(result.err){
-                callback(null,{
-                    moduleName:consts.projectConst.QUANTITY_DETAIL,
-                    err:{
-                        message:result.err.message
-                    }
-                });
-            }else {
-                callback(null,result.rList);
-            }
-
-        })
+        console.log(error);
+        throw new Error('输入的表达式有误,请重新输入!');
     }
 }
 
 async function doIsSummationUpdate(query,doc) {
-    try {
-        let returnData={
-            moduleName:'',
-            data:{
-                updateTpye:commonConsts.UT_UPDATE,
-            }
-        }
-        let  refreshQuantity=false;
-        if(query.refreshQuantity==true){
-            refreshQuantity=true;
-        }
-        delete query.refreshQuantity;
-        let updateDoc = await quantity_detail_model.update(query,doc);
-        let detailList = await getDatailList(query,returnData);
-        if(refreshQuantity==true){
-            let quantity = await summateResuts(query,detailList);
-            returnData.data.quantity = quantity;
-            returnData.data.quantityRefresh = true;
-            return returnData;
-        }else {
-            return null;
-        }
-    }catch (error){
-        console.log(error)
+    let returnObjec={query:query,doc:doc};
+    let decimal =await decimal_facade.getProjectDecimal(query.projectID);
+    let  refreshQuantity=false;
+    if(query.refreshQuantity==true){
+        refreshQuantity=true;
     }
+    delete query.refreshQuantity;
+    await quantity_detail_model.update(query,doc);
+    let detailList = await getDatailList(query,returnObjec);
+    if(refreshQuantity==true){
+        let data = {};
+        data.quantity = await summateResults(query,detailList,decimal);
+        data.isFromDetail = 1;
+        returnObjec.node.data = data;
+    }
+    return returnObjec;
 }
 
 async function getDatailList(query,resultObject) {
     let detailList = [];
+    let node={};
     if(query.hasOwnProperty('rationID')){
         detailList = await quantity_detail_model.find({'projectID':query.projectID,'rationID':query.rationID}).sort('seq').exec();
-        resultObject.moduleName = consts.projectConst.RATION;
-        resultObject.data.rationID=query.rationID;
-
+        node.type = consts.projectConst.RATION;
+        node.ID = query.rationID;
     }else {
         detailList = await quantity_detail_model.find({'projectID':query.projectID,'billID':query.billID}).sort('seq').exec();
-        resultObject.moduleName = consts.projectConst.BILLS;
-        resultObject.data.billID=query.billID;
+        node.type = consts.projectConst.BILLS;
+        node.ID=query.billID;
     }
+    resultObject.node = node;
     return detailList;
 }
 
 
-async function doRegexUpdate(datas) {
-    let resultObjec ={
-        err:null,
-        rList:[]
-    }
-    try {
-        let detailList = [];
-        let quantityResult ={
-            moduleName:'',
-            data:{
-                updateTpye:commonConsts.UT_UPDATE,
-                quantityRefresh:true
-            }
-        }
-        if(datas.query.hasOwnProperty('rationID')){
-            detailList = await quantity_detail_model.find({'projectID':datas.query.projectID,'rationID':datas.query.rationID}).sort('seq').exec();
-            quantityResult.moduleName = consts.projectConst.RATION;
-            quantityResult.data.rationID = datas.query.rationID;
-        }else {
-            detailList = await quantity_detail_model.find({'projectID':datas.query.projectID,'billID':datas.query.billID}).sort('seq').exec();
-            quantityResult.data.billID = datas.query.billID;
-            quantityResult.moduleName = consts.projectConst.BILLS;
-        }
-        let regex;
-        let result;
-        if(datas.doc.regex==null){
-            result=0
-            datas.doc.referenceIndexs=[];
-        }else {
-            regex = datas.doc.regex.toUpperCase();
-            let referenceIndexs = datas.doc.referenceIndexs;
-            result =getEvalResult(referenceIndexs,detailList,regex);
-        }
-        detailList[datas.query.index].result =result;
-        detailList[datas.query.index].regex=datas.doc.regex;
-        detailList[datas.query.index].referenceIndexs =datas.doc.referenceIndexs;
-        let updateTasks =[];
-        datas.doc.result=result;
-        updateTasks.push({
-            query:{
-                ID:datas.query.ID,
-                projectID:datas.query.projectID
-            },
-            doc:datas.doc
-        })
-        updateReferenceRecode(datas.query.index+1,detailList,updateTasks);
-        let updateEdit = await quantity_detail_model.bulkWrite(generateUpdateTaks(updateTasks));
-        resultObjec.rList.push(gernerateResultList(updateTasks));
-        if(datas.query.refreshQuantity==true){
-            quantityResult.data.quantity = await summateResuts(datas.query,detailList);
-            resultObjec.rList.push(quantityResult);
-        }
-        return resultObjec;
-    }catch (error){
-        console.log(error);
-        resultObjec.err=error;
-        return resultObjec
-
-    }
-}
-
-function gernerateResultList(updateTasks) {
-    let returndata ={
-        moduleName:consts.projectConst.QUANTITY_DETAIL,
-        data:{
-            updateTpye:commonConsts.UT_UPDATE,
-            refreshList:updateTasks
-        }
-    }
-    return returndata;
-
-}
-
-
-
 function updateReferenceRecode(index,detailList,updateTasks) {
     for(let d of detailList){
         if(_.includes(d.referenceIndexs,index)){
@@ -394,34 +329,52 @@ function updateReferenceRecode(index,detailList,updateTasks) {
 }
 
 
-async function summateResuts (query,detailList) {
+async function summateResults (query,detailList,decimal) {
     let quantity = 0;
+     decimal = decimal?decimal:await decimal_facade.getProjectDecimal(query.projectID);
     for(let d of detailList){
         if(d.isSummation==1){
-            let result = d.result==null||d.result==undefined?0:d.result
+            let result = d.result==null||d.result==undefined?0:d.result;
             quantity+=result;
         }
     }
-    quantity = quantity.toFixed(4);
     if(query.hasOwnProperty('rationID')){
+        let ration = await  ration_model.findOne({'ID':query.rationID,'projectID':query.projectID,deleteInfo: null});
+        quantity = getQuantityByUnit(quantity,ration.unit);
+        quantity = scMathUtil.roundTo(quantity, -decimal.ration.quantity);
         await ration_model.update({'ID':query.rationID,'projectID':query.projectID,deleteInfo: null},{quantity:quantity,isFromDetail:1});
     }else {
+        let bill = await bill_model.findOne({'ID':query.billID,'projectID':query.projectID,deleteInfo: null});
+        decimal = await decimal_facade.getBillsQuantityDecimal(query.projectID,bill.unit);
+        quantity = getQuantityByUnit(quantity,bill.unit);
+        quantity = scMathUtil.roundTo(quantity, -decimal);
         await bill_model.update({'ID':query.billID,'projectID':query.projectID,deleteInfo: null},{quantity:quantity,isFromDetail:1});
     }
     return quantity
 }
 
+function getQuantityByUnit(quantity,unit) {
+    if(unit){
+        let times = parseInt(unit);
+        if(isNaN(times)){
+            times = 1
+        }
+        quantity = quantity/times;
+    }
+    return quantity;
+}
 
 
-function  getEvalResult(referenceIndexs,detailList,regex) {
+ function  getEvalResult(referenceIndexs,detailList,regex,decimal={}) {
     try {
+        decimal = decimal.quantity_detail?decimal.quantity_detail:4;
         for(let i of referenceIndexs){
             regex = replaceReference(i,detailList,regex)
         }
         console.log('replace all C reference -----'+regex);
         regex =replaceSqr(regex);
         console.log('replace all sqar reference -----'+regex);
-        return scMathUtil.roundTo(eval(regex), -4);
+        return scMathUtil.roundTo(eval(regex), -decimal);
     }catch (error){
         console.log(error);
         throw new Error('输入的表达式有误,请重新输入!');
@@ -492,26 +445,6 @@ function converSqrByArr (sqararr,text) {
     return temp;
 };
 
-
-function updateRecored(query,doc,callback) {
-    quantity_detail_model.update(query,doc,(err,result)=>{
-        if(err){
-            callback(err,'');
-        }else {
-            let returndata ={
-                moduleName:consts.projectConst.QUANTITY_DETAIL,
-                data:{
-                    updateTpye:commonConsts.UT_UPDATE,
-                    query:query,
-                    doc:doc
-                }
-            }
-            callback(null,returndata);
-        }
-    })
-}
-
-
 function update_quantity_detail(user_id,datas) {
     if(datas.updateFunction){
         return updateFunctionMap[datas.updateFunction](user_id,datas);
@@ -520,57 +453,43 @@ function update_quantity_detail(user_id,datas) {
     }
 }
 
-function delete_quantity_detail(user_id,datas) {
-    return function (callback) {
-        doQuantityDelete(datas.doc).then(function (result) {
-            if(result.err){
-                callback(result.err,'')
-            }else {
-                callback(null,result.returndata)
-            }
-        });
-    }
-}
 
-async function doQuantityDelete(doc) {;
-    let result={
-        err:null
+async function deleteRecode(doc) {
+    let resultObject = {};
+    let decimal =await decimal_facade.getProjectDecimal(doc.projectID);
+
+    let query = {
+        projectID:doc.projectID,
+        seq : { $gt: doc.seq }
     }
-    try{
-        let query = {
-            projectID:doc.projectID,
-            seq : { $gt: doc.seq }
-        }
-        if(doc.hasOwnProperty('rationID')){
-            query.rationID = doc.rationID;
-        }else {
-            query.billID = doc.billID;
-        }
-        let quantity_detail_List = await getDatailList(doc,{data:{}});
-        let update_task = getUpdateReferenceTask(quantity_detail_List,doc.seq,-1);
-        await quantity_detail_model.update(query,{$inc:{seq:-1}},{multi: true});
-        if(update_task.length>0){
-            await quantity_detail_model.bulkWrite(generateUpdateTaks(update_task));
-        }
-        await quantity_detail_model.deleteOne({ID:doc.ID,projectID:doc.projectID});
-        let returndata ={
-            moduleName:consts.projectConst.QUANTITY_DETAIL,
-            data:{
-                doc:doc,
-                resort:true,
-                update_task:update_task,
-                updateTpye:commonConsts.UT_DELETE
-            }
-        };
-        result.returndata =returndata
-        return result;
-    }catch (error){
-        console.log(error)
-        result.err;
-        return result
+    if(doc.hasOwnProperty('rationID')){
+        query.rationID = doc.rationID;
+    }else {
+        query.billID = doc.billID;
     }
+    let quantity_detail_List = await getDatailList(doc,resultObject);
+    let update_task = getUpdateReferenceTask(quantity_detail_List,doc.seq,-1);
+    await quantity_detail_model.update(query,{$inc:{seq:-1}},{multi: true});
+    if(update_task.length>0){
+        await quantity_detail_model.bulkWrite(generateUpdateTaks(update_task));
+    }
+    await quantity_detail_model.deleteOne({ID:doc.ID,projectID:doc.projectID});
+    _.remove(quantity_detail_List,{ID:doc.ID,projectID:doc.projectID});
+
+    let n_data = {};//更新节点信息
+    n_data.quantity = await summateResults(query,quantity_detail_List,decimal);
+    n_data.isFromDetail = 1;
+    resultObject.node.data = n_data;
+
+    let r_data ={
+            doc:doc,
+            resort:true,
+            update_task:update_task,
+            updateTpye:commonConsts.UT_DELETE
+        };
+    resultObject.data = r_data;
 
-
+    return resultObject;
 }
 
 

+ 10 - 2
modules/main/models/bills.js

@@ -11,7 +11,7 @@ let counter = require("../../../public/counter/counter.js");
 let consts = require('./project_consts');
 let projectConsts = consts.projectConst;
 let commonConsts = consts.commonConst;
-let quantity_detial = require('../../ration_glj/facade/quantity_detail_facade');
+let quantity_detial = require('../facade/quantity_detail_facade');
 
 
 let billsSchema = new Schema({
@@ -30,6 +30,8 @@ let billsSchema = new Schema({
     feeRate:String,
     isFromDetail:{type: Number,default:0},//1 true 0 false
     programID: Number,
+    calcBase: String,
+    calcBaseValue: String,     // 计算基数表达式的值
     // 工程量计算规则
     ruleText: String,
     // 说明
@@ -104,7 +106,13 @@ class billsModel extends baseModel {
             functions.push(saveOne(data));
             quantity_detial.quantityEditChecking(data,'bills',functions);
         }
-        async.parallel(functions, callback);
+        async.parallel(functions, function(err,result){
+            let returnData = {
+                moduleName:'bills',
+                data:{}
+            };
+            callback(err, returnData);
+        });
     };
 
     getItemTemplate (callback) {

+ 6 - 5
modules/main/models/proj_setting_model.js

@@ -3,7 +3,8 @@
  */
 
 let baseModel = require('./base_model');
-import {default as projSettingSchema, collectionName as collectionName, settingConst as settingConst} from "./schemas/proj_setting";
+// import {default as projSettingSchema, collectionName as collectionName, settingConst as settingConst} from "./schemas/proj_setting";
+import {default as projSettingSchema, collectionName as collectionName} from "./schemas/proj_setting";
 
 class projSettingModel extends baseModel {
 
@@ -16,13 +17,13 @@ class projSettingModel extends baseModel {
         this.model.findOne({"projectID": projectID}, '-_id', function (err, result) {
             if (!err) {
                 let data = JSON.parse(JSON.stringify(result));
-                if (!data.billsCalcMode) {
+/*                if (!data.billsCalcMode) {
                     data.billsCalcMode = settingConst.billsCalcMode.rationContent;
                 }
-                if (!data.zanguCalcMode) {
-                    data.zanguCalcMode = settingConst.zanguCalcMode.common;
+                if (!data.zanguCalcType) {
+                    data.zanguCalcType = settingConst.zanguCalcType.common;
                 }
-                data.settingConst = settingConst;
+                data.settingConst = settingConst;*/
                 callback(0, collectionName, data);
             } else {
                 callback(1, '查询数据失败。', null);

+ 6 - 3
modules/main/models/project.js

@@ -7,13 +7,15 @@ var GLJData = require('./glj');
 var ration_glj_data = require('../../ration_glj/facade/ration_glj_facade');
 var ration_coe_data = require('../../ration_glj/facade/ration_coe_facade');
 var ration_ass_data = require('../../ration_glj/facade/ration_ass_facade');
-var quantity_detail_data = require('../../ration_glj/facade/quantity_detail_facade');
+var quantity_detail_data = require('../facade/quantity_detail_facade');
 var fee_rate_data = require('../../fee_rates/facade/fee_rates_facade');
 let projCounter = require('./proj_counter_model');
 let projSetting = require('./proj_setting_model');
-let volumePriceData = require('../../volume_price/models/volume_price_model');
+// let volumePriceData = require('../../volume_price/models/volume_price_model');
 var labour_coe_facade = require('../facade/labour_coe_facade');
 var calc_program_facade = require('../facade/calc_program_facade');
+import GLJController from "../../glj/controllers/glj_controller";
+
 
 const ProjectModel = require('../../pm/models/project_model').project;
 import GLJListModel from "../../glj/models/glj_list_model";
@@ -33,10 +35,11 @@ moduleMap[projectConsts.RATION_ASS] = ration_ass_data;
 moduleMap[projectConsts.QUANTITY_DETAIL] = quantity_detail_data;
 moduleMap[projCounter.collectionName] = projCounter;
 moduleMap[projSetting.collectionName] = projSetting;
-moduleMap[volumePriceData.collectionName] = volumePriceData;
+// moduleMap[volumePriceData.collectionName] = volumePriceData;
 moduleMap[projectConsts.FEERATE] = fee_rate_data;
 moduleMap[projectConsts.LABOUR_COE] = labour_coe_facade;
 moduleMap[projectConsts.CALC_PROGRAM] = calc_program_facade;
+moduleMap[projectConsts.PROJECTGLJ] = new GLJController();
 
 var Project = function (){};
 

+ 2 - 2
modules/main/models/project_consts.js

@@ -13,7 +13,7 @@ let projectConst = {
     GLJLIST: 'GLJList',
     UNITPRICEFILE: 'unitPriceFile',
     PROPERTIES: 'properties',
-    VOLUMEPRICE: 'volume_price',
+    // VOLUMEPRICE: 'volume_price',
     FEERATE:'feeRate',
     LABOUR_COE:'labour_coe',
     CALC_PROGRAM:'calc_program'
@@ -31,7 +31,7 @@ let projectConstList = [
     'GLJList',
     'unitPriceFile',
     'properties',
-    'volume_price',
+    // 'volume_price',
     'feeRate',
     'labour_coe',
     'calc_program'

+ 20 - 17
modules/main/models/ration.js

@@ -11,7 +11,7 @@ let counter = require("../../../public/counter/counter.js");
 let consts = require('./project_consts');
 let projectConsts = consts.projectConst;
 let commonConsts = consts.commonConst;
-let quantity_detial = require('../../ration_glj/facade/quantity_detail_facade');
+let quantity_detial = require('../facade/quantity_detail_facade');
 
 var rationAssItemSchema = mongoose.Schema({
     name: String,
@@ -26,35 +26,38 @@ var rationAssItemSchema = mongoose.Schema({
     maxValue: String
 }, { _id: false });
 
+// 定额、量价、工料机定额 合并存储
 let rationSchema = new Schema({
+    // 公用属性部分
     ID: Number,
     projectID: Number,
     billsItemID: Number,
     serialNo: Number,
-    libID: Number,
     code: String,
     name: String,
-    maskName: String,
-    caption: String,
     unit: String,
-    quantity: String, // Decimal
-    isFromDetail:{type: Number,default:0},  //1 true 2 false
+    quantity: String,
     programID: Number,
+    marketUnitFee: String,
+    marketTotalFee: String,
+    fees: [subSchema.feesSchema],
+    deleteInfo: deleteSchema,
+    type: Number,                               // 1 定额、2 量价、3 工料机定额
+    subType: Number,                            // 子类型:1人工、201材料、301机械、4主材、5设备
+
+    // 定额特有属性:
+    libID: Number,
+    maskName: String,
+    caption: String,
+    isFromDetail:{type: Number,default:0},       // 1 true 2 false
     adjustState: String,
     content: String,
     rationProjName: String,
-    // 说明
-    comments: String,
-    // 费用字段
-    fees: [subSchema.feesSchema],
-    // 标记字段
-    flags: [subSchema.flagsSchema],
-    deleteInfo: deleteSchema,
+    comments: String,                           // 说明
+    flags: [subSchema.flagsSchema],             // 标记字段
     rationAssList: [rationAssItemSchema],
-    // 工作内容
-    content: String,
-    // 计算规则
-    ruleText: String
+    content: String,                            // 工作内容
+    ruleText: String                            // 计算规则
 });
 
 let ration = db.model("ration", rationSchema, "ration");

+ 2 - 2
modules/main/models/schemas/proj_counter.js

@@ -8,8 +8,8 @@ let collectionName = 'projCounter';
 let projSettingSchema = {
     projectID: Number,
     bills: Number,
-    ration: Number,
-    volume_price: Number
+    ration: Number/*,
+    volume_price: Number*/
 };
 let model = mongoose.model(collectionName, new Schema(projSettingSchema, {versionKey: false, collection: collectionName}));
 export {model as default, collectionName as collectionName};

+ 12 - 12
modules/main/models/schemas/proj_setting.js

@@ -5,33 +5,33 @@
 let mongoose = require("mongoose");
 let Schema = mongoose.Schema;
 let collectionName = 'proj_setting';
-let settingConst = {
+/*let settingConst = {
     billsCalcMode: {
-        rationContent: 0, rationPrice: 1, rationPriceConverse: 2, billsPrice: 3
+        rationContent: 0, rationPriceConverse: 1, rationPrice: 2, billsPrice: 3
     },
-    zanguCalcMode: {
+    zanguCalcType: {
         common: 0, gatherMaterial: 1
     }
-}
-let billsCalcModeConst = {
+}*/
+/*let billsCalcModeConst = {
     
-};
+};*/
 let projSettingSchema = {
     projectID: Number,
     // 列设置
     main_tree_col: {
         type: Schema.Types.Mixed,
         default: {}
-    },
+    }/*,
     billsCalcMode: {
         type: Number,
         default: settingConst.billsCalcMode.rationContent
-        // rationContent = 0, rationPrice = 1, rationPriceConverse = 2, billsPrice = 3
     },
-    zanguCalcMode: {
+    zanguCalcType: {
         type: Number,
-        default: settingConst.zanguCalcMode.common
-    }
+        default: settingConst.zanguCalcType.common
+    }*/
 };
 let model = mongoose.model(collectionName, new Schema(projSettingSchema, {versionKey: false, collection: collectionName}));
-export {model as default, collectionName as collectionName, settingConst as settingConst};
+// export {model as default, collectionName as collectionName, settingConst as settingConst};
+export {model as default, collectionName as collectionName};

+ 8 - 2
modules/main/routes/main_route.js

@@ -4,16 +4,22 @@
 
 
 import BaseController from "../../common/base/base_controller";
+const projectModel = require("../../pm/models/project_model");
 module.exports =function (app) {
     const baseController = new BaseController();
     app.get('/main', baseController.init, function(req, res) {
         let pm = require('../../pm/controllers/pm_controller');
-        pm.checkProjectRight(req.session.sessionUser.ssoId, req.query.project, function (hasRight) {
+
+        pm.checkProjectRight(req.session.sessionUser.ssoId, req.query.project, async function (hasRight) {
             if (hasRight) {
+                // 获取项目信息
+                const projectId = req.query.project;
+                const projectData = await projectModel.project.getProject(projectId);
                 res.render('building_saas/main/html/main.html',
                     {
                         userAccount: req.session.userAccount,
-                        userID: req.session.sessionUser.ssoId
+                        userID: req.session.sessionUser.ssoId,
+                        projectData: projectData,
                     });
             } else {
                 res.redirect('/pm');

+ 20 - 0
modules/main/routes/quantity_detail_route.js

@@ -0,0 +1,20 @@
+/**
+ * Created by zhang on 2017/11/24.
+ */
+
+let express = require('express');
+let qdController = require('../controllers/quantity_detail_controller');
+
+module.exports = function (app) {
+
+    var qdRouter = express.Router();
+
+
+    qdRouter.post('/save', qdController.save);
+    qdRouter.post('/update',qdController.update);
+    qdRouter.post('/updateRegex',qdController.updateRegex);
+    qdRouter.post('/insertRecode',qdController.insertRecode);
+    qdRouter.post('/deleteRecode',qdController.deleteRecode);
+    qdRouter.post('/swapRow',qdController.swapRow);
+    app.use('/quantity_detail',qdRouter);
+}

+ 2 - 2
modules/pm/controllers/copy_proj_controller.js

@@ -6,7 +6,7 @@ let billsData = require('../../main/models/bills');
 let rationData = require('../../main/models/ration');
 let projCounter = require('../../main/models/proj_counter_model');
 let projSetting = require('../../main/models/proj_setting_model');
-let volumePriceData = require('../../volume_price/models/volume_price_model');
+// let volumePriceData = require('../../volume_price/models/volume_price_model');
 let async = require('async');
 
 module.exports = {
@@ -32,7 +32,7 @@ module.exports = {
         fun.push(copyData(rationData));
         fun.push(copyData(projCounter));
         fun.push(copyData(projSetting));
-        fun.push(copyData(volumePriceData));
+        // fun.push(copyData(volumePriceData));
         async.parallel(fun, (err) => callback(err));
     }
 };

+ 15 - 11
modules/pm/models/project_model.js

@@ -5,7 +5,7 @@ import mongoose from 'mongoose';
 import async_c from 'async';
 import UnitPriceFileModel from "../../glj/models/unit_price_file_model";
 import UnitPriceFiles from '../../glj/models/schemas/unit_price_file';
-import billsQuantityDecimal from './project_property_bills_quantity_decimal';
+import {defaultDecimal, billsQuantityDecimal, basicInformation, projectFeature,displaySetting} from './project_property_template';
 let FeeRateFiles = mongoose.model('fee_rate_file');
 let counter = require("../../../public/counter/counter.js");
 
@@ -27,13 +27,6 @@ let fileType = {
     unitPriceFile: 'UnitPriceFile',
     feeRateFile: 'FeeRateFile'
 };
-//默认的小数位数,用于定义用户可编辑的字段(入库),用户不可编辑的字段在前端defaultDecima._def中定义即可
-const defaultDecimal = {
-    bills: {unitPrice: 2, totalPrice: 2},
-    ration: {quantity: 3, unitPrice: 2, totalPrice: 2},
-    glj: {quantity: 3, unitPrice: 2},
-    feeRate: 2,
-};
 
 let ProjectsDAO = function(){};
 
@@ -77,9 +70,8 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, datas, callbac
             if (data.updateData.name !== undefined) {
                 data.updateData.name = data.updateData.name.trim();
             }
-
             if (data.updateType === 'update') {
-                Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll)
+                Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
             } else if (data.updateType === 'new') {
                 data.updateData['userID'] = userId;
                 data.updateData['createDateTime'] = new Date();
@@ -103,8 +95,20 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, datas, callbac
                     data.updateData.property.unitPriceFile.id = addResult.id;
                 }
                 if(data.updateData.projType === projectType.tender){
+                    //小数位数
                     data.updateData.property.decimal = defaultDecimal;
+                    //清单工程量精度
                     data.updateData.property.billsQuantityDecimal = billsQuantityDecimal;
+                    //基本信息
+                    basicInformation[0]['items'][1]['value'] = data.updateData.property.engineeringName || '';
+                    data.updateData.property.basicInformation = basicInformation;
+                    //工程特征
+                    data.updateData.property.projectFeature = projectFeature;
+                    //呈现选项
+		    data.updateData.property.displaySetting = displaySetting;
+		    
+                    data.updateData.property.billsCalcMode = 0;
+		    data.updateData.property.zanguCalcMode = 0;
                 }
                 newProject = new Projects(data.updateData);
                 // 查找同级是否存在同名数据
@@ -126,7 +130,7 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, datas, callbac
                 }
                 newProject.save(async function (err, result) {
                     if (!err && result._doc.projType === projectType.tender) {
-                        newProjController.copyTemplateData(data.updateData.property, newProject.ID, updateAll);
+                        newProjController.copyTemplateData(result._doc.property, newProject.ID, updateAll);
                     } else {
                         updateAll(err);
                     }

+ 0 - 51
modules/pm/models/project_property_bills_quantity_decimal.js

@@ -1,51 +0,0 @@
-/**
- * Created by Zhong on 2017/11/16.
- */
-/*
-* 单位工程清单工程量精度模板
-* */
-const billsQuantityDecimal = [
-    {unit: '其他未列单位', decimal: 2},
-    {unit: 't', decimal: 3},
-    {unit: '吨', decimal: 3},
-    {unit: 'km', decimal: 3},
-    {unit: '千米', decimal: 3},
-    {unit: 'm', decimal: 2},
-    {unit: '米', decimal: 2},
-    {unit: 'm2', decimal: 2},
-    {unit: '平方米', decimal: 2},
-    {unit: 'm3', decimal: 2},
-    {unit: '立方米', decimal: 2},
-    {unit: 'kg', decimal: 2},
-    {unit: '千克', decimal: 2},
-    {unit: '公斤', decimal: 2},
-    {unit: '元', decimal: 2},
-    {unit: '工日', decimal: 0},
-    {unit: '台班', decimal: 0},
-    {unit: '个', decimal: 0},
-    {unit: '件', decimal: 0},
-    {unit: '根', decimal: 0},
-    {unit: '组', decimal: 0},
-    {unit: '宗', decimal: 0},
-    {unit: '付', decimal: 0},
-    {unit: '处', decimal: 0},
-    {unit: '系统', decimal: 0},
-    {unit: '部', decimal: 0},
-    {unit: '台', decimal: 0},
-    {unit: '辆', decimal: 0},
-    {unit: '套', decimal: 0},
-    {unit: '株', decimal: 0},
-    {unit: '从', decimal: 0},
-    {unit: '缸', decimal: 0},
-    {unit: '支', decimal: 0},
-    {unit: '只', decimal: 0},
-    {unit: '块', decimal: 0},
-    {unit: '座', decimal: 0},
-    {unit: '对', decimal: 0},
-    {unit: '份', decimal: 0},
-    {unit: '樘', decimal: 0},
-    {unit: '攒', decimal: 0},
-    {unit: '榀', decimal: 0}
-];
-
-export default billsQuantityDecimal;

+ 126 - 0
modules/pm/models/project_property_template.js

@@ -0,0 +1,126 @@
+/**
+ * Created by Zhong on 2017/11/16.
+ */
+
+//默认的小数位数,用于定义用户可编辑的字段(入库),用户不可编辑的字段在前端defaultDecima._def中定义即可
+const defaultDecimal = {
+    bills: {unitPrice: 2, totalPrice: 2},
+    ration: {quantity: 3, unitPrice: 2, totalPrice: 2},
+    glj: {quantity: 3, unitPrice: 2},
+    feeRate: 2,
+    quantity_detail: 4,
+    process: 6
+};
+const displaySetting = {
+    autoHeight:true,
+    disPlayMainMateria:true
+}
+
+/*
+* 单位工程清单工程量精度模板
+* */
+const billsQuantityDecimal = [
+    {unit: '其他未列单位', decimal: 2},
+    {unit: 't', decimal: 3},
+    {unit: '吨', decimal: 3},
+    {unit: 'km', decimal: 3},
+    {unit: '千米', decimal: 3},
+    {unit: 'm', decimal: 2},
+    {unit: '米', decimal: 2},
+    {unit: 'm2', decimal: 2},
+    {unit: '平方米', decimal: 2},
+    {unit: 'm3', decimal: 2},
+    {unit: '立方米', decimal: 2},
+    {unit: 'kg', decimal: 2},
+    {unit: '千克', decimal: 2},
+    {unit: '公斤', decimal: 2},
+    {unit: '元', decimal: 2},
+    {unit: '工日', decimal: 0},
+    {unit: '台班', decimal: 0},
+    {unit: '个', decimal: 0},
+    {unit: '件', decimal: 0},
+    {unit: '根', decimal: 0},
+    {unit: '组', decimal: 0},
+    {unit: '宗', decimal: 0},
+    {unit: '付', decimal: 0},
+    {unit: '处', decimal: 0},
+    {unit: '系统', decimal: 0},
+    {unit: '部', decimal: 0},
+    {unit: '台', decimal: 0},
+    {unit: '辆', decimal: 0},
+    {unit: '套', decimal: 0},
+    {unit: '株', decimal: 0},
+    {unit: '从', decimal: 0},
+    {unit: '缸', decimal: 0},
+    {unit: '支', decimal: 0},
+    {unit: '只', decimal: 0},
+    {unit: '块', decimal: 0},
+    {unit: '座', decimal: 0},
+    {unit: '对', decimal: 0},
+    {unit: '份', decimal: 0},
+    {unit: '樘', decimal: 0},
+    {unit: '攒', decimal: 0},
+    {unit: '榀', decimal: 0}
+];
+
+//项目属性基本信息
+const basicInformation = [
+    {dispName: '基本信息', key: 'basicInfo', items: [
+        {dispName: '合同号', key: 'contractNum', value: ''},
+        {dispName: '工程专业', key: 'engineering', value: ''},//只读,用户新建单位工程时选择的值
+        {dispName: '建设地点', key: 'constructionPlace', value: ''},
+        {dispName: '质量标准', key: 'qualityStandard', value: ''},
+        {dispName: '工程类别', key: 'projectCategory', value: ''},
+        {dispName: '建设日期', key: 'constructionDate', value: ''},
+        {dispName: '工程规模', key: 'projectScale', value: ''},
+        {dispName: '建设单位', key: 'constructionUnit', value: ''},
+        {dispName: '设计单位', key: 'designUnit', value: ''},
+        {dispName: '施工单位', key: 'buildingUnit', value: ''},
+        {dispName: '监理单位', key: 'supervisingUnit', value: ''},
+        {dispName: '审核单位', key: 'auditUnit', value: ''},
+        {dispName: '建设单位审核人', key: 'constructionUnitAudit', value: ''},
+        {dispName: '设计单位负责人', key: 'designUnitPrincipal', value: ''},
+        {dispName: '施工单位编制人', key: 'buildingUnitAuthor', value: ''},
+        {dispName: '监理单位审核人', key: 'supervisingUnitAuditor', value: ''},
+        {dispName: '审核单位审核人', key: 'auditUnitAuditor', value: ''},
+        {dispName: '编制时间', key: 'establishDate', value: ''}
+    ]},
+    {dispName: '招标信息', key: 'biddingInfo', items: [
+        {dispName: '招标人', key: 'bidInviter', value: ''},
+        {dispName: '法定代表人或其他授权人', key: 'representative', value: ''},
+        {dispName: '造价工程师', key: 'costEngineer', value: ''}
+    ]},
+    {dispName: '投标信息', key: 'bidInfo', items: [
+        {dispName: '投标人', key: 'bidder', value: ''},
+        {dispName: '法定代表人或其授权人', key: 'representative', value: ''},
+        {dispName: '造价工程师', key: 'costEngineer', value: ''}
+    ]},
+    {dispName: '工程造价咨询信息', key: 'engineeringCostConsultationInfo', items: [
+        {dispName: '工程造价咨询人', key: 'consultant', value: ''},
+        {dispName: '法定代表人或其授权人', key: 'representative', value: ''},
+        {dispName: '造价工程师', key: 'costEngineer', value: ''}
+    ]}
+];
+
+const projectFeature = [
+    {dispName: '工程类型', key: 'projType', value: ''},
+    {dispName: '结构类型', key: 'structureType', value: ''},
+    {dispName: '基础类型', key: 'baseType', value: ''},
+    {dispName: '建筑特征', key: 'buildingFeature', value: ''},
+    {dispName: '建筑面积(m2)', key: 'buildingArea', value: ''},
+    {dispName: '总层数', key: 'totalFloors', value: ''},
+    {dispName: '地下室层数(+/-0.00以下)', key: 'basementFloors', value: ''},
+    {dispName: '建筑层数(+/-0.00以下)', key: 'buildingFloors', value: ''},
+    {dispName: '建筑物总高度(m)', key: 'buildingHeight', value: ''},
+    {dispName: '地下室总高度(m)', key: 'basementHeight', value: ''},
+    {dispName: '首层高度(m)', key: 'firstFloorHeight', value: ''},
+    {dispName: '裙楼高度(m)', key: 'podiumBuildingHeight', value: ''},
+    {dispName: '标准层高度(m)', key: 'standardFloorHeight', value: ''},
+    {dispName: '基础材料及装饰', key: 'baseMaterial', value: ''},
+    {dispName: '楼地面材料及装饰', key: 'flooringMaterial', value: ''},
+    {dispName: '外墙材料及装饰', key: 'exteriorWallMaterial', value: ''},
+    {dispName: '屋面材料及装饰', key: 'roofingMaterial', value: ''},
+    {dispName: '门窗材料及装饰', key: 'doorsWindowsMaterial', value: ''}
+];
+
+export {defaultDecimal, billsQuantityDecimal, basicInformation, projectFeature,displaySetting};

+ 20 - 3
modules/ration_glj/controllers/ration_glj_controller.js

@@ -13,7 +13,8 @@ module.exports={
     addGLJ:addGLJ,
     replaceGLJ:replaceGLJ,
     mReplaceGLJ:mReplaceGLJ,
-    updateRationGLJByEdit:updateRationGLJByEdit
+    updateRationGLJByEdit:updateRationGLJByEdit,
+    getGLJClass:getGLJClass
 }
 function createRationGLJ() {
     let gls = mongoose.model('ration_glj');
@@ -102,7 +103,6 @@ async function mReplaceGLJ(req, res){
     try {
         let data = req.body.data;
         data = JSON.parse(data);
-        console.log(data);
         let mresult= await ration_glj_facade.mReplaceGLJ(data);
         result.data=mresult;
     }catch (err){
@@ -128,5 +128,22 @@ async  function updateRationGLJByEdit(req, res) {
         result.message = err.message;
     }
     res.json(result);
-    
+}
+
+async  function getGLJClass(req, res) {
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let info = await ration_glj_facade.getLibInfo(req);
+        let tresult= await ration_glj_facade.getGLJClass(info,data);
+        result.data=tresult;
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+    res.json(result);
 }

+ 16 - 13
modules/ration_glj/facade/glj_calculate_facade.js

@@ -11,6 +11,7 @@ let ration_coe = mongoose.model('ration_coe');
 let std_ration_lib_ration_items = mongoose.model('std_ration_lib_ration_items');
 let glj_type_util = require('../../../public/cache/std_glj_type_util');
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
+let decimal_facade = require('../../main/facade/decimal_facade');
 
 module.exports={
     calculateQuantity:calculateQuantity,
@@ -28,7 +29,7 @@ let stateSeq ={
 }
 
 
-async function calculateQuantity(query,isMarkPriceAjust){
+async function calculateQuantity(query,noNeedCal){
     try {
          let  result ={
              glj_result:[],
@@ -51,11 +52,11 @@ async function calculateQuantity(query,isMarkPriceAjust){
                  }
          }
          for(let i =0;i<gljList.length;i++ ){
-             let r = await calculateQuantityPerGLJ(gljList[i],i,coeList,assList,adjustState,isMarkPriceAjust);
+             let r = await calculateQuantityPerGLJ(gljList[i],i,coeList,assList,adjustState,noNeedCal);
              result.glj_result.push(r);
          }
 
-        if(isMarkPriceAjust==null){
+        if(noNeedCal==null){
             await ration_glj.bulkWrite(generateUpdateTasks(result.glj_result));
         }
          adjustState= _.sortByOrder(adjustState, ['index'], ['asc']);
@@ -85,8 +86,10 @@ function generateUpdateTasks(result) {
 }
 
 
-async function calculateQuantityPerGLJ(glj,index,coeList,assList,adjustState,isMarkPriceAjust) {
-    let quantity =  glj.quantity;
+async function calculateQuantityPerGLJ(glj,index,coeList,assList,adjustState,noNeedCal) {
+    let decimalObject =await decimal_facade.getProjectDecimal(glj.projectID);
+    let decimal = (decimalObject&&decimalObject.glj&&decimalObject.glj.quantity)?decimalObject.glj.quantity:3;
+    let quantity =  scMathUtil.roundTo(parseFloat(glj.quantity),-decimal);
     let result={
         query:{
             ID:glj.ID,
@@ -97,9 +100,9 @@ async function calculateQuantityPerGLJ(glj,index,coeList,assList,adjustState,isM
         }
     };
     try {
-        if(isMarkPriceAjust==null){
+        if(noNeedCal==null){
             if(!glj._doc.hasOwnProperty('customQuantity')||glj.customQuantity==null){
-                quantity =glj.rationItemQuantity;
+                quantity =scMathUtil.roundTo(parseFloat(glj.rationItemQuantity),-decimal);
                 quantity =calculateAss(quantity,assList,glj);
                 quantity = calculateQuantityByCoes(quantity,coeList,glj);
             }else {
@@ -110,9 +113,9 @@ async function calculateQuantityPerGLJ(glj,index,coeList,assList,adjustState,isM
             if(customerCoe.isAdjust==1){
                 quantity = calculateQuantityByCustomerCoes(quantity,customerCoe,glj);
             }
-            result.doc.quantity =_.round(quantity,3);
+            result.doc.quantity =scMathUtil.roundToString(quantity,decimal);
         }
-        generateAdjustState(glj,coeList,adjustState,index);
+        generateAdjustState(glj,coeList,adjustState,index,result.doc.quantity);
         return result;
     }catch (err){
         throw err;
@@ -134,12 +137,12 @@ function calculateAss(quantity,assList,glj) {
     return scMathUtil.roundTo(quantity,-6);
 }
 
-function generateAdjustState(glj,coeList,adjustState,index) {
+function generateAdjustState(glj,coeList,adjustState,index,quantity) {
    //替换工料机 and 添加工料机
     if(glj._doc.createType=='replace'){
         adjustState.push({index:stateSeq.replase,content:glj.rcode+'换'+glj.code});
     }else if(glj._doc.createType=='add'){
-        adjustState.push({index:stateSeq.add,content:'添'+glj.code+'量'+glj.quantity});
+        adjustState.push({index:stateSeq.add,content:'添'+glj.code+'量'+parseFloat(quantity)});
     }
     // to do
 
@@ -158,8 +161,8 @@ function generateAdjustState(glj,coeList,adjustState,index) {
 
     //自定义消耗量
     if(glj._doc.createType!='add'&&glj._doc.hasOwnProperty('customQuantity')){
-        if(glj.customQuantity!==null){
-            adjustState.push({index:stateSeq.cusQuantity,content:glj.code+'量'+glj.customQuantity});
+        if(glj.customQuantity!==null&&glj.customQuantity!=""){
+            adjustState.push({index:stateSeq.cusQuantity,content:glj.code+'量'+parseFloat(glj.customQuantity)});
         }
     }
     //市场单价调整

+ 58 - 15
modules/ration_glj/facade/ration_glj_facade.js

@@ -16,11 +16,12 @@ let ration_coe_facade = require('./ration_coe_facade');
 let ration_coe = mongoose.model('ration_coe');
 let std_ration_lib_ration_items = mongoose.model('std_ration_lib_ration_items');
 let glj_calculate_facade = require('./glj_calculate_facade');
-let quantity_detail_facade = require('./quantity_detail_facade');
+let quantity_detail_facade = require('../../main/facade/quantity_detail_facade');
 let logger = require("../../../logs/log_helper").logger;
 import stdgljutil  from "../../../public/cache/std_glj_type_util";
 import EngineeringLibModel from "../../users/models/engineering_lib_model";
 import GljDao from "../../complementary_glj_lib/models/gljModel";
+import {complementaryGljModel, stdGljModel, gljClassModel} from "../../complementary_glj_lib/models/schemas";
 
 
 module.exports={
@@ -33,7 +34,8 @@ module.exports={
     addGLJ:addGLJ,
     replaceGLJ:replaceGLJ,
     mReplaceGLJ:mReplaceGLJ,
-    updateRationGLJByEdit:updateRationGLJByEdit
+    updateRationGLJByEdit:updateRationGLJByEdit,
+    getGLJClass:getGLJClass
 }
 
 let operationMap={
@@ -90,10 +92,10 @@ function combineQuantity(results,rations) {
         }
         resultList.push(tmp);
       /*  if(resultMap.hasOwnProperty(data.projectGLJID)){
-            resultMap[data.projectGLJID] += data.quantity;
-        }else {
-            resultMap[data.projectGLJID] = data.quantity;
-        }*/
+         resultMap[data.projectGLJID] += data.quantity;
+         }else {
+         resultMap[data.projectGLJID] = data.quantity;
+         }*/
     });
 
  /*   var resultList =[];
@@ -123,6 +125,7 @@ function get_lib_glj_info(ration_glj) {
                 ration_glj.shortName = glj.shortName;
                 ration_glj.type = glj.gljType;
                 ration_glj.repositoryId = glj.repositoryId;
+                ration_glj.adjCoe = glj.adjCoe;
                 getInfoFromProjectGLJ(ration_glj).then(function (info) {
                     if(info){
                         let tem={};
@@ -201,7 +204,9 @@ async function getInfoFromProjectGLJ(ration_glj) {
              marketPrice:pg.unit_price.market_price,
              adjustPrice:pg.adjust_price,
              isEstimate:pg.is_evaluate,
-             isMixRatio:true
+             isMixRatio:true,
+             isAdd:pg.unit_price.is_add,
+             GLJID:pg.glj_id
          }
          temRationGLJs.push(tem);
      }
@@ -551,6 +556,7 @@ function getGLJSearchInfo(ration_glj) {
         base_price: ration_glj.basePrice,
         market_price: ration_glj.basePrice,
         repositoryId:ration_glj.repositoryId,
+        adjCoe:ration_glj.adjCoe,
         from:ration_glj.from?ration_glj.from:'std'//std:标准工料机库, cpt:补充工料机库
     };
      if(data.from=='cpt'){//从补充工料机来的数据即为新增数据
@@ -568,6 +574,7 @@ async function addGLJ(rgList) {
        g.marketPrice=result.unit_price.market_price;
        g.adjustPrice=result.unit_price.base_price;
        g.basePrice=result.unit_price.base_price;
+       g.isAdd=result.unit_price.is_add,
        g.projectGLJID=result.id;
        g.isEstimate=result.is_evaluate;
        g.ID=uuidV1();
@@ -591,13 +598,18 @@ async function replaceGLJ(data) {
     let rdata={};
     let projectGljModel = new GLJListModel();
     let result = await projectGljModel.addList(getGLJSearchInfo(data));
+    data.projectGLJID=result.id;
+    let updateResult=await ration_glj.findOneAndUpdate({ID:data.ID,projectID:data.projectID},data);//更新定额工料机
+    //组装回传数据
     data.marketPrice=result.unit_price.market_price;
     data.adjustPrice=result.unit_price.base_price;
     data.basePrice=result.unit_price.base_price;
-    data.projectGLJID=result.id;
+    data.isAdd=result.unit_price.is_add;
     data.isEstimate=result.is_evaluate;
-    let updateResult=await ration_glj.findOneAndUpdate({ID:data.ID,projectID:data.projectID},data);
-    let stateResult =  await glj_calculate_facade.calculateQuantity({projectID:data.projectID,rationID:data.rationID});
+    if(result.hasOwnProperty('subList')&&result.subList.length>0){
+        data.subList=getMixRatioShowDatas(result.subList);
+    }
+    let stateResult =  await glj_calculate_facade.calculateQuantity({projectID:data.projectID,rationID:data.rationID},true);
     rdata.data=data;
     rdata.adjustState=stateResult.adjustState;
     return rdata;
@@ -606,13 +618,18 @@ async function mReplaceGLJ(data) {
     let mresult={};
     let projectGljModel = new GLJListModel();
     let result = await projectGljModel.addList(getGLJSearchInfo(data.doc));
+    data.doc.projectGLJID=result.id;
+    let rationList=await ration_glj.distinct('rationID',data.query);
+    let updateResult=await ration_glj.update(data.query,data.doc,{multi: true});
+
     data.doc.marketPrice=result.unit_price.market_price;
     data.doc.adjustPrice=result.unit_price.base_price;
     data.doc.basePrice=result.unit_price.base_price;
-    data.doc.projectGLJID=result.id;
+    data.doc.isAdd=result.unit_price.is_add;
     data.doc.isEstimate=result.is_evaluate;
-    let rationList=await ration_glj.distinct('rationID',data.query);
-    let updateResult=await ration_glj.update(data.query,data.doc,{multi: true});
+    if(result.hasOwnProperty('subList')&&result.subList.length>0){
+        data.doc.subList=getMixRatioShowDatas(result.subList);
+    }
     let stateList= await changAdjustState(data,rationList);
     mresult.data=data;
     mresult.stateList=stateList;
@@ -665,12 +682,38 @@ async function doRationGLJUpdate(data){
     return resutl;
 }
 
-
+async function getGLJClass(info,data) {
+    let result={
+        exist:false
+    }
+    //检查补充工料机中是否已经存在一样的记录了
+    let condition = {
+        userId:info.userID,
+        compilationId:info.compilationId,
+        code:data.code,
+        name:data.name,
+        unit:data.unit,
+        gljType:data.type,
+        basePrice:data.basePrice
+    }
+    if(data.specs!=null&&data.specs!=undefined&&data.specs!=''){
+        condition['specs']=data.specs;
+    }
+    let glj = await complementaryGljModel.find(condition);
+    if(glj.length>0){ //如果已存在就直接返回,不用再新增了
+        result.exist = true;
+        return result
+    }
+    //查找工料机类型树
+   let items = await gljClassModel.find({"repositoryId": info.gljLibId, "$or": [{"isDeleted": null}, {"isDeleted": false} ]});
+    result.items = items;
+   return result;
+}
 
 async function changAdjustState(data,rationList) {
     let stateList=[];
     for(let r of rationList){
-      let stateResult = await glj_calculate_facade.calculateQuantity({projectID:data.query.projectID,rationID:r});
+      let stateResult = await glj_calculate_facade.calculateQuantity({projectID:data.query.projectID,rationID:r},true);
       stateList.push({rationID:r,adjustState:stateResult.adjustState});
     }
     return stateList;

+ 2 - 0
modules/ration_glj/models/ration_glj.js

@@ -24,6 +24,8 @@ var ration_glj = new Schema({
     shortName:String,
     billsItemID: Number,
     type:Number,
+    // 调整系数ID
+    adjCoe: Number,
     quantity:String,
     customQuantity:String,
     rationItemQuantity:String,

+ 1 - 0
modules/ration_glj/routes/ration_glj_route.js

@@ -13,6 +13,7 @@ module.exports = function (app) {
     rgRouter.post('/replaceGLJ',rgController.replaceGLJ);
     rgRouter.post('/mReplaceGLJ',rgController.mReplaceGLJ);
     rgRouter.post('/updateRationGLJByEdit',rgController.updateRationGLJByEdit);
+    rgRouter.post('/getGLJClass', rgController.getGLJClass);
     app.use('/rationGlj',rgRouter);
 }
 

+ 53 - 1
modules/reports/controllers/rpt_controller.js

@@ -92,7 +92,7 @@ function getAllPagesCommon(user_id, prj_id, rpt_id, pageSize, option, cb) {
                     let maxPages = printCom.totalPages;
                     let pageRst = printCom.outputAsSimpleJSONPageArray(rptTpl, tplData, 1, maxPages, defProperties);
                     if (pageRst) {
-                        //fsUtil.wirteObjToFile(pageRst, "D:/GitHome/ConstructionCost/tmp/testBuiltPageResult.js");
+                        //fsUtil.writeObjToFile(pageRst, "D:/GitHome/ConstructionCost/tmp/testBuiltPageResult.js");
                         cb(null, pageRst);
                     } else {
                         cb('Have errors while on going...', null);
@@ -115,6 +115,7 @@ module.exports = {
             prj_id = params.prj_id,
             user_id = params.user_id,
             pageSize = params.pageSize;
+        // req.session.sessionUser.ssoId
         getAllPagesCommon(user_id, prj_id, rpt_id, pageSize, null, function (err, pageRst) {
             callback(req, res, err, pageRst);
         });
@@ -128,6 +129,33 @@ module.exports = {
             callback(req, res, err, pageRst);
         })
     },
+    getExcel: function(req, res) {
+        let prj_id = req.params.prj_id,
+            rpt_id = req.params.rpt_id,
+            pageSize = req.params.size,
+            rptName = req.params.rptName,
+            isOneSheet = req.params.isOneSheet,
+            option = req.params.option;
+        let user_id = req.session.sessionUser.ssoId;
+        let dftOption = option||JV.PAGING_OPTION_NORMAL;
+        getAllPagesCommon(user_id, prj_id, rpt_id, pageSize, dftOption, function(err, pageRst){
+            try {
+                rpt_xl_util.exportExcel(pageRst, pageSize, rptName, isOneSheet, null, function(newName){
+                    res.setHeader('Content-Type', 'application/vnd.openxmlformats');
+                    res.setHeader("Content-Disposition", "attachment; filename=" + strUtil.getPinYinCamelChars(rptName) + ".xlsx");
+                    let filestream = fs.createReadStream(__dirname.slice(0, __dirname.length - 28) + '/tmp/' + newName + '.xlsx');
+                    filestream.on('data', function(chunk) {
+                        res.write(chunk);
+                    });
+                    filestream.on('end', function() {
+                        res.end();
+                    });
+                });
+            } catch (e) {
+                console.log(e);
+            }
+        })
+    },
     getTestExcel: function(req, res) {
         let rpt_id = req.params.id,
             pageSize = req.params.size,
@@ -194,6 +222,30 @@ module.exports = {
             }
         })
     },
+    getPDF:function (req, res) {
+        let prj_id = req.params.prj_id,
+            rpt_id = req.params.rpt_id,
+            pageSize = req.params.size,
+            rptName = req.params.rptName
+        ;
+        let user_id = req.session.sessionUser.ssoId;
+        getAllPagesCommon(user_id, prj_id, rpt_id, pageSize, JV.PAGING_OPTION_NORMAL, function(err, pageRst){
+            rpt_pdf_util.export_pdf_file(pageRst, pageSize, rptName,function (newName) {
+                res.setHeader('Content-Type', 'application/vnd.openxmlformats');
+                res.setHeader("Content-Disposition", "attachment; filename=" + strUtil.getPinYinCamelChars(rptName) + ".pdf");
+
+                let filestream = fs.createReadStream(__dirname.slice(0, __dirname.length - 28) + '/tmp/' + newName + '.pdf');
+                filestream.on('data', function(chunk) {
+                    res.write(chunk);
+                });
+                filestream.on('end', function() {
+                    res.end();
+                });
+            })
+
+        })
+
+    },
     getTestPDF:function (req, res) {
         let rpt_id = req.params.id,
             pageSize = req.params.size,

+ 2 - 2
modules/reports/routes/report_router.js

@@ -25,9 +25,9 @@ module.exports =function (app) {
     rptRouter.get('/getTestPDF/:id/:size/:rptName', reportController.getTestPDF);
     //now is the real:
     rptRouter.post('/getReport', reportController.getReportAllPages);
-    // rptRouter.get('/getExcel/:id/:size/:rptName/:isOneSheet/:option', reportController.getExcel);
+    rptRouter.get('/getExcel/:prj_id/:rpt_id/:size/:rptName/:isOneSheet/:option', reportController.getExcel);
+    rptRouter.get('/getPDF/:prj_id/:rpt_id/:size/:rptName', reportController.getPDF);
     // rptRouter.get('/getExcelInOneBook/:ids/:size/:rptName/:option', reportController.getExcelInOneBook);
-    // rptRouter.get('/getPDF/:id/:size/:rptName', reportController.getPDF);
 
     app.use("/report_api", rptRouter);
 };

+ 43 - 14
modules/reports/rpt_component/helper/jpc_helper_area.js

@@ -8,29 +8,58 @@ let JpcAreaHelper = {
             let areaWidth = 1.0 * (band[JV.PROP_RIGHT] - band[JV.PROP_LEFT]) / multipleDispCol;
             areaWidth = areaWidth / colAmount;
             let innerLeft = 0.0, innerRight = areaWidth;
-            switch (areaNode[JV.PROP_H_CALCULATION]) {
-                case JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE] :
+            //
+            if (typeof areaNode[JV.PROP_H_CALCULATION]  === "string") {
+                switch (areaNode[JV.PROP_H_CALCULATION]) {
+                    case JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE] :
+                        innerLeft = (1.0 * areaNode[JV.PROP_LEFT] * areaWidth / JV.HUNDRED_PERCENT);
+                        innerRight = (1.0 * areaNode[JV.PROP_RIGHT] * areaWidth / JV.HUNDRED_PERCENT);
+                        break;
+                    case JV.CAL_TYPE[JV.CAL_TYPE_ABSTRACT] :
+                        innerLeft = 1.0 * areaNode[JV.PROP_LEFT] * unitFactor;
+                        innerRight = 1.0 * areaNode[JV.PROP_RIGHT] * unitFactor;
+                        break;
+                }
+            } else {
+                //颗粒度更加细化的控制,可能左右两边的计算坐标方式都不同
+                if (areaNode[JV.PROP_H_CALCULATION][JV.PROP_LEFT] === JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE]) {
                     innerLeft = (1.0 * areaNode[JV.PROP_LEFT] * areaWidth / JV.HUNDRED_PERCENT);
-                    innerRight = (1.0 * areaNode[JV.PROP_RIGHT] * areaWidth / JV.HUNDRED_PERCENT) ;
-                    break;
-                case JV.CAL_TYPE[JV.CAL_TYPE_ABSTRACT] :
+                } else {
                     innerLeft = 1.0 * areaNode[JV.PROP_LEFT] * unitFactor;
-                    innerRight = 1.0 * areaNode[JV.PROP_RIGHT] * unitFactor ;
-                    break;
+                }
+                if (areaNode[JV.PROP_H_CALCULATION][JV.PROP_RIGHT] === JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE]) {
+                    innerRight = (1.0 * areaNode[JV.PROP_RIGHT] * areaWidth / JV.HUNDRED_PERCENT);
+                } else {
+                    innerRight = 1.0 * areaNode[JV.PROP_RIGHT] * unitFactor;
+                }
             }
             //2. calculate top/bottom
             let  areaHeight = 1.0 * (band[JV.PROP_BOTTOM] - band[JV.PROP_TOP]);
             areaHeight = areaHeight / rowAmount;
             let innerTop = 0.0, innerBottom = areaHeight;
-            switch (areaNode[JV.PROP_V_CALCULATION]) {
-                case JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE] :
+            if (typeof areaNode[JV.PROP_V_CALCULATION]  === "string") {
+                switch (areaNode[JV.PROP_V_CALCULATION]) {
+                    case JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE] :
+                        innerTop = (1.0 * areaNode[JV.PROP_TOP] * areaHeight / JV.HUNDRED_PERCENT);
+                        innerBottom = (1.0 * areaNode[JV.PROP_BOTTOM] * areaHeight / JV.HUNDRED_PERCENT);
+                        break;
+                    case JV.CAL_TYPE[JV.CAL_TYPE_ABSTRACT] :
+                        innerTop = 1.0 * areaNode[JV.PROP_TOP] * unitFactor;
+                        innerBottom = 1.0 * areaNode[JV.PROP_BOTTOM] * unitFactor;
+                        break;
+                }
+            } else {
+                //颗粒度更加细化的控制,可能上下两边的计算坐标方式都不同
+                if (areaNode[JV.PROP_H_CALCULATION][JV.PROP_TOP] === JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE]) {
                     innerTop = (1.0 * areaNode[JV.PROP_TOP] * areaHeight / JV.HUNDRED_PERCENT);
-                    innerBottom = (1.0 * areaNode[JV.PROP_BOTTOM] * areaHeight / JV.HUNDRED_PERCENT) ;
-                    break;
-                case JV.CAL_TYPE[JV.CAL_TYPE_ABSTRACT] :
+                } else {
                     innerTop = 1.0 * areaNode[JV.PROP_TOP] * unitFactor;
-                    innerBottom = 1.0 * areaNode[JV.PROP_BOTTOM] * unitFactor ;
-                    break;
+                }
+                if (areaNode[JV.PROP_H_CALCULATION][JV.PROP_BOTTOM] === JV.CAL_TYPE[JV.CAL_TYPE_PERCENTAGE]) {
+                    innerBottom = (1.0 * areaNode[JV.PROP_BOTTOM] * areaHeight / JV.HUNDRED_PERCENT);
+                } else {
+                    innerBottom = 1.0 * areaNode[JV.PROP_BOTTOM] * unitFactor;
+                }
             }
             //
             let rstLeft = 0.0, rstRight = 0.0, rstTop = 0.0, rstBottom = 0.0;

+ 40 - 18
modules/reports/rpt_component/jpc_bill_tab.js

@@ -1,8 +1,6 @@
 let JV = require('./jpc_value_define');
 let JpcFieldHelper = require('./helper/jpc_helper_field');
 let JpcBandHelper = require('./helper/jpc_helper_band');
-let JpcBand = require('./jpc_band');
-let JpcFlowTabHelper = require('./helper/jpc_helper_flow_tab');
 let JpcCommonHelper = require('./helper/jpc_helper_common');
 let JpcDiscreteHelper = require('./helper/jpc_helper_discrete');
 let JpcTextHelper = require('./helper/jpc_helper_text');
@@ -20,11 +18,24 @@ JpcBillTabSrv.prototype.createNew = function(){
         let me = this;
         JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_BILL_INFO][JV.NODE_BILL_CONTENT][JV.PROP_BILL_FIELDS], null, me.disp_fields_idx);
     };
-    JpcBillTabResult.paging = function(rptTpl) {
-        let me = this, rst = 0;
-        let detail_fields = rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS];
-        if (detail_fields && detail_fields.length > 0) {
-            rst = detail_fields[0].length;
+    JpcBillTabResult.paging = function(rptTpl, dataObj) {
+        let rst = 0;
+        function getDataLength(fields_str) {
+            let dataFields = dataObj[fields_str];
+            if (dataFields && dataFields.length > 0) {
+                rst = dataFields[0].length;
+            }
+        }
+        if (rptTpl[JV.NODE_FIELD_MAP]) {
+            if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS]) {
+                getDataLength(JV.DATA_DETAIL_DATA);
+            } else if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS]) {
+                getDataLength(JV.DATA_MASTER_DATA);
+            } else if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DISCRETE_FIELDS]) {
+                getDataLength(JV.DATA_DISCRETE_DATA);
+            } else if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DISCRETE_PARAMS]) {
+                rst = 1;
+            }
         }
         return rst;
     };
@@ -36,32 +47,43 @@ JpcBillTabSrv.prototype.createNew = function(){
         //2. start to output detail-part
         let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
         //2.1 output content
-        tabRstLst.push(me.outputContent(rptTpl, dataObj, page, bands, unitFactor, controls, pageStatus));
+        tabRstLst.push(me.outputContent(rptTpl, dataObj, page, bands, unitFactor, controls, pageStatus, $CURRENT_RPT));
         //2.2 output discrete
         tabRstLst.push(JpcDiscreteHelper.outputDiscreteInfo(rptTpl[JV.NODE_BILL_INFO][JV.NODE_DISCRETE_INFO], bands, dataObj, unitFactor, pageStatus, page - 1, 1, 0, $CURRENT_RPT));
-    }
-    JpcBillTabResult.outputContent = function(rptTpl, dataObj, page, bands, unitFactor, controls, pageStatus) {
+        for (let i = 0; i < tabRstLst.length; i++) {
+            rst = rst.concat(tabRstLst[i]);
+            tabRstLst[i] = null;
+        }
+        return rst;
+    };
+    JpcBillTabResult.outputContent = function(rptTpl, dataObj, page, bands, unitFactor, controls, pageStatus, $CURRENT_RPT) {
         let me = this, rst = [];
         let tab = rptTpl[JV.NODE_BILL_INFO][JV.NODE_BILL_CONTENT];
         let band = bands[tab[JV.PROP_BAND_NAME]];
         if (band) {
-            if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] == true) {
+            if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]]) {
                 let tab_fields = tab[JV.PROP_BILL_FIELDS];
-                let data_details = dataObj[JV.DATA_MASTER_DATA];
+                let data_details = null;
+                if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS]) {
+                    data_details = dataObj[JV.DATA_DETAIL_DATA];
+                } else if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS]) {
+                    data_details = dataObj[JV.DATA_MASTER_DATA];
+                }
                 for (let i = 0; i < tab_fields.length; i++) {
                     let tab_field = tab_fields[i];
                     let data_field = null;
-                    if (me.disp_fields_idx[i] != JV.BLANK_FIELD_INDEX) {
+                    if (me.disp_fields_idx.length > i && me.disp_fields_idx[i] !== JV.BLANK_FIELD_INDEX) {
                         data_field = data_details[me.disp_fields_idx[i]];
                     } else {
-                        data_field = JE.F(tab_field[JV.PROP_FIELD_ID]);
+                        data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
                         if (data_field) {
                             data_field = data_field[JV.PROP_AD_HOC_DATA];
                         }
                     }
                     if (!(tab_field[JV.PROP_HIDDEN])) {
-                        let cellItem = JpcCommonOutputHelper.createCommonOutput(tab_field, JpcFieldHelper.getValue(data_field, page - 1), controls);
-                        cellItem[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_field[JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, 1, 0, true, false);
+                        let val = JpcFieldHelper.getValue(data_field, page - 1);
+                        let cellItem = JpcCommonOutputHelper.createCommonOutput(tab_field, val, controls);
+                        cellItem[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_field[JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, 1, 0, false, false);
                         rst.push(cellItem);
                     }
                 }
@@ -79,8 +101,8 @@ JpcBillTabSrv.prototype.createNew = function(){
             }
         }
         return rst;
-    }
+    };
     return JpcBillTabResult;
-}
+};
 
 module.exports = new JpcBillTabSrv();

+ 19 - 13
modules/reports/rpt_component/jpc_data.js

@@ -10,17 +10,21 @@ let JpcData = {
             let private_analyse = function(MASTER_FIELD_STR, DETAIL_FIELD_STR, MASTER_DATA_STR, DETAIL_DATA_STR, dataSeqArr) {
                 //1. get ID fields
                 let masterIDs = [];
-                for (let i = 0; i < rptTpl[JV.NODE_FIELD_MAP][MASTER_FIELD_STR].length; i++) {
-                    let mstFieldObj = rptTpl[JV.NODE_FIELD_MAP][MASTER_FIELD_STR][i];
-                    if (jpc_common_helper.getBoolean(mstFieldObj[JV.PROP_IS_ID])) {
-                        masterIDs.push({"idx": i, "seq": mstFieldObj[JV.PROP_ID_SEQ]});
+                if (rptTpl[JV.NODE_FIELD_MAP][MASTER_FIELD_STR]) {
+                    for (let i = 0; i < rptTpl[JV.NODE_FIELD_MAP][MASTER_FIELD_STR].length; i++) {
+                        let mstFieldObj = rptTpl[JV.NODE_FIELD_MAP][MASTER_FIELD_STR][i];
+                        if (jpc_common_helper.getBoolean(mstFieldObj[JV.PROP_IS_ID])) {
+                            masterIDs.push({"idx": i, "seq": mstFieldObj[JV.PROP_ID_SEQ]});
+                        }
                     }
                 }
                 let detailIDs = [];
-                for (let i = 0; i < rptTpl[JV.NODE_FIELD_MAP][DETAIL_FIELD_STR].length; i++) {
-                    let dtlFieldObj = rptTpl[JV.NODE_FIELD_MAP][DETAIL_FIELD_STR][i];
-                    if (jpc_common_helper.getBoolean(dtlFieldObj[JV.PROP_IS_ID])) {
-                        detailIDs.push({"idx": i, "seq": dtlFieldObj[JV.PROP_ID_SEQ]});
+                if (rptTpl[JV.NODE_FIELD_MAP][DETAIL_FIELD_STR]) {
+                    for (let i = 0; i < rptTpl[JV.NODE_FIELD_MAP][DETAIL_FIELD_STR].length; i++) {
+                        let dtlFieldObj = rptTpl[JV.NODE_FIELD_MAP][DETAIL_FIELD_STR][i];
+                        if (jpc_common_helper.getBoolean(dtlFieldObj[JV.PROP_IS_ID])) {
+                            detailIDs.push({"idx": i, "seq": dtlFieldObj[JV.PROP_ID_SEQ]});
+                        }
                     }
                 }
                 //2. sort the ID fields
@@ -84,11 +88,13 @@ let JpcData = {
                         }
                     }
                 } else { //if no master data
-                    let field = dataObj[DETAIL_DATA_STR][0];
-                    //dataSeqArr = [[]];
-                    dataSeqArr.push([]);
-                    for (let i = 0; i < field.length; i++) {
-                        dataSeqArr[0].push(i);
+                    if (dataObj && dataObj[DETAIL_DATA_STR] && dataObj[DETAIL_DATA_STR].length > 0) {
+                        //may be bill type report which may only have discrete fields!
+                        let field = dataObj[DETAIL_DATA_STR][0];
+                        dataSeqArr.push([]);
+                        for (let i = 0; i < field.length; i++) {
+                            dataSeqArr[0].push(i);
+                        }
                     }
                 }
             };

+ 5 - 2
modules/reports/rpt_component/jpc_ex.js

@@ -125,6 +125,9 @@ JpcExSrv.prototype.createNew = function(){
                 me.flowTabEx.sorting(rptTpl, dataObj, dataHelper.exDataSeq.slice(0));
             }
         }
+        if (me.billTab) {
+            me.billTab.sorting(rptTpl, dataObj, dataHelper.dataSeq.slice(0));
+        }
         if (me.crossTab) {
             me.crossTab.sorting(rptTpl, dataObj, dataHelper.dataSeq.slice(0));
         }
@@ -155,7 +158,7 @@ JpcExSrv.prototype.createNew = function(){
             //me.totalPages = me.crossTab.preSetupPages(rptTpl, defProperties, dftPagingOption);
             me.totalPages = me.crossTab.preSetupPages(rptTpl, defProperties, JV.PAGING_OPTION_NORMAL); //infinity对交叉表来说无意义
         } else if (me.billTab) {
-            me.totalPages = me.billTab.paging();
+            me.totalPages = me.billTab.paging(rptTpl, dataObj);
         }
     };
     JpcResult.executeFormulas = function(runType, $CURRENT_TEMPLATE, $CURRENT_DATA, $CURRENT_RPT) {
@@ -244,7 +247,7 @@ JpcExSrv.prototype.createNew = function(){
             } else if (me.crossTab) {
                 rst[JV.PROP_CELLS] = me.crossTab.outputAsSimpleJSONPage(rptTpl, dataObj, page, bands, controls, me);
             } else if (me.billTab) {
-                //
+                rst[JV.PROP_CELLS] = me.billTab.outputAsSimpleJSONPage(rptTpl, dataObj, page, bands, controls, me);
             }
             if (!(me.flowTab && me.flowTab.paging_option === JV.PAGING_OPTION_INFINITY)) {
                 let pageMergeBorder = getPageMergeBorder();

+ 2 - 1
modules/reports/rpt_component/jpc_flow_tab.js

@@ -370,7 +370,8 @@ JpcFlowTabSrv.prototype.createNew = function(){
                         }
                     }
                     //检测是否可退出
-                    if (currentRecAmt >= ttlSegRecAmt) {
+                    if ((currentRecAmt >= ttlSegRecAmt) && (pageIdx % me.multiCols === 0)) {
+                        //备注:这里必须得考虑多栏的情况,否则会造成pageStatus出界的问题
                         break;
                     }
                     //控制阀值,超过阀值则强制退出,防止死循环

+ 68 - 15
modules/reports/util/rpt_construct_data_util.js

@@ -18,7 +18,7 @@ const GLJ_TYPE = {
     Machine: 3,
     Main_Material: 4,
     Equipment: 5
-}
+};
 
 class Rpt_Common{
     initialize(rpt_tpl, currentDataObj) {
@@ -143,6 +143,9 @@ class Rpt_Data_Extractor {
         pri_setup_filter(JV.NODE_DETAIL_FIELDS);
         pri_setup_filter(JV.NODE_MASTER_FIELDS_EX);
         pri_setup_filter(JV.NODE_DETAIL_FIELDS_EX);
+        if (rst.length === 0) {
+            rst.push(projectConst.RATION_ASS);
+        }
         return rst;
     };
 
@@ -246,7 +249,7 @@ function summaryData(sourceData, handleCfg, prjData){
             }
         }
         return ( "grp_key_" + keys.join('_'));
-    }
+    };
     let sumObj = {};
     for (let dtl of tempRstArr) {
         let grpKey = private_get_grp_key(dtl);
@@ -256,14 +259,15 @@ function summaryData(sourceData, handleCfg, prjData){
         } else {
             for (let sumKey of handleCfg[JV.PROP_SUM_SUM_KEYS]) {
                 if (dtl[sumKey]) {
-                    sumObj[grpKey][sumKey] += dtl[sumKey];
+                    // sumObj[grpKey][sumKey] += dtl[sumKey];
+                    sumObj[grpKey][sumKey] = parseFloat(sumObj[grpKey][sumKey]) + parseFloat(dtl[sumKey]);
                 }
             }
         }
     }
     delete sourceData.data;
     sourceData.data = rstArr;
-    // fsUtil.wirteObjToFile(sourceData.data, "D:/GitHome/ConstructionCost/tmp/sumRst.js");
+    // fsUtil.writeObjToFile(sourceData.data, "D:/GitHome/ConstructionCost/tmp/sumRst.js");
 }
 
 function filterData(sourceData, handleCfg, prjData) {
@@ -306,7 +310,7 @@ function filterData(sourceData, handleCfg, prjData) {
                 rst = true;
         }
         return rst;
-    }
+    };
     for (let item of tempRstArr) {
         let compRst = true;
         let curComparePrjData = null;
@@ -331,7 +335,7 @@ function filterData(sourceData, handleCfg, prjData) {
     }
     delete sourceData.data;
     sourceData.data = rstArr;
-    // fsUtil.wirteObjToFile(sourceData.data, "D:/GitHome/ConstructionCost/tmp/filteredRst.js");
+    // fsUtil.writeObjToFile(sourceData.data, "D:/GitHome/ConstructionCost/tmp/filteredRst.js");
 }
 
 function adjustData(sourceData, adjustCfg) {
@@ -527,13 +531,13 @@ function sortData(sourceData, sortCfg, prjData) {
             treeUtil.getFlatArray(rst, destArr);
             delete sourceData.data;
             sourceData.data = destArr;
-            // fsUtil.wirteObjToFile(sourceData.data, "D:/GitHome/ConstructionCost/tmp/sortedAndFlattedRst.js");
+            // fsUtil.writeObjToFile(sourceData.data, "D:/GitHome/ConstructionCost/tmp/sortedAndFlattedRst.js");
             break;
         case "normal":
             private_normal_sort(tempRstArr, sortCfg[JV.PROP_SORT_KEYS]);
             delete sourceData.data;
             sourceData.data = tempRstArr;
-            // fsUtil.wirteObjToFile(sourceData.data, "D:/GitHome/ConstructionCost/tmp/normalSortedRst.js");
+            // fsUtil.writeObjToFile(sourceData.data, "D:/GitHome/ConstructionCost/tmp/normalSortedRst.js");
             break;
         case "accord_to_parent":
             let pcKey = sortCfg[JV.PROP_PARENT_CHILD_SORT_KEY];
@@ -592,11 +596,6 @@ function sortData(sourceData, sortCfg, prjData) {
 
 function setupFunc(obj, prop, ownRawObj) {
     obj[prop] = {};
-    // if (prop === projectConst.CALC_PROGRAM) {
-    //     obj[prop]["myOwnRawDataObj"] = ownRawObj.data;
-    // } else {
-    //     obj[prop]["myOwnRawDataObj"] = ownRawObj;
-    // }
     obj[prop]["myOwnRawDataObj"] = ownRawObj;
     obj[prop].getProperty = ext_getPropety;
     obj[prop].getFee = ext_getFee;
@@ -620,14 +619,63 @@ function shielded_exec_env($PROJECT, $ME, rptDataItemObj) {
     }
 }
 
+function getActPropertyVal(firstPropKey, secPropKey, orgObj) {
+    let rst = null;
+    if (orgObj[firstPropKey]) {
+        rst = orgObj[firstPropKey];
+    } else if (orgObj[secPropKey]){
+        rst = orgObj[secPropKey];
+    }
+    return rst;
+}
+
+function getDeepProperty(propKey, orgObj, destArr) {
+    let keys = propKey.split(".");
+    let dftPropKey = "key", dftPropVal = "value", secDftPropVal = "items";
+    let parent = orgObj, lastVal = null;
+    for (let key of keys) {
+        if (parent instanceof Array) {
+            for (let item of parent) {
+                if (item[dftPropKey] === key) {
+                    lastVal = getActPropertyVal(dftPropVal, secDftPropVal, item);
+                    break;
+                }
+            }
+        } else {
+            lastVal = null;
+            if (parent[key] !== undefined) {
+                lastVal = parent[key];
+            } else if (parent[secDftPropVal]){
+                for (let item of parent[secDftPropVal]) {
+                    if (item[dftPropKey] === key) {
+                        // lastVal = item[dftPropVal];
+                        lastVal = getActPropertyVal(dftPropVal, secDftPropVal, item);
+                        break;
+                    }
+                }
+            }
+        }
+        parent = lastVal;
+        if (parent === null) break;
+    }
+    if (destArr && destArr instanceof Array) {
+        destArr.push(lastVal);
+    }
+}
+
 function ext_mainGetPropety(propKey) {
     let rst = [], parentObj = this;
     let dtObj = parentObj["myOwnRawDataObj"];
     if (propKey && dtObj) {
         if (dtObj.hasOwnProperty("property")) {
-            rst.push(dtObj["property"][propKey]);
+            if (!dtObj["property"][propKey] && dtObj[propKey]) {
+                rst.push(dtObj[propKey]);
+            } else {
+                getDeepProperty(propKey, dtObj["property"], rst);
+            }
         } else  {
-            rst.push(dtObj[propKey]);
+            // rst.push(dtObj[propKey]);
+            getDeepProperty(propKey, dtObj, rst);
         }
     }
     return rst;
@@ -640,6 +688,11 @@ function ext_getPropety(propKey) {
         for (let dItem of dtObj.data) {
             let doc = (dItem._doc === null || dItem._doc === undefined)?dItem:dItem._doc;
             if (doc.hasOwnProperty("property")) {
+                // if (!doc["property"][propKey] && doc[propKey]) {
+                //     rst.push(doc[propKey]);
+                // } else {
+                //     getDeepProperty(propKey, doc["property"], rst);
+                // }
                 rst.push(doc["property"][propKey]);
             } else if (doc.hasOwnProperty(propKey)) {
                 rst.push(doc[propKey]);

+ 42 - 12
modules/reports/util/rpt_excel_util.js

@@ -158,6 +158,9 @@ function writeStyles(stylesObj){
         if (strUtil.convertStrToBoolean(font[JV.FONT_PROPS[3]])) {
             rst.push('<b/>');
         }
+        if (strUtil.convertStrToBoolean(font[JV.FONT_PROPS[5]])) {
+            rst.push('<u/>');
+        }
         rst.push('<sz val="' + font.size + '"/>');
         rst.push('<color indexed="' + font.colorIdx + '"/>');
         rst.push('<name val="' + font[JV.FONT_PROPS[0]] + '"/>');
@@ -270,7 +273,13 @@ function writeSharedString(sharedStrList){
         for (let i = 0; i < sharedStrList.length; i++) {
             //rst.push('<si><t>' + sharedStrList[i] + '</t></si>');
             if (typeof sharedStrList[i] === 'string') {
-                rst.push('<si><t>' + sharedStrList[i].replace('|','\r\n') + '</t></si>');
+                if (sharedStrList[i].indexOf('|') >= 0) {
+                    //rst.push('<si><t>' + sharedStrList[i].split('|').join('\r\n') + '</t></si>');
+                    rst.push('<si><t>' + sharedStrList[i].split('|').join('\n') + '</t></si>');
+                } else {
+                    rst.push('<si><t>' + sharedStrList[i] + '</t></si>');
+                }
+                // rst.push('<si><t>' + sharedStrList[i].replace('|','\r\n') + '</t></si>');
             } else {
                 rst.push('<si><t>' + sharedStrList[i] + '</t></si>');
             }
@@ -332,7 +341,7 @@ function writeSheets(pageData, paperSize, sharedStrList, stylesObj, isSinglePage
 function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
     let rst = [], xPos = [], yPos = [], yMultiPos = [], headerStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
     let cacheBorderCell = {};
-    let currentPageMergePos = null;
+    let currentPageMergePos = null; //在 JV.PAGING_OPTION_INFINITY 场合应用
     let private_pre_analyze_pos = function(){
         let cell, pos;
         let self_analyze_sheet_pos = function (theShtData, theXPos, theYPos) {
@@ -416,11 +425,17 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
             stylesObj.fonts = [];
             //for (let i = 0; i < sheetData.font_collection)
         }
-        let sheetFont = pageData.font_collection[cell.font];
+        let sheetFont = null;
+        if (typeof cell[JV.PROP_FONT] === "string") {
+            sheetFont = pageData[JV.NODE_FONT_COLLECTION][cell[JV.PROP_FONT]];
+        } else {
+            sheetFont = cell[JV.PROP_FONT];
+        }
         for (let i = 0; i < stylesObj.fonts.length; i++) {
             let font = stylesObj.fonts[i];
             if (sheetFont) {
-                if (font[JV.FONT_PROPS[0]] === sheetFont[JV.FONT_PROPS[0]] && font.size === Math.round(sheetFont[JV.FONT_PROPS[1]] * 3 / 4) && font[JV.FONT_PROPS[3]] == sheetFont[JV.FONT_PROPS[3]]) {
+                if (font[JV.FONT_PROPS[0]] === sheetFont[JV.FONT_PROPS[0]] && font.size === Math.round(sheetFont[JV.FONT_PROPS[1]] * 3 / 4)
+                    && font[JV.FONT_PROPS[3]] === sheetFont[JV.FONT_PROPS[3]] && font[JV.FONT_PROPS[5]] === sheetFont[JV.FONT_PROPS[5]]) {
                     hasFont = true;
                     rst = i;
                     break;
@@ -436,6 +451,7 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
             font.charset = 134;
             font.colorIdx = "8";
             font[JV.FONT_PROPS[3]] = sheetFont[JV.FONT_PROPS[3]]; //font bold
+            font[JV.FONT_PROPS[5]] = sheetFont[JV.FONT_PROPS[5]]; //font underline
             stylesObj.fonts.push(font);
             rst = stylesObj.fonts.length - 1;
         }
@@ -476,7 +492,10 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
         return rst;
     };
     let private_chkAndGetMergeLine = function(cell, sheetBorder, borderStr, needFurtherChk) {
-        let rst = 0, mergeBorder = pageData[JV.BAND_PROP_MERGE_BAND];
+        let rst = 0,
+            mergeBorder = (sheetData[JV.PROP_PAGE_MERGE_BORDER])?sheetData[JV.PROP_PAGE_MERGE_BORDER]:pageData[JV.BAND_PROP_MERGE_BAND],
+            mergeBand = pageData[JV.BAND_PROP_MERGE_BAND]
+        ;
         if (sheetBorder[borderStr] && sheetBorder[borderStr][JV.PROP_LINE_WEIGHT] !== undefined) {
             rst = sheetBorder[borderStr][JV.PROP_LINE_WEIGHT];
         }
@@ -489,7 +508,7 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
                     for (let i = 0; i < topSide.length; i++) {
                         if (cell[JV.PROP_AREA][JV.PROP_TOP] >= topSide[i]) {
                             if (cell[JV.PROP_AREA][JV.PROP_BOTTOM] <= bottomSide[i]) {
-                                let destStyle = pageData[JV.NODE_STYLE_COLLECTION][mergeBorder[JV.PROP_STYLE][JV.PROP_ID]];
+                                let destStyle = pageData[JV.NODE_STYLE_COLLECTION][mergeBand[JV.PROP_STYLE][JV.PROP_ID]];
                                 rst = destStyle[borderStr][JV.PROP_LINE_WEIGHT];
                                 break;
                             }
@@ -498,13 +517,13 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
                         }
                     }
                 } else {
-                    let destStyle = pageData[JV.NODE_STYLE_COLLECTION][mergeBorder[JV.PROP_STYLE][JV.PROP_ID]];
+                    let destStyle = pageData[JV.NODE_STYLE_COLLECTION][mergeBand[JV.PROP_STYLE][JV.PROP_ID]];
                     rst = destStyle[borderStr][JV.PROP_LINE_WEIGHT];
                 }
             }
         } else {
             if (cell[JV.PROP_AREA][borderStr] === mergeBorder[borderStr]) {
-                let destStyle = pageData[JV.NODE_STYLE_COLLECTION][mergeBorder[JV.PROP_STYLE][JV.PROP_ID]];
+                let destStyle = pageData[JV.NODE_STYLE_COLLECTION][mergeBand[JV.PROP_STYLE][JV.PROP_ID]];
                 if (needFurtherChk) {
                     if (cell[JV.PROP_AREA][JV.PROP_TOP] >= mergeBorder[JV.PROP_TOP] &&
                         cell[JV.PROP_AREA][JV.PROP_BOTTOM] <= mergeBorder[JV.PROP_BOTTOM]) {
@@ -568,12 +587,23 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj){
         let rst = 1, hasStyle = false;
         if (!(stylesObj.cellXfs)) stylesObj.cellXfs = [];
         let fontId = private_getFontId(cell);
-        let fontAngle = parseInt(pageData.font_collection[cell.font].FontAngle);
+        let fontAngle = 0;
+        if (typeof cell[JV.PROP_FONT] === "string") {
+            fontAngle = parseInt(pageData[JV.NODE_FONT_COLLECTION][cell[JV.PROP_FONT]].FontAngle);
+        } else {
+            fontAngle = parseInt(cell[JV.PROP_FONT].FontAngle);
+        }
+
         let borderId = private_getBorderId(cell);
-        let cellControl = pageData[JV.NODE_CONTROL_COLLECTION][cell[JV.PROP_CONTROL]];
+        let cellControl = null;
+        if (typeof cell[JV.PROP_CONTROL] === "string") {
+            cellControl = pageData[JV.NODE_CONTROL_COLLECTION][cell[JV.PROP_CONTROL]];
+        } else {
+            cellControl = cell[JV.PROP_CONTROL];
+        }
         for (let i = 0; i < stylesObj.cellXfs.length; i++) {
             let sheetControl = stylesObj.cellXfs[i];
-            if (sheetControl.fontId == fontId && sheetControl.borderId == borderId) {
+            if (sheetControl.fontId === fontId && sheetControl.borderId === borderId) {
                 if (private_checkControl(cellControl, sheetControl)) {
                     rst = i;
                     hasStyle = true;
@@ -983,7 +1013,7 @@ module.exports = {
             }
             //3. everything is ok, then call me
             me.exportExcel(newPageData, paperSize, fName, 'false', sheetNames, callback);
-            fsUtil.wirteObjToFile(newPageData, 'D:/GitHome/ConstructionOperation/tmp/combinedHeader.js');
+            fsUtil.writeObjToFile(newPageData, 'D:/GitHome/ConstructionOperation/tmp/combinedHeader.js');
         } catch (e) {
             console.log(e);
         }

+ 132 - 50
modules/reports/util/rpt_pdf_util.js

@@ -6,8 +6,8 @@
  */
 
 const prf_cons = require('./pdf_base_files/rpt_pdf_consts');
-var pdf = require('pdfkit');
-var fs = require('fs');
+let pdf = require('pdfkit');
+let fs = require('fs');
 let jpcCmnHelper = require('../rpt_component/helper/jpc_helper_common');
 let DPI = jpcCmnHelper.getScreenDPI()[0];
 //let JV = prf_cons.JV;
@@ -21,7 +21,7 @@ module.exports ={
 function export_pdf_file (pageData, paperSize, fName, callback) {
     let offsetX= 10;
     let offsetY=10;
-    var doc = new pdf({autoFirstPage: false});
+    let doc = new pdf({autoFirstPage: false});
     let newName = '' + (new Date()).valueOf();
     let stream = doc.pipe(fs.createWriteStream(__dirname.slice(0, __dirname.length - 21) + '/tmp/'+newName+'.pdf'));
     let pageObj = pageData;
@@ -29,18 +29,38 @@ function export_pdf_file (pageData, paperSize, fName, callback) {
     let paperSizeIdx = JV.PAGES_SIZE_STR.indexOf(paperSize);
     let size = JV.PAGES_SIZE[paperSizeIdx];
 
+    function private_getIniPageMergeBorder(mergedBand) {
+        let rst = {};
+        rst[JV.PROP_LEFT] = mergedBand[JV.PROP_LEFT];
+        rst[JV.PROP_RIGHT] = mergedBand[JV.PROP_RIGHT];
+        rst[JV.PROP_TOP] = mergedBand[JV.PROP_TOP];
+        rst[JV.PROP_BOTTOM] = mergedBand[JV.PROP_BOTTOM];
+        rst[JV.PROP_STYLE] = mergedBand[JV.PROP_STYLE];
+        return rst;
+    }
+
+    let newPageMergeBand = private_getIniPageMergeBorder(pageObj[JV.BAND_PROP_MERGE_BAND]);
     if (pageObj && pageObj.items.length > 0 ) {
         for(let i=0;i<pageObj.items.length;i++){
-            doc.addPage({size:[size[1]*DPI,size[0]*DPI]});
-            var page = pageObj.items[i],
+            if (pageData[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE][0] > pageData[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE][1]) {
+                doc.addPage({size:[size[1]*DPI,size[0]*DPI]});
+            } else {
+                doc.addPage({size:[size[0]*DPI,size[1]*DPI]});
+            }
+            let page = pageObj.items[i],
                 fonts = pageObj[JV.NODE_FONT_COLLECTION],
                 styles = pageObj[JV.NODE_STYLE_COLLECTION],
-                controls = pageObj[JV.NODE_CONTROL_COLLECTION],
-                mergedBand = pageObj[JV.BAND_PROP_MERGE_BAND];
+                controls = pageObj[JV.NODE_CONTROL_COLLECTION];
 
-            for (var j = 0; j < page.cells.length; j++) {
-                var cell = page.cells[j];
-                private_drawCell(cell, fonts, styles, controls, mergedBand);
+            if (page[JV.PROP_PAGE_MERGE_BORDER]) {
+                newPageMergeBand[JV.PROP_LEFT] = page[JV.PROP_PAGE_MERGE_BORDER][JV.PROP_LEFT];
+                newPageMergeBand[JV.PROP_RIGHT] = page[JV.PROP_PAGE_MERGE_BORDER][JV.PROP_RIGHT];
+                newPageMergeBand[JV.PROP_TOP] = page[JV.PROP_PAGE_MERGE_BORDER][JV.PROP_TOP];
+                newPageMergeBand[JV.PROP_BOTTOM] = page[JV.PROP_PAGE_MERGE_BORDER][JV.PROP_BOTTOM];
+            }
+            for (let j = 0; j < page.cells.length; j++) {
+                let cell = page.cells[j];
+                private_drawCell(cell, fonts, styles, controls, newPageMergeBand);
             }
         }
     }
@@ -48,34 +68,43 @@ function export_pdf_file (pageData, paperSize, fName, callback) {
     stream.on('finish',function () {
         console.log(newName + ".pdf was written.");
         callback(newName);
-    })
+    });
 
+    function private_chkIfInMergedBand(mergedBand, cell) {
+        let rst = false;
+        if (mergedBand && cell) {
+            rst = mergedBand[JV.PROP_TOP] <= cell[JV.PROP_AREA][JV.PROP_TOP] && mergedBand[JV.PROP_BOTTOM] >= cell[JV.PROP_AREA][JV.PROP_BOTTOM] &&
+                mergedBand[JV.PROP_LEFT] <= cell[JV.PROP_AREA][JV.PROP_LEFT] && mergedBand[JV.PROP_RIGHT] >= cell[JV.PROP_AREA][JV.PROP_RIGHT];
+        }
+        return rst;
+    }
 
     function private_drawCell(cell, fonts, styles, controls, mergedBand) {
         doc.save();
         //doc.translate(0.5,0.5);
-        var style = styles[cell[JV.PROP_STYLE]];
+        let style = styles[cell[JV.PROP_STYLE]];
         if (style) {
-            private_drawLine(cell, doc, style, JV.PROP_TOP, [JV.PROP_LEFT, JV.PROP_TOP],[JV.PROP_RIGHT, JV.PROP_TOP], mergedBand, styles);
-            private_drawLine(cell, doc, style, JV.PROP_RIGHT, [JV.PROP_RIGHT, JV.PROP_TOP],[JV.PROP_RIGHT, JV.PROP_BOTTOM], mergedBand, styles);
-            private_drawLine(cell, doc, style, JV.PROP_BOTTOM, [JV.PROP_RIGHT, JV.PROP_BOTTOM],[JV.PROP_LEFT, JV.PROP_BOTTOM], mergedBand, styles);
-            private_drawLine(cell, doc, style, JV.PROP_LEFT, [JV.PROP_LEFT, JV.PROP_BOTTOM],[JV.PROP_LEFT, JV.PROP_TOP], mergedBand, styles);
+            let isNeedMergeBand = private_chkIfInMergedBand(mergedBand, cell);
+            private_drawLine(cell, doc, style, JV.PROP_TOP, [JV.PROP_LEFT, JV.PROP_TOP],[JV.PROP_RIGHT, JV.PROP_TOP], mergedBand, styles, isNeedMergeBand);
+            private_drawLine(cell, doc, style, JV.PROP_RIGHT, [JV.PROP_RIGHT, JV.PROP_TOP],[JV.PROP_RIGHT, JV.PROP_BOTTOM], mergedBand, styles, isNeedMergeBand);
+            private_drawLine(cell, doc, style, JV.PROP_BOTTOM, [JV.PROP_RIGHT, JV.PROP_BOTTOM],[JV.PROP_LEFT, JV.PROP_BOTTOM], mergedBand, styles, isNeedMergeBand);
+            private_drawLine(cell, doc, style, JV.PROP_LEFT, [JV.PROP_LEFT, JV.PROP_BOTTOM],[JV.PROP_LEFT, JV.PROP_TOP], mergedBand, styles, isNeedMergeBand);
         }
         private_drawCellText(cell, fonts, controls);
         doc.restore();
 
     }
 
-    function private_drawLine(cell, doc, style, styleBorderDest, startP, destP, mergedBand, styles) {
+    function private_drawLine(cell, doc, style, styleBorderDest, startP, destP, mergedBand, styles, isNeedMergeBand) {
         //doc.beginPath();
-        var destStyle = style;
+        let destStyle = style;
         if (mergedBand) {
-            if (mergedBand[styleBorderDest] == cell[JV.PROP_AREA][styleBorderDest]) {
+            if (isNeedMergeBand && parseFloat(mergedBand[styleBorderDest]) === parseFloat(cell[JV.PROP_AREA][styleBorderDest])) {
                 destStyle = styles[mergedBand[JV.PROP_STYLE][JV.PROP_ID]];
             }
         }
         doc.moveTo(cell[JV.PROP_AREA][startP[0]] + offsetX, cell[JV.PROP_AREA][startP[1]] + offsetY);
-        if (destStyle[styleBorderDest] && destStyle[styleBorderDest][JV.PROP_LINE_WEIGHT] != 0) {
+        if (destStyle[styleBorderDest] && parseFloat(destStyle[styleBorderDest][JV.PROP_LINE_WEIGHT]) !== 0) {
             doc.lineWidth(1.0 * destStyle[styleBorderDest][JV.PROP_LINE_WEIGHT]);
             doc.lineTo(cell[JV.PROP_AREA][destP[0]] + offsetX, cell[JV.PROP_AREA][destP[1]] + offsetY);
             doc.strokeColor(destStyle[styleBorderDest][JV.PROP_COLOR]);
@@ -84,12 +113,24 @@ function export_pdf_file (pageData, paperSize, fName, callback) {
     }
     function private_drawCellText(cell, fonts, controls) {
         if (cell[JV.PROP_VALUE]) {
-            var values = ("" + cell[JV.PROP_VALUE]).split('|');
-            var font = fonts[cell[JV.PROP_FONT]];
-            var control = controls[cell[JV.PROP_CONTROL]];
-            var height = cell[JV.PROP_AREA][JV.PROP_BOTTOM] - cell[JV.PROP_AREA][JV.PROP_TOP];
-            var area = [cell[JV.PROP_AREA][JV.PROP_LEFT] + offsetX, cell[JV.PROP_AREA][JV.PROP_TOP] + offsetY, cell[JV.PROP_AREA][JV.PROP_RIGHT] + offsetX, cell[JV.PROP_AREA][JV.PROP_BOTTOM] + offsetY];
-            for (var i = 0; i < values.length; i++) {
+            let values = ("" + cell[JV.PROP_VALUE]).split('|');
+            // let font = fonts[cell[JV.PROP_FONT]];
+            let font = null;
+            if (typeof cell[JV.PROP_FONT] === "string") {
+                font = fonts[cell[JV.PROP_FONT]];
+            } else {
+                font = cell[JV.PROP_FONT];
+            }
+            // let control = controls[cell[JV.PROP_CONTROL]];
+            let control = null;
+            if (typeof cell[JV.PROP_CONTROL] === "string") {
+                control = controls[cell[JV.PROP_CONTROL]];
+            } else {
+                control = cell[JV.PROP_CONTROL];
+            }
+            let height = cell[JV.PROP_AREA][JV.PROP_BOTTOM] - cell[JV.PROP_AREA][JV.PROP_TOP];
+            let area = [cell[JV.PROP_AREA][JV.PROP_LEFT] + offsetX, cell[JV.PROP_AREA][JV.PROP_TOP] + offsetY, cell[JV.PROP_AREA][JV.PROP_RIGHT] + offsetX, cell[JV.PROP_AREA][JV.PROP_BOTTOM] + offsetY];
+            for (let i = 0; i < values.length; i++) {
                 area[JV.IDX_TOP] = cell[JV.PROP_AREA][JV.PROP_TOP] + i * (height / values.length) + offsetY;
                 area[JV.IDX_BOTTOM] = cell[JV.PROP_AREA][JV.PROP_TOP] + (i + 1) * (height / values.length) + offsetY;
                 private_drawText(values[i], area, font, control);
@@ -98,22 +139,22 @@ function export_pdf_file (pageData, paperSize, fName, callback) {
     }
 
     function private_drawText(val, area, font, control) {
-        var dftFontHeight = 12;
-        var output = [];
+        let dftFontHeight = 12;
+        let output = [];
         if (font) {
             dftFontHeight = 1 * font[JV.FONT_PROPS[1]];
-            var dftFontBold = font[JV.FONT_PROPS[3]];
-            var dftFontItalic = font[JV.FONT_PROPS[4]];
-            if (dftFontBold && dftFontBold == 'T') {
+            let dftFontBold = font[JV.FONT_PROPS[3]];
+            let dftFontItalic = font[JV.FONT_PROPS[4]];
+            if (dftFontBold && dftFontBold === 'T') {
                 doc.font(__dirname+'/pdf_base_files/hwxsb.ttf');
-            }else if(dftFontItalic && dftFontItalic == 'T'){
+            }else if(dftFontItalic && dftFontItalic === 'T'){
                 doc.font(__dirname+'/pdf_base_files/Smart-italic.ttf');
             }else {
                 doc.font(__dirname+'/pdf_base_files/Smart.ttf');
             }
             doc.fontSize(dftFontHeight);
         }
-        var options={};
+        let options={};
         if (control) {
             private_setupAreaH(area, control.Horizon, font.FontAngle, dftFontHeight, output,options);
             private_setupAreaV(area, control.Vertical, font.FontAngle, dftFontHeight, output);
@@ -121,8 +162,8 @@ function export_pdf_file (pageData, paperSize, fName, callback) {
             private_setupAreaH(area, "left", font.FontAngle, dftFontHeight, output,options);
             private_setupAreaV(area, "bottom", font.FontAngle, dftFontHeight, output);
         }
-        var w = area[JV.IDX_RIGHT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT] - area[JV.IDX_LEFT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT];
-        if (font.FontAngle != "0") {
+        let w = area[JV.IDX_RIGHT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT] - area[JV.IDX_LEFT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT];
+        if (parseInt(font.FontAngle) !== 0) {
             w = area[JV.IDX_BOTTOM] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM] - area[JV.IDX_TOP] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
         }
         doc.save();
@@ -142,12 +183,54 @@ function export_pdf_file (pageData, paperSize, fName, callback) {
             }
         }
 
-        var rotateOptions;
-        if (font.FontAngle != "0") {
+        function private_drawUnderline() {
+            //A. 暂不支持角度; B. PDF输出时,坐标没有translate
+            let ctx = doc;
+            //1. 计算下划线的相关坐标
+            let width = ctx.widthOfString(val);
+            let height = dftFontHeight;
+            let startX = area[JV.IDX_LEFT], startY = area[JV.IDX_TOP], endX = area[JV.IDX_RIGHT], endY = area[JV.IDX_BOTTOM];
+            // let startX = 0, startY = 0, endX = width, endY = startY;
+            if (control.Horizon === "left") {
+                startX = Math.round(area[JV.IDX_LEFT] + JV.OUTPUT_OFFSET[JV.IDX_LEFT]);
+            } else if (control.Horizon === "right") {
+                startX = Math.round(area[JV.IDX_RIGHT] - width - JV.OUTPUT_OFFSET[JV.IDX_RIGHT]);
+            } else {
+                startX = Math.round( area[JV.IDX_LEFT] + (area[JV.IDX_RIGHT] - area[JV.IDX_LEFT] - width) / 2);
+            }
+            endX = Math.round(startX + width);
+
+            if (control.Vertical === "top") {
+                startY = Math.round(area[JV.IDX_TOP] + JV.OUTPUT_OFFSET[JV.IDX_TOP] + JV.OUTPUT_OFFSET[JV.IDX_BOTTOM] + height);
+            } else if (control.Vertical === "bottom") {
+                startY = Math.round(area[JV.IDX_BOTTOM] + JV.OUTPUT_OFFSET[JV.IDX_BOTTOM]);
+            } else {
+                startY = Math.round( area[JV.IDX_TOP] + (area[JV.IDX_BOTTOM] - area[JV.IDX_TOP] + height) / 2) + JV.OUTPUT_OFFSET[JV.IDX_TOP] + JV.OUTPUT_OFFSET[JV.IDX_BOTTOM];
+            }
+            endY = Math.round(startY);
+            //2. 画线
+            // ctx.save();
+            if ( output[1] !== Math.round(output[1])) {
+                ctx.translate(0,0.5);
+            }
+            // ctx.beginPath();
+            ctx.moveTo(startX, startY);
+            ctx.lineWidth(1);
+            ctx.strokeStyle = "BLACK";
+            ctx.lineTo(endX, endY);
+            ctx.stroke();
+            // ctx.restore();
+        }
+
+        let rotateOptions;
+        if (font[JV.FONT_PROPS[5]] === 'T' && parseInt(font.FontAngle) === 0) {
+            private_drawUnderline();
+        }
+        if (parseInt(font.FontAngle) !== 0) {
             if (control){
-                rotateOptions=private_setupAreaRotateOption(area,w,control.Vertical,dftFontHeight, output);
-            }else {
-                rotateOptions=private_setupAreaRotateOption(area,w,"bottom",dftFontHeight, output);
+                rotateOptions = private_setupAreaRotateOption(area,w,control.Vertical,dftFontHeight, output);
+            } else {
+                rotateOptions = private_setupAreaRotateOption(area,w,"bottom",dftFontHeight, output);
             }
             doc.rotate(font.FontAngle,rotateOptions);
         }
@@ -156,15 +239,15 @@ function export_pdf_file (pageData, paperSize, fName, callback) {
     }
 
     function private_setupAreaH(area, type, fontAngle, dftFontHeight, outputPoint,options) {
-        var lType = type;
-        if (type != "left" && type != "right" && type != "center") lType = "left";
+        let lType = type;
+        if (type !== "left" && type !== "right" && type !== "center") lType = "left";
         options.align=lType;
         outputPoint[0]=1 * area[JV.IDX_LEFT]+ JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT];
     }
 
     function private_setupAreaV(area, type, fontAngle, dftFontHeight, outputPoint) {
-        var lType = type;
-        if (type != "top" && type != "bottom" && type != "center") lType = "top";
+        let lType = type;
+        if (type !== "top" && type !== "bottom" && type !== "center") lType = "top";
         switch (lType) {
             case "top":
                 outputPoint[1] = 1 * area[JV.IDX_TOP] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
@@ -179,13 +262,12 @@ function export_pdf_file (pageData, paperSize, fName, callback) {
     }
 
     function private_setupAreaRotateOption(area,w, type="top",dftFontHeight,outputPoint){
-        var x = (area[JV.IDX_RIGHT] - area[JV.IDX_LEFT])/2+area[JV.IDX_LEFT];
-        var y =(area[JV.IDX_BOTTOM] - area[JV.IDX_TOP])/2+ area[JV.IDX_TOP];
-        var rotateOptions = {origin:[x,y]};
-        var h = area[JV.IDX_RIGHT] - area[JV.IDX_LEFT];
+        let x = (area[JV.IDX_RIGHT] - area[JV.IDX_LEFT])/2+area[JV.IDX_LEFT];
+        let y =(area[JV.IDX_BOTTOM] - area[JV.IDX_TOP])/2+ area[JV.IDX_TOP];
+        let rotateOptions = {origin:[x,y]};
+        let h = area[JV.IDX_RIGHT] - area[JV.IDX_LEFT];
         outputPoint[0]=x-w/2+JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT];
-        var lType = type;
-        switch (lType) {
+        switch (type) {
             case "top":
                 outputPoint[1] = y- h/2+ JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
                 break;

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

@@ -88,7 +88,14 @@ class LoginController {
             console.log(error);
             return response.json({error: 1, msg: error});
         }
-        response.json({error: 0, msg: '', login_ask: preferenceSetting.login_ask, compilation_list: JSON.stringify(compilationList)});
+        console.log(request.session.lastPage);
+        response.json({
+            error: 0,
+            msg: '',
+            login_ask: preferenceSetting.login_ask,
+            compilation_list: JSON.stringify(compilationList),
+            last_page: request.session.lastPage
+        });
     }
 
 }

+ 1 - 0
modules/users/models/user_model.js

@@ -97,6 +97,7 @@ class UserModel extends BaseModel {
             result = await logModel.addLoginLog(userDataFromDb._id, request);
         }
         request.session.sessionUser.id = userDataFromDb._id;
+        request.session.sessionUser.real_name = userDataFromDb.real_name;
 
         return result;
     }

+ 2 - 0
modules/volume_price/models/volume_price_model.js

@@ -30,6 +30,7 @@ class volumePriceModel extends baseModel {
             if(err){
                 callback(1, '', null);
             }else {
+                // console.log(JSON.stringify(datas));
                 callback(0, consts.projectConst.VOLUMEPRICE, datas);
             }
         })
@@ -39,6 +40,7 @@ class volumePriceModel extends baseModel {
         let funs = [];
 
         function saveOne(doc) {
+            // console.log('-----------------------------------------------------------------------------' + JSON.stringify(doc));
             return function (cb) {
                 switch (doc.updateType) {
                     case commonConsts.UT_UPDATE:

+ 6 - 0
modules/volume_price/models/volume_price_schema.js

@@ -20,12 +20,18 @@ let volumePriceSchema = new Schema({
     serialNo: Number,
     // 编号
     code: String,
+    // 量价类型:人工、材料、机械、主材、设备
+    type: String,
+    programID: Number,
     // 名称
     name: String,
     // 单位
     unit: String,
     // 数量
     quantity: Number,
+    marketUnitFee: String,
+    marketTotalFee: String,
+
     // 费用字段
     fees: [subSchema.feesSchema],
     // 是否删除

+ 0 - 12
public/calc_util.js

@@ -1,12 +0,0 @@
-/**
- * Created by Tony on 2017/6/21.
- * Modified by CSL, 2017-08-01 引入多套计算程序、费率同步、人工系数同步、改进基数计算、费字段映射等。
- * added by CSL, 2017-09-01 增加公式解析对象analyzer,用于解析用户修改公式、自定义表达式。
- */
-
-class Calculation {
-
-};
-
-/*
-export default analyzer;*/

+ 0 - 20
public/common_util.js

@@ -1,20 +0,0 @@
-/**
- * Created by CSL on 2017-06-06.
- * public functions.
- */
-
-function deleteEmptyObject(arr) {
-    function isEmptyObject(e) {
-        var t;
-        for (t in e)
-            return !1;
-        return !0
-    };
-
-    for (var i = 0; i < arr.length; i++) {
-        if (isEmptyObject(arr[i])) {
-            arr.splice(i, 1);
-            i = i - 1;
-        };
-    };
-};

+ 0 - 28
public/debug.js

@@ -1,28 +0,0 @@
-/**
- * Created by CSL on 2017-05-19.
- * 用于展示未知对象的内容。如:debug.m(GC.Spread.Sheets.Events);
- */
-
-debug = {
-    m: function (flag, obj) {
-        alert(flag + this.objStr(obj));
-    },
-
-    d: function (flag, obj) {
-        alert(flag + JSON.stringify(obj));
-    },
-
-    objStr: function (obj) {
-        var str = "";
-        var spr = "";
-        for (var x in obj) {
-            if (obj.hasOwnProperty(x)) {
-                if (str == '') {
-                    spr = ''
-                } else { spr = ', ' } ;
-                str += spr + x + ':' + obj[x];
-            }
-        }
-        return str;
-    }
-}

+ 1 - 1
public/fsUtil.js

@@ -25,7 +25,7 @@ module.exports = {
             });
         }
     },
-    wirteObjToFile: function(obj, filePath) {
+    writeObjToFile: function(obj, filePath) {
         if (obj) {
             let arr = [];
             arr.push(JSON.stringify(obj));

File diff suppressed because it is too large
+ 2 - 2
public/web/PerfectLoad.js


+ 20 - 0
public/web/common_util.js

@@ -0,0 +1,20 @@
+/**
+ * Created by CSL on 2017-06-06.
+ * public functions for web.
+ */
+
+// 忽略大小写判断字符串是否和参数指定的字符串相同
+String.prototype.sameText = function (str) {
+    return this.toLowerCase() == str.toLowerCase();
+};
+
+// 忽略大小写判断字符串是否有参数指定的子串
+String.prototype.hasSubStr = function (str) {
+    return this.toLowerCase().indexOf(str.toLowerCase()) > -1;
+};
+
+// 树结点计算时,取费会出现值为NaN的情况,导致往父节点汇总(递归相加)会出现错误。
+function parseFloatPlus(value){
+    let rst = parseFloat(value);
+    return  isNaN(rst) ? 0 : rst;
+};

+ 4 - 1
public/web/rpt_value_define.js

@@ -136,6 +136,7 @@ const JV = {
     PROP_CALCULATION: "CalculationType",
     PROP_H_CALCULATION: "H_CalculationType",
     PROP_V_CALCULATION: "V_CalculationType",
+    PROP_FIT_AREA: "isFitArea",
 
     IDX_LEFT: 0,
     IDX_TOP: 1,
@@ -236,7 +237,7 @@ const JV = {
     SIZE_16K: [7.75, 10.75],
     SIZE_EXECUTIVE: [7.25, 10.5],
 
-    OUTPUT_OFFSET: [2,1,2,3],
+    OUTPUT_OFFSET: [2,2,1,3],
     OFFSET_IDX_LEFT: 0,
     OFFSET_IDX_RIGHT: 1,
     OFFSET_IDX_TOP: 2,
@@ -265,6 +266,8 @@ const JV = {
 
     VERTICAL_ANGLE: "90",
     ANTI_VERTICAL_ANGLE: "-90",
+    VERTICAL_ANGLE_INT: 90,
+    ANTI_VERTICAL_ANGLE_INT: -90,
 
     LAST_DEF: ""
 };

+ 13 - 0
public/web/scMathUtil.js

@@ -98,6 +98,19 @@ let scMathUtil = {
     roundTo: function(num, digit){
         let me = this;
         return me.innerRoundTo(me.binToFloat(me.incMantissa(me.floatToBin(num))), digit);
+    },
+    isNumber : function (obj) {
+        return obj === +obj;
+    },
+    roundToString:function(obj,decimal){
+        let me = this;
+        let value;
+        if(me.isNumber(obj)){
+            value = me.roundTo(obj,-decimal)
+        }else {
+            value = me.roundTo(Number(obj),-decimal);
+        }
+        return value.toFixed(decimal);
     }
 };
 

+ 12 - 2
public/web/sheet/sheet_common.js

@@ -97,6 +97,11 @@ var sheetCommonObj = {
         //sheet.addRows(row, 1);
 
         sheet.clear(0, 0, sheet.getRowCount(), sheet.getColumnCount(), GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
+        if(sheet.getRowCount()<data.length){
+            data.length<30? sheet.setRowCount(30):sheet.setRowCount(data.length);
+        }else if(sheet.getRowCount()==0){
+            sheet.setRowCount(30);
+        }
         for (var col = 0; col < setting.header.length; col++) {
             var hAlign = "left", vAlign = "center";
             if (setting.header[col].hAlign) {
@@ -124,9 +129,14 @@ var sheetCommonObj = {
                 var val = data[row][setting.header[col].dataCode];
                 if(val&&setting.header[col].dataType === "Number"){
                     if(setting.header[col].hasOwnProperty('tofix')){
-                        val =parseFloat(val).toFixed(setting.header[col].tofix);
+                        val =scMathUtil.roundToString(val,setting.header[col].tofix);
+                    }
+                    if(setting.header[col].hasOwnProperty('decimalField')){
+                        var decimal = getDecimal(setting.header[col].decimalField);
+                        val =scMathUtil.roundToString(val,decimal);
+                        sheet.setFormatter(-1, col,getFormatter(decimal), GC.Spread.Sheets.SheetArea.viewport);
                     }else {
-                        val =parseFloat(val).toFixed(2);
+                        val =scMathUtil.roundToString(val,2);
                     }
                 }
                 if(val!=null&&setting.header[col].cellType === "checkBox"){

+ 1 - 1
public/web/tree_sheet/tree_sheet_helper.js

@@ -137,7 +137,7 @@ var TREE_SHEET_HELPER = {
                     cell.value(getFieldText2());
                 }
                 if (colSetting.data.cellType && Object.prototype.toString.apply(colSetting.data.cellType) !== "[object String]") {
-                    cell.cellType(colSetting.data.cellType);
+                    cell.cellType(colSetting.data.cellType(node));
                 }
                 if (colSetting.readOnly) {
                     if (Object.prototype.toString.apply(colSetting.readOnly) === "[object Function]") {

+ 16 - 0
public/web/tree_table/tree_table.js

@@ -198,6 +198,17 @@
  			return treenode;
  		};
 
+		Tree.prototype.findNodeByNid = function (nid) {
+			let treenode = null,
+				callback = function (node) {
+					if(node.data && node.data[node.setting.tree.nid] === nid){
+						treenode = node;
+					}
+				};
+			this.traverseDF.call(this, callback);
+			return treenode;
+		}
+
  		Tree.prototype.findNodeByDomId = function (domId) {
  			var treenode = null,
  				callback = function (node) {
@@ -274,6 +285,11 @@
 					parent.children.splice(parent.childIndex(node), 1);
 					parent.children.splice(parent.childIndex(next), 0, node);
 				};
+				let pre = that.findNodeByNid(node.data.ID) || null;
+				if(pre && parent.childIndex(pre) !== parent.childIndex(node) - 1){
+					parent.children.splice(parent.childIndex(pre), 1);
+					parent.children.splice(parent.childIndex(node), 0, pre);
+				}
 			};
 
  			for (i = 0; i < arrData.length; i++){

+ 5 - 5
server.js

@@ -52,7 +52,8 @@ app.use(bodyParser.json({limit: '3mb'}));
 app.use(session({
     name: 'usersSession',
     secret: 'session users secret',
-    cookie: {maxAge: 1000*60*30},
+    // @todo 上线前修改回正常值 (目前为24小时)
+    cookie: {maxAge: 3600 * 24 * 1000},
     resave: false,
     rolling: true,
     saveUninitialized: true
@@ -66,9 +67,7 @@ app.use(session({
 // 登录状态全局判断
 app.use(function (req, res, next) {
     let url = req.originalUrl;
-    // @todo 上一个页面跳转
-    let referer = '';
-    if (/^\/login/.test(url)) {
+    if (/^\/login/.test(url) || /\.map|\.ico$/.test(url)) {
         // 如果是登录页面则忽略判断数据
         next();
     } else {
@@ -78,9 +77,10 @@ app.use(function (req, res, next) {
             if (!sessionUser) {
                 throw 'session error';
             }
-
             res.locals.sessionUser = sessionUser;
         } catch (error) {
+            // 最后一个页面存入session
+            req.session.lastPage = url;
             return res.redirect('/login');
         }
 

+ 2 - 2
test/tmp_data/bills_grid_setting.js

@@ -405,10 +405,10 @@ var BillsGridSetting ={
                 ]
             },
             "data":{
-                "field":"",
+                "field":"calcBase",
                 "vAlign":1,
                 "hAlign":0,
-                "font":"Arial"
+                "font":"Arial",
             }
         },
         {

+ 3 - 82
test/tmp_data/test_ration_calc/ration_calc_base.js

@@ -3,89 +3,10 @@
  */
 "use strict";
 
-const baseCalc = 0, adjustCalc = 1, budgetCalc = 2, diffCalc = 3,  offerCalc = 4;
-
-const gljType = {
-    // 人工
-    LABOUR: 1,
-    // ==============材料类型=================
-    // 普通材料
-    GENERAL_MATERIAL: 201,
-    // 混凝土
-    CONCRETE: 202,
-    // 砂浆
-    MORTAR: 203,
-    // 配合比
-    MIX_RATIO: 204,
-    // 商品混凝土
-    COMMERCIAL_CONCRETE: 205,
-    // 商品砂浆
-    COMMERCIAL_MORTAR: 206,
-    // ==============材料类型=================
-    // ==============机械类型=================
-    // 普通机械
-    GENERAL_MACHINE: 301,
-    // 机械组成物
-    MACHINE_COMPOSITION: 302,
-    // 机上人工
-    MACHINE_LABOUR: 303,
-    // ==============机械类型=================
-    // 主材
-    MAIN_MATERIAL: 4,
-    // 设备
-    EQUIPMENT: 5
-};
-
-let rationCalcBase = [
-    {
-        'dispName': '定额基价人工费',
-        'calcFun': 'base',
-        'calcType': baseCalc,
-        'gljTypes': [gljType.LABOUR]
-    },{
-        'dispName': '定额基价材料费',
-        'calcFun': 'base',
-        'calcType': baseCalc,
-        'gljTypes': [gljType.GENERAL_MATERIAL, gljType.CONCRETE, gljType.MORTAR, gljType.MIX_RATIO, gljType.COMMERCIAL_CONCRETE, gljType.COMMERCIAL_MORTAR]
-    },{
-        'dispName': '定额基价机械费',
-        'calcFun': 'base',
-        'calcType': baseCalc,
-        'gljTypes': [gljType.GENERAL_MACHINE]
-    },{
-        'dispName': '定额基价机上人工费',
-        'calcFun': 'base',
-        'calcType': baseCalc,
-        'gljTypes': [gljType.MACHINE_LABOUR]
-    },{
-        'dispName': '人工费价差',
-        'calcFun': 'diff',
-        'calcType': diffCalc,
-        'gljTypes': [gljType.LABOUR]
-    },{
-        'dispName': '材料费价差',
-        'calcFun': 'diff',
-        'calcType': diffCalc,
-        'gljTypes': [gljType.GENERAL_MATERIAL, gljType.CONCRETE, gljType.MORTAR, gljType.MIX_RATIO, gljType.COMMERCIAL_CONCRETE, gljType.COMMERCIAL_MORTAR]
-    },{
-        'dispName': '机械费价差',
-        'calcFun': 'diff',
-        'calcType': diffCalc,
-        'gljTypes': [gljType.GENERAL_MACHINE]
-    },{
-        'dispName': '主材费',
-        'calcFun': 'budget',
-        'calcType': budgetCalc,
-        'gljTypes': [gljType.MAIN_MATERIAL]
-    },{
-        'dispName': '设备费',
-        'calcFun': 'budget',
-        'calcType': budgetCalc,
-        'gljTypes': [gljType.EQUIPMENT]
-    }
-];
+// 这里的代码分别移到main_costs.js中和calc_program.js中,目的是相关内容集中放置,减少重复声明,减少单元引用。
 
 // 该方法暂时不用,已使用中文预编译的方式代替,但可靠性有待验证,若有问题再用回来。
+/*
 function getRationCalcBase(dispName){
     for (let base of rationCalcBase) {
        if (base.dispName == dispName) {
@@ -93,4 +14,4 @@ function getRationCalcBase(dispName){
        };
     }
     return null;
-};
+};*/

+ 115 - 0
test/unit/reports/test_cover_01.js

@@ -0,0 +1,115 @@
+/**
+ * Created by Tony on 2017/11/21.
+ */
+
+let test = require('tape');
+import JpcEx from "../../../modules/reports/rpt_component/jpc_ex";
+import JV from "../../../modules/reports/rpt_component/jpc_value_define";
+let mongoose = require("mongoose");
+let fileUtils = require("../../../modules/common/fileUtils");
+let path = require('path');
+let dbm = require("../../../config/db/db_manager");
+let rpt_cfg = require('./rpt_cfg');
+dbm.connect();
+let consts = require('../../../modules/main/models/project_consts');
+let projectConsts = consts.projectConst;
+fileUtils.getGlobbedFiles('../../../modules/complementary_glj_lib/models/*.js').forEach(function(modelPath) {
+    require(path.resolve(modelPath));
+});
+
+fileUtils.getGlobbedFiles('../../../modules/ration_glj/models/*.js').forEach(function(modelPath) {
+    require(path.resolve(modelPath));
+});
+
+//引入报表模块
+fileUtils.getGlobbedFiles('../../../modules/reports/models/*.js').forEach(function(modelPath) {
+    require(path.resolve(modelPath));
+})
+
+//暂时引入其它模块的model
+require('../../../modules/fee_rates/models/fee_rates');
+// 引入人工系数模块
+require('../../../modules/main/models/labour_coe_model');
+require('../../../modules/main/models/calc_program_model');
+
+let fsUtil = require("../../../public/fsUtil");
+
+let prjMdl = require('../../../modules/pm/models/project_model');
+let projectDataMdl = require('../../../modules/main/models/project');
+let demoPrjId = - 1;
+let demoRptId = 223, pagesize = "A4";
+
+let userId_Leng = 1142; //小冷User Id
+demoPrjId = 1220; //QA:
+/*/
+ let userId_Dft = userId_Leng;
+ /*/
+let userId_Dft = 76075;
+//*/
+
+let rptTplFacade = require("../../../modules/reports/facade/rpt_template_facade");
+let rptTplDataFacade = require("../../../modules/reports/facade/rpt_tpl_data_facade");
+
+import rptDataExtractor from "../../../modules/reports/util/rpt_construct_data_util";
+
+let fs = require('fs');
+//设置Date Format函数
+fs.readFile(__dirname.slice(0, __dirname.length - 18) + '/public/web/date_util.js', 'utf8', 'r', function (err, data) {
+    eval(data);
+});
+
+//*
+test('测试 - 打开模板: 封-1 ', function (t) {
+    rptTplFacade.getRptTemplate(demoRptId).then(function(rptTpl) {
+        let rptDataUtil = new rptDataExtractor();
+        rptDataUtil.initialize(rptTpl._doc);
+        let filter = rptDataUtil.getDataRequestFilter();
+        // console.log(filter);
+        //正常应该根据报表模板定义的数据类型来请求数据
+        rptTplDataFacade.prepareProjectData(userId_Dft, demoPrjId, filter, function (err, msg, rawDataObj) {
+            if (!err) {
+                try {
+                    // fsUtil.writeObjToFile(rawDataObj, "D:/GitHome/ConstructionCost/tmp/rptTplRawDataObject.js");
+                    let tplData = rptDataUtil.assembleData(rawDataObj);
+                    //build the report
+                    let printCom = JpcEx.createNew();
+                    rptTpl[JV.NODE_MAIN_INFO][JV.NODE_PAGE_INFO][JV.PROP_PAGE_SIZE] = pagesize;
+                    let defProperties = rpt_cfg;
+                    let dftOption = JV.PAGING_OPTION_NORMAL;
+                    printCom.initialize(rptTpl);
+                    printCom.analyzeData(rptTpl, tplData, defProperties, dftOption);
+                    let maxPages = printCom.totalPages;
+                    let pageRst = printCom.outputAsSimpleJSONPageArray(rptTpl, tplData, 1, maxPages, defProperties);
+                    if (pageRst) {
+                        // fsUtil.writeObjToFile(pageRst, "D:/GitHome/ConstructionCost/tmp/testBuiltPageResult.js");
+                    } else {
+                        console.log("oh! no pages were created!");
+                    }
+                } catch (ex) {
+                    console.log(ex);
+                    t.pass('pass with exception!');
+                    t.end();
+                }
+
+                t.pass('pass succeeded!');
+                t.end();
+            } else {
+                console.log(msg);
+                t.pass('pass with error!');
+                t.end();
+            }
+        })
+    });
+});
+//*/
+
+test('close the connection', function (t) {
+    setTimeout(function () {
+        mongoose.disconnect();
+        t.pass('closing db connection');
+        t.end();
+    }, 8000);
+    // mongoose.disconnect();
+    // t.pass('closing db connection');
+    // t.end();
+});

+ 6 - 6
test/unit/reports/test_tpl_09_1.js

@@ -62,7 +62,7 @@ fs.readFile(__dirname.slice(0, __dirname.length - 18) + '/public/web/date_util.j
 test('测试 - 获取project数据: ', function (t) {
     projectDataMdl.getData(demoPrjId, function (err, message, result) {
         if (!err) {
-            fsUtil.wirteObjToFile(result, "D:/GitHome/ConstructionCost/tmp/ProjectDataFullObject.js");
+            fsUtil.writeObjToFile(result, "D:/GitHome/ConstructionCost/tmp/ProjectDataFullObject.js");
             t.pass('pass succeeded!');
             t.end();
         } else {
@@ -91,8 +91,8 @@ test('测试 - 获取project部分数据: ', function (t) {
                     //     newData.push(JSON.stringify(item));
                     // }
                     // fsUtil.writeArrayToFile(newData, "D:/GitHome/ConstructionCost/tmp/getProjectData_partial.js");
-                    // fsUtil.wirteObjToFile(prjObj, "D:/GitHome/ConstructionCost/tmp/getProjectObjectNew.js");
-                    fsUtil.wirteObjToFile(results, "D:/GitHome/ConstructionCost/tmp/getProjectData_partialNew.js");
+                    // fsUtil.writeObjToFile(prjObj, "D:/GitHome/ConstructionCost/tmp/getProjectObjectNew.js");
+                    fsUtil.writeObjToFile(results, "D:/GitHome/ConstructionCost/tmp/getProjectData_partialNew.js");
                     t.pass('pass succeeded!');
                     t.end();
                 } else {
@@ -121,7 +121,7 @@ test('测试 - 测试模板啦: ', function (t) {
             if (!err) {
                 try {
                     let tplData = rptDataUtil.assembleData(rawDataObj);
-                    // fsUtil.wirteObjToFile(rawDataObj, "D:/GitHome/ConstructionCost/tmp/rptTplRawDataObject.js");
+                    // fsUtil.writeObjToFile(rawDataObj, "D:/GitHome/ConstructionCost/tmp/rptTplRawDataObject.js");
                     //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;
@@ -134,7 +134,7 @@ test('测试 - 测试模板啦: ', function (t) {
                     if (pageRst) {
                         // fsUtil.wirteObjToFile(pageRst, "D:/GitHome/ConstructionCost/tmp/testBuiltPageResult.js");
                     } else {
-                        console.log("oh! no pages were created!")
+                        console.log("oh! no pages were created!");
                     }
                 } catch (ex) {
                     console.log(ex);
@@ -180,7 +180,7 @@ test('测试 - 显示保存小数位数问题: ', function (t) {
     rpt_decimal_mdl.find({}).then(function (rst) {
         //console.log(rst);
         if (rst.length > 0) {
-            fsUtil.wirteObjToFile(rst, "D:/GitHome/ConstructionCost/tmp/testDecimalResult.js");
+            fsUtil.writeObjToFile(rst, "D:/GitHome/ConstructionCost/tmp/testDecimalResult.js");
         }
         t.pass('pass get decimal ok!');
         t.end();

+ 3 - 0
tmp/forDeployment.js

@@ -0,0 +1,3 @@
+/**
+ * Created by Tony on 2017/11/28.
+ */

+ 13 - 0
web/building_saas/css/main.css

@@ -311,3 +311,16 @@ body {
 .navbar-crumb span{
   max-width: 200px
 }
+.dropdown-item{
+  color:#007bff
+}
+.dropdown-item.disabled, .dropdown-item:disabled{
+  pointer-events:none
+}
+.text-green{
+    color: #172a30
+}
+label.title{
+    display: inline-block;
+    width: 100px;
+}

+ 4 - 1
web/building_saas/glj/html/glj_index.html

@@ -12,7 +12,7 @@
 </div>
 <div class="container-fluid">
     <div class="row">
-        <div class="main-content col-lg-12 p-0">
+        <div class="col-lg-12 p-0" id="glj-main">
             <div class="top-content">
                 <div class="main-data-top" id="project-glj">
                     <p style="text-align: center; margin-top: 30px;">正在加载数据</p>
@@ -29,6 +29,9 @@
                     <li class="nav-item">
                         <a class="nav-link" data-toggle="tab" data-name="machine" href="#jx" role="tab">机械单价</a>
                     </li>
+                    <li class="nav-item ml-auto mr-4">
+                        <a class="nav-link text-muted position-absolute resize" data-toggle="tooltip" data-placement="bottom" title="按住,上下拖动调整高度"><i class="fa fa-arrows-v"></i></a>
+                    </li>
                 </ul>
                 <!-- Tab panes -->
                 <div class="tab-content">

+ 0 - 1
web/building_saas/glj/js/composition_spread.js

@@ -133,7 +133,6 @@ CompositionSpread.prototype.getRatioData = function(projectGLJid) {
         success: function(response) {
             if (response.err === 0) {
                 response.data = JSON.parse(response.data);
-                console.log(response.data);
                 // 设置数据
                 self.sheetObj.setData(response.data);
                 self.specialColumn(response.data);

+ 43 - 17
web/building_saas/glj/js/project_glj.js

@@ -22,6 +22,7 @@ let mixRatioConnectData = [];
 let mixRatioMap={};
 // 单价文件相关
 let usedUnitPriceInfo = {};
+let usedTenderList = [];
 let otherFileData = {};
 let currentTag = '';
 let isChanging = false;
@@ -31,6 +32,10 @@ $(document).ready(function () {
         init();
     });
 
+    slideResize($("#glj-main"), function() {
+        projectGLJSpread.sheetObj.spread.refresh();
+    });
+
     // 单价文件切换弹框
     $('#change-dj').on('shown.bs.modal', function () {
         // 获取当前建设项数据
@@ -162,12 +167,15 @@ $(document).ready(function () {
             // 从其他项目中复制
             changeUnitPriceId = $("#other-file").val();
         }
+        $('#change-dj').modal("hide");
+        $.bootstrapLoading.start();
         $.ajax({
             url: '/glj/change-file',
             type: 'post',
             data: {project_id: scUrlUtil.GetQueryString('project'), change_id: changeUnitPriceId, type: type},
             error: function() {
                 isChanging = false;
+                $.bootstrapLoading.end();
             },
             beforeSend: function() {
                 isChanging = true;
@@ -177,9 +185,16 @@ $(document).ready(function () {
                 if (response.err === 1) {
                     let msg = response.msg !== undefined ? response.msg : '未知错误';
                     alert(msg);
+                    $.bootstrapLoading.end();
                     return false;
                 }
-                $('#change-dj').modal("hide");
+                projectObj.project.projectGLJ.loadData(function () {
+                    let projectGLJ = projectObj.project.projectGLJ;
+                    projectGLJ.loadCacheData();
+                    unitPriceFileInit();
+                    gljOprObj.refreshView();
+                    $.bootstrapLoading.end();
+                });
             }
         });
     });
@@ -193,6 +208,14 @@ $(document).ready(function () {
             $("#notify").slideUp('fast');
         });
     });
+
+    $('#pop-dj').popover({
+            placement:"bottom",
+            html:true,
+            trigger:"hover | focus",
+            content: getUsedTenderInfo
+        }
+    );
 });
 
 /**
@@ -215,14 +238,10 @@ function init() {
                 data.constData.ownCompositionTypes : canNotChangeTypeId;
             GLJTypeConst = data.constData.GLJTypeConst !== undefined ? JSON.parse(data.constData.GLJTypeConst) : GLJTypeConst;
 
-            let usedTenderList = data.usedTenderList !== undefined ? data.usedTenderList : [];
-            usedUnitPriceInfo = data.constData.usedUnitPriceInfo !== undefined ?
-                data.constData.usedUnitPriceInfo : {};
-
             // 连接socket服务器
             socketInit();
 
-            unitPriceFileInit(usedUnitPriceInfo.name, usedTenderList);
+            unitPriceFileInit();
 
             setTimeout(spreadInit, 1);
         } else {
@@ -270,6 +289,10 @@ function spreadInit() {
             projectGLJSheet.filterData('unit_price.type', []);
         }
     });
+
+    loadSize("glj-main", function() {
+        projectGLJSpread.sheetObj.spread.refresh();
+    });
 }
 
 /**
@@ -279,17 +302,19 @@ function spreadInit() {
  * @param {Array} data
  * @return {void}
  */
-function unitPriceFileInit(name, data) {
-    $("#used-name").text(name);
-    let usedCount = data.length <= 0 ? 1 : data.length;
+function unitPriceFileInit() {
+    let projectGLJ = projectObj.project.projectGLJ;
+    let data = projectGLJ.datas;
+     usedTenderList = data.usedTenderList !== undefined ? data.usedTenderList : [];
+    usedUnitPriceInfo = data.constData.usedUnitPriceInfo !== undefined ?
+        data.constData.usedUnitPriceInfo : {};
+    $("#used-name").text(usedUnitPriceInfo.name);
+    let usedCount = usedTenderList.length <= 0 ? 1 : usedTenderList.length;
     $("#used-count").text(usedCount);
-    $('#pop-dj').popover({
-            placement:"bottom",
-            html:true,
-            trigger:"hover | focus",
-            content: data.join('<br>')
-        }
-    );
+}
+
+function getUsedTenderInfo() {
+   return usedTenderList.join("<br>");
 }
 
 /**
@@ -360,4 +385,5 @@ function filterProjectGLJ(jsonData) {
         jsonData = tmpData;
     }
     return jsonData;
-}
+}
+

+ 6 - 4
web/building_saas/glj/js/project_glj_spread.js

@@ -48,9 +48,9 @@ ProjectGLJSpread.prototype.init = function () {
         {name: 'ID', field: 'id', visible: false},
         {name: '类型', field: 'unit_price.type', visible: false},
         {name: '总消耗量', field: 'quantity', visible: true,width:100},
-        {name: '基价单价', field: "unit_price.base_price", visible: true,width:70},
-        {name: '调整价', field: 'adjust_price', visible: true,width:70},
-        {name: '市场价', field: "unit_price.market_price", visible: true, validator: 'number',width:70},
+        {name: '定额价', field: "unit_price.base_price", visible: true,width:70},
+        {name: '调整价', field: 'adjust_price', visible: true,width:70},
+        {name: '市场价', field: "unit_price.market_price", visible: true, validator: 'number',width:70},
         {
             name: '是否暂估',
             field: 'is_evaluate',
@@ -246,6 +246,7 @@ ProjectGLJSpread.prototype.specialColumn = function (sourceData) {
     // 获取列号
     let isEvaluateColumn = this.sheetObj.getFieldColumn('is_evaluate');
     let marketPriceColumn = this.sheetObj.getFieldColumn('unit_price.market_price');
+    let adjustPriceColumn = this.sheetObj.getFieldColumn("adjust_price");
     let connectCodeColumn = this.sheetObj.getFieldColumn('connect_code');
     let consumptionColumn = this.sheetObj.getFieldColumn('consumption');
     let supplyColumn = this.sheetObj.getFieldColumn('supply');
@@ -306,7 +307,8 @@ ProjectGLJSpread.prototype.specialColumn = function (sourceData) {
             activeSheet.setValue(rowCounter, connectCodeColumn, connectCodeString);
             activeSheet.setValue(rowCounter, consumptionColumn, consumptionString);
         }
-
+        data.adjust_price = projectObj.project.projectGLJ.getAdjustPrice(data);
+        activeSheet.setValue(rowCounter,adjustPriceColumn,data.adjust_price);
         rowCounter++;
     }
 };

BIN
web/building_saas/img/FirstPageSimple.cur


BIN
web/building_saas/img/LastPageSimple.cur


BIN
web/building_saas/img/NextPageSimple.cur


BIN
web/building_saas/img/PreviousPageSimple.cur


BIN
web/building_saas/img/baobiao.png


+ 46 - 13
web/building_saas/js/global.js

@@ -17,17 +17,18 @@ function autoFlashHeight(){
     $(".poj-list").height($(window).height()-headerHeight-toolsbarHeight);
     $(".form-view").height($(window).height()-headerHeight-ftoolsbarHeight);
     $(".form-list").height($(window).height()-headerHeight-50 );
+
 };
 $(window).resize(autoFlashHeight);
 /*全局自适应高度结束*/
-$(function(){
-/*侧滑*/
-$(".open-sidebar").click(function(){
-    $(".slide-sidebar").animate({width:"800"}).addClass("open");
-});
-$("body").click(function(event){
+$(function () {
+    /*侧滑*/
+    $(".open-sidebar").click(function () {
+        $(".slide-sidebar").animate({width: "800"}).addClass("open");
+    });
+    $("body").click(function (event) {
         var e = event || window.event; //浏览器兼容性
-        if(!$(event.target).is('a')) {
+        if (!$(event.target).is('a')) {
             var elem = event.target || e.srcElement;
             while (elem) { //循环判断至跟节点,防止点击的是div子元素
                 if (elem.className == "open-sidebar" || elem.className == 'slide-sidebar open') {
@@ -35,13 +36,45 @@ $("body").click(function(event){
                 }
                 elem = elem.parentNode;
             }
-            $(".slide-sidebar").animate({width:"0"}).removeClass("open")// 关闭处理
+            $(".slide-sidebar").animate({width: "0"}).removeClass("open")// 关闭处理
         }
 
     });
-/*侧滑*/
-/*工具提示*/
-$(function () {
-  $('[data-toggle="tooltip"]').tooltip()
-});
+    /*侧滑*/
+    /*工具提示*/
+    $(function () {
+        $('[data-toggle="tooltip"]').tooltip();
+    });
+
 });
+
+/**
+ * 设置本地缓存
+ *
+ * @param {String} key
+ * @param {String|Number} value
+ * @return {void}
+ */
+function setLocalCache(key, value) {
+    const storage = window.localStorage;
+    if (!storage || key === '' || value === '') {
+        return;
+    }
+
+    storage.setItem(key, value);
+}
+
+/**
+ * 获取本地缓存
+ *
+ * @param {String} key
+ * @return {String}
+ */
+function getLocalCache(key) {
+    const storage = window.localStorage;
+    if (!storage || key === '') {
+        return null;
+    }
+
+    return storage.getItem(key);
+}

+ 189 - 75
web/building_saas/main/html/main.html

@@ -5,7 +5,7 @@
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     <meta http-equiv="x-ua-compatible" content="ie=edge">
-    <title>造价书-Smartcost</title>
+    <title><%= projectData.name !== undefined ? projectData.name : '造价书' %>-纵横云造价</title>
     <!-- inject:css -->
     <link rel="stylesheet" href="/lib/bootstrap/css/bootstrap.min.css">
     <link rel="stylesheet" href="/web/building_saas/css/main.css">
@@ -13,6 +13,7 @@
     <!--zTree-->
     <link rel="stylesheet" href="/lib/ztree/css/zTreeStyle.css" type="text/css">
     <!--SpreadJs-->
+    <link rel="stylesheet" href="/lib/jquery-ui/jquery-ui.css" type="text/css">
     <link rel="stylesheet" href="/lib/spreadjs/sheets/css/gc.spread.sheets.excel2013lightGray.10.0.1.css" type="text/css">
     <link rel="stylesheet" href="/lib/spreadjs/views/gc.spread.views.dataview.10.0.0.css">
     <!-- jquery.contextmenu -->
@@ -36,42 +37,6 @@
             </div>
         </div>
         <%include ../../../common/html/header.html %>
-        <nav class="navbar navbar-expand-lg justify-content-between navbar-light p-0">
-            <ul class="nav navbar-nav px-1">
-                <li class="nav-item">
-                    <a class="nav-link" href="#" aria-expanded="false" data-toggle="modal" data-target="#poj-set"><i class="fa fa-cube"></i> 项目属性</a>
-                </li>
-                <li class="nav-item">
-                    <a class="nav-link" href="#" aria-expanded="false" data-toggle="modal" data-target="#opts-set"><i class="fa fa-sliders"></i> 选项</a>
-                    <!--<a class="nav-link" href="#" aria-haspopup="true" aria-expanded="false"><i class="fa fa-sliders"></i> 选项</a>-->
-                </li>
-                <li class="nav-item dropdown">
-                    <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-wrench"></i> 工具</a>
-                    <div class="dropdown-menu">
-                        <a class="dropdown-item" href="#">定额库编辑器</a>
-                        <a class="dropdown-item" href="/complementaryGlj">工料机库编辑器</a>
-                    </div>
-                </li>
-                <li class="nav-item dropdown">
-                    <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-question-circle-o"></i> 帮助</a>
-                    <div class="dropdown-menu">
-                        <a class="dropdown-item" href="#">帮助</a>
-                        <a class="dropdown-item" href="#">升级说明</a>
-                        <a class="dropdown-item" href="#">重庆市2008定额说明</a>
-                        <a class="dropdown-item" href="#">纵横官网</a>
-                        <a class="dropdown-item" href="#">动画教程</a>
-                        <a class="dropdown-item" href="#">联系客服</a>
-                        <a class="dropdown-item" href="#">关于</a>
-                    </div>
-                </li>
-                <li class="nav-item">
-                    <a class="nav-link" href="#" aria-haspopup="true" aria-expanded="false"><i class="fa fa-history"></i> 历史记录</a>
-                </li>
-            </ul>
-            <form class="form-inline">
-                <input class="form-control form-control-sm mr-1" type="text" placeholder="告诉我你想做什么">
-            </form>
-        </nav>
     </div>
     <div class="main">
         <div class="main-nav">
@@ -89,15 +54,15 @@
               <!--造价书-->
               <div class="toolsbar px-1 d-flex justify-content-between">
                   <div class="tools-btn btn-group align-top">
-                    <a href="" class="btn btn-sm"><i class="fa fa-files-o" aria-hidden="true"></i> 复制</a>
-                    <a href="" class="btn btn-sm"><i class="fa fa-scissors" aria-hidden="true"></i> 接切</a>
-                    <a href="" class="btn btn-sm"><i class="fa fa-clipboard" aria-hidden="true"></i> 粘贴</a>
-                    <a href="javascript:void(0)" class="btn btn-sm" id="insert"><i class="fa fa-sign-in" aria-hidden="true"></i> 插入</a>
-                    <a href="javascript:void(0)" class="btn btn-sm" id="delete"><i class="fa fa-remove" aria-hidden="true"></i> 删除</a>
-                    <a href="javascript:void(0)" class="btn btn-sm" id="upLevel"><i class="fa fa-arrow-left" aria-hidden="true"></i> 升级</a>
-                    <a href="javascript:void(0)" class="btn btn-sm" id="downLevel"><i class="fa fa-arrow-right" aria-hidden="true"></i> 降级</a>
-                    <a href="javascript:void(0)" class="btn btn-sm" id="downMove"><i class="fa fa-arrow-down" aria-hidden="true"></i> 下移</a>
-                    <a href="javascript:void(0)" class="btn btn-sm" id="upMove"><i class="fa fa-arrow-up" aria-hidden="true"></i> 上移</a>
+                    <a href="" class="btn btn-sm" title="复制"><i class="fa fa-files-o" aria-hidden="true"></i></a>
+                    <a href="" class="btn btn-sm" title="剪切"><i class="fa fa-scissors" aria-hidden="true"></i></a>
+                    <a href="" class="btn btn-sm" title="粘贴"><i class="fa fa-clipboard" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" class="btn btn-sm" id="insert" title="插入"><i class="fa fa-sign-in" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" class="btn btn-sm" id="delete" title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" class="btn btn-sm" id="upLevel" title="升级"><i class="fa fa-arrow-left" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" class="btn btn-sm" id="downLevel" title="降级"><i class="fa fa-arrow-right" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" class="btn btn-sm" id="downMove" title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" class="btn btn-sm" id="upMove" title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
                   </div>
                   <div class="tools-btn">
                       <a href="javacript:void(0);" data-toggle="modal" data-target="#column" class="btn btn-sm"><i class="fa fa-table" aria-hidden="true"></i> 列设置</a>
@@ -115,7 +80,7 @@
               </div>
               <div class="container-fluid">
                   <div class="row">
-                      <div class="main-content col-lg-12 p-0">
+                      <div class="main-content col-lg-12 p-0" id="main">
                           <div class="top-content">
                               <div class="main-data-top" id="billsSpread"></div>
                           </div>
@@ -142,6 +107,9 @@
                                   <li class="nav-item">
                                       <a class="nav-link" id="linkTZJNR" data-toggle="tab" href="#subSpread" role="tab">特征及内容</a>
                                   </li>
+                                  <li class="nav-item ml-auto mr-4">
+                                      <a class="nav-link text-muted position-absolute resize" data-toggle="tooltip" data-placement="bottom" title="按住,上下拖动调整高度"><i class="fa fa-arrows-v"></i></a>
+                                  </li>
                               </ul>
                               <!-- Tab panes -->
                               <div class="tab-content" id="tabCon">
@@ -152,9 +120,67 @@
                                           <textarea class="form-control" rows="8" readonly=""></textarea>
                                       </div>
                                       <div id="tzjnrCon" class="main-data-bottom">
-                                          <div class="main-data-bottom ovf-hidden" style="width: 40%; float: left;" id="jobSpread">
+                                          <div class="main-data-bottom ovf-hidden" style="width: 33%; float: left;" id="jobSpread">
+                                          </div>
+                                          <div class="main-data-bottom ovf-hidden" style="width: 33%; float: left;" id="itemSpread">
                                           </div>
-                                          <div class="main-data-bottom ovf-hidden" style="width: 60%; float: left;" id="itemSpread">
+                                          <div id="add-rule" style="width: 33%;float: left;background: #EFEFEF; height: 100%;display: none;">
+                                              <p style="text-align: center">添加规则</p>
+                                              <p>
+                                                  <label class="title">添加位置:</label>
+                                                  <select name="" id="add-position">
+                                                      <option value="">添加到项目特征列</option>
+                                                      <option value="">添加到清单名称列</option>
+                                                      <option value="">添加到工作内容列</option>
+                                                      <option value="">分别添加到对应列</option>
+                                                  </select>
+                                              </p>
+                                              <p>
+                                                  <label class="title">添加内容:</label>
+                                                  <select name="" id="add-content">
+                                                      <option value="">无</option>
+                                                      <option value="">项目特征+工作内容</option>
+                                                      <option value="">工作内容+项目特征</option>
+                                                      <option value="">项目特征</option>
+                                                      <option value="">工作内容</option>
+                                                      <option value="">定额子目</option>
+                                                  </select>
+                                              </p>
+                                              <p>
+                                                  <label class="title">显示格式:</label>
+                                                  <select name="" id="format">
+                                                      <option value="">换行分隔</option>
+                                                      <option value="">逗号分隔</option>
+                                                      <option value="">括号分隔</option>
+                                                  </select>
+                                              </p>
+                                              <p>
+                                                  <label class="title">特征生成方式:</label>
+                                                  <select name="" id="">
+                                                      <option value="">特征值</option>
+                                                      <option value="">特征:特征值</option>
+                                                  </select>
+                                              </p>
+                                              <p>
+                                                  <label class="title">子目生成方式:</label>
+                                                  <select name="" id="">
+                                                      <option value="">编号+定额名称</option>
+                                                      <option value="">序号+定额名称</option>
+                                                  </select>
+                                              </p>
+                                              <p>
+                                                  <label class="title">序号格式:</label>
+                                                  <select name="" id="">
+                                                      <option value="">无</option>
+                                                      <option value="">1.</option>
+                                                      <option value="">a.</option>
+                                                      <option value="">A.</option>
+                                                  </select>
+                                              </p>
+                                              <p style="text-align: center">
+                                                  <button class="btn btn-primary btn-sm" type="button">应用到选中清单</button>
+                                                  <button class="btn btn-primary btn-sm" type="button">应用到所有清单</button>
+                                              </p>
                                           </div>
                                       </div>
                                   </div>
@@ -260,26 +286,26 @@
                     <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-1" role="tab">基本信息</a></li>
-                                <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-2" 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" 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-3" role="tab">指标信息</a></li>
                                 <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-4" 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>
+                                <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-billsQuanDecimal" id="tab_poj-settings-bqDecimal" role="tab">清单工程精度</a></li>
                                 <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-decimal" role="tab" id="tab_poj-settings-decimal">小数位数</a></li>
                                 <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-6" role="tab" id="tab_poj-settings-6">人工单价调整</a></li>
+                                <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#display-setting" role="tab" id="tab_display_setting">呈现选项</a></li>
                             </ul>
                         </div>
                         <div class="col-9">
                             <div class="tab-content">
                                 <!--基本信息-->
-                                <div class="tab-pane fade show active" id="poj-settings-1" role="tabpanel">
-                                    <div class="modal-auto-height">
-                                        基本信息
+                                <div class="tab-pane fade show active" 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-2" role="tabpanel">
-                                    <div class="modal-auto-height">
+                                <div class="tab-pane fade" id="poj-settings-projFeature" role="tabpanel">
+                                    <div class="modal-auto-height" style="overflow: hidden" id="projFeatureSpread">
                                         工程特征
                                     </div>
                                 </div>
@@ -302,13 +328,13 @@
                                             </div>
                                             <div class="form-check">
                                                 <label class="form-check-label">
-                                                    <input class="form-check-input" name="calcFlag" id="rationPrice" value="1" type="radio">
+                                                    <input class="form-check-input" name="calcFlag" id="rationPriceConverse" value="1" type="radio">
                                                     子目单价取费(反算):清单综合合价=清单综合单价*清单工程量
                                                 </label>
                                             </div>
                                             <div class="form-check">
                                                 <label class="form-check-label">
-                                                    <input class="form-check-input" name="calcFlag" id="rationPriceConverse" value="2" type="radio">
+                                                    <input class="form-check-input" name="calcFlag" id="rationPrice" value="2" type="radio">
                                                     子目单价取费(正算):清单综合合价=∑子目综合合价
                                                 </label>
                                             </div>
@@ -385,7 +411,7 @@
                                             <div class="row m-0">
                                                 <div class="col-sm-3">
                                                     <div class="input-group input-group-sm mb-2">
-                                                        <div class="input-group-addon">工程量</div>
+                                                        <div class="input-group-addon">消耗量</div>
                                                         <input type="number" name="glj-quantity" class="form-control" value="2" step="1" max="6" min="0">
                                                     </div>
                                                 </div>
@@ -416,6 +442,25 @@
                                     <div style="height:8px;"></div>
                                     <div class="modal-auto-height" id="labourCoeSpread"></div>
                                 </div>
+                                <!--呈现选项-->
+                                <div class="tab-pane fade" id="display-setting" role="tabpanel">
+                                    <div class="modal-auto-height" style="overflow: hidden">
+                                        <fieldset class="form-group">
+                                            <div class="form-check">
+                                                <label class="form-check-label">
+                                                    <input class="form-check-input" id="autoHeight" value="1" type="checkbox">
+                                                    造价书表格自动调整行高
+                                                </label>
+                                            </div>
+                                            <div class="form-check">
+                                                <label class="form-check-label">
+                                                    <input class="form-check-input" id="disPlayMainMaterial" value="1" type="checkbox">
+                                                    定额下显示主材、设备
+                                                </label>
+                                            </div>
+                                        </fieldset>
+                                    </div>
+                                </div>
                             </div>
                         </div>
                     </div>
@@ -553,6 +598,37 @@
             </div>
         </div>
     </div>
+    <!--工料机类型选择-->
+    <div class="modal fade" id="glj_class_div" data-backdrop="static">
+        <div class="modal-dialog modal-m" role="document" id="class_con">
+            <div class="modal-content" >
+                <div class="modal-header">
+                    <h5 class="modal-title">请选择分类</h5>
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                        <span aria-hidden="true">&times;</span>
+                    </button>
+                    <input type="hidden" name="" id="selected_class">
+                </div>
+                <div class="modal-body">
+                    <div class="row">
+                        <div class="col-12">
+                            <div  class="modal-auto-height" id="classTreeDiv" style="overflow: hidden">
+                                <!--<div class="print-list">-->
+                                <div style="width: 100%; height: 100%; overflow: auto">
+                                    <ul id="classTree" class="ztree"></ul>
+                                </div>
+                                <!--</div>-->
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" id="classCacnel" class="btn btn-secondary" data-dismiss="modal">取消</button>
+                    <button type="button" href="javascript:void(0);" id="class_selected_conf" class="btn btn-primary" disabled >确定</button>
+                </div>
+            </div>
+        </div>
+    </div>
     <!--费率选择窗口-->
     <div class="modal fade" id="fee_rate_tree" data-backdrop="static">
         <div class="modal-dialog modal-feeRate" role="document" id="fee_rate_dialog">
@@ -566,17 +642,48 @@
                 <div class="modal-body">
                     <input type="hidden" id="edit_from">
                     <div class="row">
-                        <div class="modal-auto-height col-12" style="overflow: hidden" id="fee_rate_sheet">
+                        <div class="modal-auto-height col-12" style="overflow: hidden" id="fee_rate_sheet"></div>
+                    </div>
+                    <div class="modal-footer">
+                        <button type="button" id="frCacnel" class="btn btn-secondary" data-dismiss="modal">取消</button>
+                        <a href="javascript:void(0);" id="fee_selected_conf" class="btn btn-primary">确定</a>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <!--弹出 清单 计算基数-->
+    <div class="modal fade" id="qd-jsjs" data-backdrop="static">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">计算基础选择</h5>
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                        <span aria-hidden="true">&times;</span>
+                    </button>
+                </div>
+                <div class="modal-body">
+                    <div class="form-group">
+                        <input class="form-control" id="calcBaseExp" value="">
+                        <p class="form-text">
+                            <button class="btn btn-secondary btn-sm" id="addOpr">+</button>
+                            <button class="btn btn-secondary btn-sm" id="subOpr">-</button>
+                            <button class="btn btn-secondary btn-sm" id="mulOpr">*</button>
+                            <button class="btn btn-secondary btn-sm" id="divOpr">/</button>
+                            <button class="btn btn-secondary btn-sm" id="leftOpr">(</button>
+                            <button class="btn btn-secondary btn-sm" id="rightOpr">)</button>
+                        </p>
+                    </div>
+                    <div class=" modal-auto-height" style="overflow: hidden" id="billsBaseSpread">
                     </div>
                 </div>
                 <div class="modal-footer">
-                    <button type="button" id="frCacnel" class="btn btn-secondary" data-dismiss="modal">取消</button>
-                    <a href="javascript:void(0);" id="fee_selected_conf" class="btn btn-primary">确定</a>
+                    <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                    <a href="javascript:void(0);" class="btn btn-primary" id="calcBaseConf">确定</a>
                 </div>
             </div>
         </div>
     </div>
-
         <!-- JS. -->
         <script type="text/javascript" src="/lib/spreadjs/sheets/gc.spread.sheets.all.10.0.1.min.js"></script>
 
@@ -591,6 +698,9 @@
 
 
         <!-- inject:js -->
+        <!--<script type="text/javascript" src="/test/tmp_data/test_ration_calc/ration_calc_base.js"></script>-->
+        <script type="text/javascript" src="/web/building_saas/main/js/models/main_consts.js"></script>
+        <script type="text/javascript" src="/public/web/common_util.js"></script>
 
         <script type="text/javascript" src="/web/building_saas/glj/js/project_glj.js"></script>
         <script type="text/javascript" src="/web/building_saas/glj/js/composition.js"></script>
@@ -604,18 +714,14 @@
 
         <script type="text/javascript" src="/public/web/sheet/sheet_common.js"></script>
 
-
-        <!-- JS. -->
-        <script src="/lib/popper/popper.min.js"></script>
-        <script src="/lib/bootstrap/bootstrap.min.js"></script>
-        <script src="/web/building_saas/js/global.js"></script>
-
         <!--expression calculate-->
         <script src="/lib/JSExpressionEval_src/Date.js"></script>
         <script src="/lib/JSExpressionEval_src/Stack.js"></script>
         <script src="/lib/JSExpressionEval_src/Tokanizer.js"></script>
         <script src="/lib/JSExpressionEval_src/Evaluator.js"></script>
         <!--end expression calculate-->
+        <script type="text/javascript" src="/lib/jquery-ui/jquery-ui.min.js"></script>
+        <script type="text/javascript" src="/lib/jquery-ui/jquery-ui-datepickerCN.js"></script>
         <script type="text/javascript" src="/lib/jquery-contextmenu/jquery.contextMenu.js"></script>
         <script type="text/javascript" src="/lib/jquery-contextmenu/jquery.ui.position.js"></script>
         <script type="text/javascript" src="/lib/lodash/lodash.js"></script>
@@ -624,13 +730,18 @@
         <script type="text/javascript" src="/public/web/url_util.js"></script>
         <script type="text/javascript" src="/public/web/number_util.js"></script>
         <script type="text/javascript" src="/public/web/sheet/sheet_common.js"></script>
+
+        <!-- JS. -->
+        <script src="/lib/popper/popper.min.js"></script>
+        <script src="/lib/bootstrap/bootstrap.min.js"></script>
+        <script src="/web/building_saas/js/global.js"></script>
         <!--报表 zTree -->
 
         <!-- SpreadJs -->
 
         <!--<script src="/lib/spreadjs/views/locale/gc.spread.views.dataview.locale.zh-CN.10.0.0.min.js" type="text/javascript"></script>-->
         <!-- Model -->
-        <script type="text/javascript" src="/web/building_saas/main/js/models/main_consts.js"></script>
+
         <script type="text/javascript" src="/web/building_saas/main/js/models/project.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/models/bills.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/models/ration.js"></script>
@@ -641,13 +752,14 @@
         <script type="text/javascript" src="/web/building_saas/main/js/models/ration_glj.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/models/ration_coe.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/models/ration_ass.js"></script>
-        <script type="text/javascript" src="/web/building_saas/main/js/models/volume_price.js"></script>
+        <!--<script type="text/javascript" src="/web/building_saas/main/js/models/volume_price.js"></script>-->
         <script type="text/javascript" src="/web/building_saas/main/js/models/labour_coe.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/models/calc_program.js"></script>
+        <script type="text/javascript" src="/web/building_saas/main/js/models/calc_base.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/views/calc_program_manage.js"></script>
 
+
         <script type="text/javascript" src="/public/web/id_tree.js"></script>
-        <script type="text/javascript" src="/test/tmp_data/test_ration_calc/ration_calc_base.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/models/cache_tree.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/calc/calc_fees.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/calc/ration_calc.js"></script>
@@ -665,6 +777,9 @@
         <script type="text/javascript" src="/web/building_saas/main/js/views/options_view.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/views/project_property_bills_quantity_decimal.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/views/project_property_decimal_view.js"></script>
+        <script type="text/javascript" src="/web/building_saas/main/js/views/project_property_basicInfo.js"></script>
+        <script type="text/javascript" src="/web/building_saas/main/js/views/project_property_projFeature.js"></script>
+        <script type="text/javascript" src="/web/building_saas/main/js/views/project_property_display_view.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/main_ajax.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/main.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/controllers/project_controller.js"></script>
@@ -688,6 +803,7 @@
         <script type="text/javascript" src="/web/building_saas/main/js/views/sub_view.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/views/fee_rate_view.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/views/sub_fee_rate_views.js"></script>
+        <script type="text/javascript" src="/web/building_saas/main/js/views/calc_base_view.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/views/project_property_labour_coe_view.js"></script>
         <!-- endinject -->
 
@@ -695,8 +811,6 @@
             autoFlashHeight();
         </script>
 
-        <script src="/public/debug.js"></script>
-
         <SCRIPT type="text/javascript">
             <!--
 //            var zNodes =[

+ 3 - 3
web/building_saas/main/js/calc/bills_calc.js

@@ -2,7 +2,7 @@
  * Created by Mai on 2017/7/5.
  */
 
-const rationContent = 0, rationPrice = 1, rationPriceConverse = 2, billsPrice = 3;
+// const rationContent = 0, rationPrice = 1, rationPriceConverse = 2, billsPrice = 3;
 
 // sumTotalFeeFlag: sum(child.totalFee), totalFeeFlag: bills.quantity × bills.unitFee
 const sumTotalFeeFlag = 0, totalFeeFlag = 1;
@@ -268,8 +268,8 @@ class BillsCalcHelper {
         let nodeCalc = nodeCalcObj, virData= null, decimal = this.project.Decimal;
 
         // 清单单价:套用定额计算程序
-        // if (this.project.calcFlag === billsPrice) {
-        if (this.project.projSetting.billsCalcMode === billsPrice) {
+        // if (this.project.projSetting.billsCalcMode === billsPrice) {
+        if (this.project.property.billsCalcMode === leafBillGetFeeType.billsPrice) {
             rationCalcObj.calcGljs = this.getBillsGLjs(node);
             console.log(rationCalcObj.calcGljs);
             rationCalcObj.calcFields = rationCalcFields;

+ 2 - 1
web/building_saas/main/js/calc/calc_fees.js

@@ -18,6 +18,7 @@ let feeType = [
     {type: 'material', name: '材料费'},
     {type: 'machine', name: '机械费'},
     {type: 'mainMaterial', name: '主材费'},
+    {type: 'equipment', name: '设备费'},
     {type: 'manage', name: '企业管理费'},
     {type: 'profit', name: '利润'},
     {type: 'risk', name: '风险费'},
@@ -70,7 +71,7 @@ let calcFees = {
     getFee: function (data, fullField) {
         let fields = fullField.split('.'), value = data;
         for (let field of fields) {
-            if (value[field]) {
+            if (value[field]!=undefined||value[field]!=null) {
                 value = value[field];
             } else {
                 return 0;

+ 14 - 20
web/building_saas/main/js/controllers/project_controller.js

@@ -47,7 +47,7 @@ ProjectController = {
             this.syncDisplayNewNode(sheetController, newNode);
         }
     },
-    addRation: function (project, sheetController, std) {
+    addRation: function (project, sheetController, rationType, std) {
         if (!project || !sheetController) { return; }
 
         let selected = project.mainTree.selected, newSource = null, newNode = null;
@@ -59,17 +59,13 @@ ProjectController = {
             } else if (false) {
                 alert('当前清单已有公式计算,不能套用定额。');
             } else {
-                let firstChild = selected.firstChild();
-                if (firstChild && firstChild.sourceType === project.VolumePrice.getSourceType()) {
-                    alert('当前位置已有量价,不能套用定额。');
+                if (std) {
+                    newSource = project.Ration.insertStdRation(selected.source.getID(), null, std);
+                    project.ration_glj.addRationGLJ(newSource,std);
                 } else {
-                    if (std) {
-                        newSource = project.Ration.insertStdRation(selected.source.getID(), null, std);
-                        project.ration_glj.addRationGLJ(newSource,std);
-                    } else {
-                        newSource = project.Ration.insertRation(selected.source.getID());
-                    }
+                    newSource = project.Ration.insertRation(selected.source.getID(),null, rationType);
                 }
+
                 newNode = project.mainTree.insert(selected.getID(), selected.tree.rootID());
             }
         } else if (selected.sourceType === project.Ration.getSourceType()) {
@@ -77,12 +73,10 @@ ProjectController = {
                 newSource = project.Ration.insertStdRation(selected.source[project.masterField.ration], selected.source, std);
                 project.ration_glj.addRationGLJ(newSource,std);
             } else {
-                newSource = project.Ration.insertRation(selected.source[project.masterField.ration], selected.source);
+                newSource = project.Ration.insertRation(selected.source[project.masterField.ration], selected.source, rationType);
             }
             newNode = project.mainTree.insert(selected.getParentID(), selected.getNextSiblingID());
-        } else if (selected.sourceType === project.VolumePrice.getSourceType()) {
-            alert('当前位置已有量价,不能套用定额。');
-        }
+        };
         if (newNode) {
             newNode.source = newSource;
             newNode.sourceType = project.Ration.getSourceType();
@@ -104,8 +98,8 @@ ProjectController = {
         } else {
             alert('当前焦点行不是定额,无法替换。');
         }
-    },
-    addVolumePrice: function (project, sheetController) {
+    }
+/*    addVolumePrice: function (project, sheetController) {
         if (!project || !sheetController) { return null; }
 
         var selected = project.mainTree.selected;
@@ -117,7 +111,7 @@ ProjectController = {
         if (selected.sourceType === project.Bills.getSourceType() && selected.source.children.length === 0) {
             newSource = project.VolumePrice.insertVolumePrice(selected.source.getID());
             newNode = project.mainTree.insert(selected.getID(), selected.tree.rootID());
-        } else if (selected.sourceType === project.VolumePrice.getSourceType()) {
+        } else if (selected.sourceType === project.Ration.getSourceType() || selected.sourceType === project.VolumePrice.getSourceType()) {
             newSource = project.VolumePrice.insertVolumePrice(selected.source[project.masterField.volumePrice], selected.source);
             newNode = project.mainTree.insert(selected.getParentID(), selected.getNextSiblingID());
         }
@@ -128,13 +122,13 @@ ProjectController = {
 
             this.syncDisplayNewNode(sheetController, newNode);
         }
-    },
-    calculateAll: function (project, sheetController, CalcType) {
+    },*/
+/*    calculateAll: function (project, sheetController, CalcType) {
         this.project.setCalcFlag(CalcType);
         let calc = new BillsCalcHelper(project);
         calc.calcAll();
         sheetController.showTreeData();
         project.Bills.updateAll();
         calc = null;
-    }
+    }*/
 }

+ 107 - 1
web/building_saas/main/js/main.js

@@ -3,6 +3,11 @@
  */
 
 $(function () {
+    // 读取本地存储的高度(必须放在载入spread之前)
+    loadSize("main", function() {
+        refreshSubSpread();
+    });
+
     projectInfoObj.showProjectInfo();
     projectObj.checkMainSpread();
     projectObj.loadProjectData();
@@ -19,4 +24,105 @@ $(function () {
         // do something
     });
 
-});
+    slideResize($("#main"), function() {
+        projectObj.mainSpread.refresh();
+        refreshSubSpread();
+    });
+
+    const projectId = scUrlUtil.GetQueryString('project');
+    // 绑定点击事件
+    projectObj.mainSpread.bind(GC.Spread.Sheets.Events.CellClick, function(sender, info) {
+        if (info.row !== undefined && projectId !== undefined) {
+            setLocalCache('lastRow:' + projectId, info.row);
+            setLocalCache('lastCol:' + projectId, info.col);
+        }
+    });
+
+});
+
+/**
+ * 拖动更改div大小
+ *
+ * @param {Object} rootElement - 最顶层div
+ * @param {function} callback - 成功后执行
+ * @return {void}
+ */
+function slideResize(rootElement, callback) {
+    let startY = 0;
+    let drag = false;
+    const element = rootElement.find('.resize');
+    const topContentEle = rootElement.find(".top-content");
+    const bottomContentEle = rootElement.find(".bottom-content");
+    let topContentHeight = 0;
+    let bottomContentHeight = 0;
+    let navHeight = 0;
+    let topChangeHeight = 0;
+    let bottomChangeHeight = 0;
+
+    // 鼠标点下时
+    element.mousedown(function(e) {
+        drag = true;
+        startY = e.clientY;
+        // 获取上部分的高度
+        topContentHeight = topContentEle.height();
+        // 获取下部分的高度
+        bottomContentHeight = bottomContentEle.height();
+        // nav高度部分
+        navHeight = bottomContentEle.children('ul.nav').height();
+        element.tooltip('hide');
+    });
+
+    // 鼠标移动
+    $("body").mousemove(function(e) {
+        if (drag) {
+            let moveHeight = e.clientY - startY;
+            // 判断拖动范围不能超出
+            topChangeHeight = topContentHeight + moveHeight;
+            topChangeHeight = topChangeHeight < 170 ? 170 : topChangeHeight;
+            topChangeHeight = topChangeHeight > 700 ? 700 : topChangeHeight;
+            topContentEle.children(".main-data-top").height(topChangeHeight);
+
+            bottomChangeHeight = bottomContentHeight - moveHeight;
+            bottomChangeHeight = bottomChangeHeight < 170 ? 170 : bottomChangeHeight;
+            bottomChangeHeight = bottomChangeHeight > 700 ? 700 : bottomChangeHeight;
+            bottomContentEle.children().find(".main-data-bottom").height(bottomChangeHeight - navHeight);
+        }
+    });
+
+    // 鼠标弹起
+    $("body").mouseup(function(e) {
+        if (drag) {
+            callback();
+            drag = false;
+            // 存入本地缓存
+            const id = rootElement.attr('id');
+            setLocalCache('topHeight:' + id, topChangeHeight);
+            setLocalCache('bottomHeight:' + id, bottomChangeHeight);
+        }
+    });
+}
+
+/**
+ * 读取设置的高度
+ *
+ * @param {String} tag - 顶层div的id
+ * @param {function} callback - 回调函数
+ * @return {void}
+ */
+function loadSize(tag, callback) {
+    if (tag === '') {
+        return;
+    }
+    let topHeight = getLocalCache('topHeight:' + tag);
+    let bottomHeight = getLocalCache('bottomHeight:' + tag);
+    if (topHeight === null || bottomHeight === null) {
+        return;
+    }
+    const navHeight = $("#"+ tag +" .bottom-content").children('ul.nav').height();
+    topHeight = parseFloat(topHeight);
+    bottomHeight = parseFloat(bottomHeight);
+    $("#"+ tag +" .main-data-top").height(topHeight);
+    $("#"+ tag +" .main-data-bottom").height(bottomHeight - navHeight);
+    $("#"+ tag +" .bottom-content").height(bottomHeight);
+    callback();
+}

+ 8 - 18
web/building_saas/main/js/models/bills.js

@@ -122,6 +122,7 @@ var Bills = {
             if(data.quantityRefresh){
                 this.refreshDatas(data,'quantity');
             }
+            $.bootstrapLoading.end();
         };
 
         bills.prototype.refreshDatas = function(data,fieldName){
@@ -182,6 +183,7 @@ var Bills = {
                     data.data.itemCharacter = stdBillsData.itemCharacter;
                     data.data.jobContentText = stdBillsData.jobContentText;
                     data.data.itemCharacterText = stdBillsData.itemCharacterText;
+                    data.data.programID = stdBillsData.engineering;
                     //zhong
                     newData = data.data;
                 }
@@ -195,15 +197,16 @@ var Bills = {
         bills.prototype.deleteBills = function (node) {
             let deleteNode = function (node) {
                 this.project.Ration.deleteByBills([node]);
-                this.project.VolumePrice.deleteByBills([node]);
+                // this.project.VolumePrice.deleteByBills([node]);
                 return this.tree.delete(node);
             }
             var deleteData = this.tree.getDeleteData(node);
             var ration_glj =projectObj.project.ration_glj;
-            let modules =[ModuleNames.bills, ModuleNames.ration, ModuleNames.ration_glj, ModuleNames.volume_price];
+            // let modules =[ModuleNames.bills, ModuleNames.ration, ModuleNames.ration_glj, ModuleNames.volume_price];
+            let modules =[ModuleNames.bills, ModuleNames.ration, ModuleNames.ration_glj];
             let deleteDatas=[tools.coverseTreeUpdateData(deleteData, this.project.ID()),
                 this.project.Ration.getDeleteDataByBill([node]), ration_glj.getDeleteDataByBills(deleteData),
-                this.project.VolumePrice.getDeleteDataByBills([node])
+                // this.project.VolumePrice.getDeleteDataByBills([node])
             ];
             project.ration_glj.deleteByBills(deleteData);
             project.quantity_detail.deleteByBills(deleteData);
@@ -242,21 +245,7 @@ var Bills = {
             calcFees.setFee(node.data, field, newValue);
             let updateData = [];
             let data = {'ID': node.getID(), 'projectID': this.project.ID()};
-            if (field === 'quantity') {
-                data[field] = newValue;
-                data.isFromDetail=0;
-                // to do Calculate
-                if (node.data.fees) {
-                    data.fees = node.data.fees;
-                }
-            } else if (field === 'feesIndex.common.unitFee') {
-                // to do Calculate
-                if (node.data.fees) {
-                    data.fees = node.data.fees;
-                }
-            } else {
-                data[field] = newValue;
-            }
+            data[field] = newValue;
             updateData.push({'updateType': 'ut_update', 'updateData': tools.formatBillsUpdateData(data)});
             this.project.pushNow('updateBills', this.getSourceType(), updateData);
         };
@@ -332,6 +321,7 @@ var Bills = {
                 // 特征
                 node.data.itemCharacter = stdBillsData.itemCharacter;
                 node.data.itemCharacterText = stdBillsData.itemCharacterText;
+                node.data.programID = stdBillsData.engineering;
             }
             updateData.push({'updateType': 'ut_update', 'updateData': tools.formatBillsUpdateData(node.data)});
 

+ 613 - 0
web/building_saas/main/js/models/calc_base.js

@@ -0,0 +1,613 @@
+/**
+ * Created by Zhong on 2017/11/28.
+ */
+//清单固定行
+const fixedFlag = {
+    // 分部分项工程
+    SUB_ENGINERRING: 1,
+    // 措施项目
+    MEASURE: 2,
+    // 施工技术措施项目
+    CONSTRUCTION_TECH: 3,
+    // 安全文明施工按实计算费用
+    SAFETY_CONSTRUCTION_ACTUAL: 4,
+    // 施工组织措施专项费用
+    CONSTRUCTION_ORGANIZATION: 5,
+    // 安全文明施工专项费用
+    SAFETY_CONSTRUCTION: 6,
+    // 其他项目
+    OTHER: 7,
+    // 暂列金额
+    PROVISIONAL: 8,
+    // 暂估价
+    ESTIMATE: 9,
+    // 材料(工程设备)暂估价
+    MATERIAL_PROVISIONAL: 10,
+    // 专业工程暂估价
+    ENGINEERING_ESITIMATE: 11,
+    // 计日工
+    DAYWORK: 12,
+    // 总承包服务费
+    TURN_KEY_CONTRACT: 13,
+    // 索赔与现场签证
+    CLAIM_VISA: 14,
+    // 规费
+    CHARGE: 15,
+    // 社会保险费及住房公积金 Social insurance fee and housing accumulation fund
+    SOCIAL_INSURANCE_HOUSING_FUND: 16,
+    // 工程排污费 charges for disposing pollutants
+    POLLUTANTS: 17,
+    // 税金
+    TAX: 18
+};
+
+let cbTools = {
+    isDef: function (v) {
+        return v !== undefined && v !== null;
+    },
+    isUnDef: function (v) {
+        return v === undefined || v === null;
+    },
+    isNum: function (v) {
+        return this.isDef(v) && !isNaN(v) && v !== Infinity;
+    },
+    isFlag: function (v) {
+        return this.isDef(v.flagsIndex) && this.isDef(v.flagsIndex.fixed);
+    },
+    returnV: function (v, r) {
+        if(this.isDef(v)){
+            return v;
+        }
+        return r;
+    },
+    findBill: function (fixedFlag) {
+        let bills = projectObj.project.Bills.datas;
+        for(let i = 0, len = bills.length; i < len; i++){
+            if(bills[i].flagsIndex.flag === fixedFlag){
+                return bills[i];
+            }
+        }
+    },
+    //需要用到计算基数的时候,先找出所有的固定清单,避免每个基数都要去遍历寻找清单
+    setFixedBills: function (project, billsObj, fixedFlag) {
+        let bills = project.Bills.datas;
+        for(let i = 0, len = bills.length; i < len; i++){
+            if(this.isDef(bills[i].flagsIndex.fixed)){
+                for(let flag in fixedFlag){
+                    if(fixedFlag[flag] === bills[i].flagsIndex.fixed.flag){
+                        billsObj[fixedFlag[flag]] = Object.create(null);
+                        billsObj[fixedFlag[flag]]['base']  = Object.create(null);
+                        billsObj[fixedFlag[flag]]['bill']  = bills[i];
+                    }
+                }
+            }
+        }
+    },
+    //清单基数设置所属固定清单属性
+    setBaseBills: function (baseFigure, fixedBills) {
+        for(let i in baseFigure){
+            let calcBase = baseFigure[i];
+            calcBase.fixedBill = null;
+            if(cbTools.isDef(fixedBills[calcBase.fixedFlag])){
+                fixedBills[calcBase.fixedFlag]['base'][i] = calcBase;
+                calcBase.fixedBill = fixedBills[calcBase.fixedFlag];
+            }
+        }
+    },
+    //生成清单基数计算分类模板
+    setBaseFigureClass: function (baseFigures, mapObj) {
+        mapObj['CONSTRUCTION_ORGANIZATION'] = Object.create(null);
+        mapObj['OTHER'] = Object.create(null);
+        mapObj['CHARGE'] = Object.create(null);
+        mapObj['TAX'] = Object.create(null);
+        mapObj['OTHERS'] = Object.create(null);
+        let filter = ['CSXMF', 'ZZCSXMF', 'ZZCSXMDEJJZJGCF', 'ZZCSXMDEJJRGF', 'ZZCSXMDEJJCLF', 'ZZCSXMDEJJJXF', 'QTXMF', 'GF', 'SJ'];
+        for(let figure in baseFigures){
+            if(filter.indexOf(baseFigures[figure]['base']) === -1){
+                mapObj['CONSTRUCTION_ORGANIZATION'][figure] = baseFigures[figure];
+            }
+            if(baseFigures[figure]['base'] !== 'QTXMF'){
+                mapObj['OTHER'][figure] = baseFigures[figure];
+            }
+            if(baseFigures[figure]['base'] !== 'GF'){
+                mapObj['CHARGE'][figure] = baseFigures[figure];
+            }
+            if(baseFigures[figure]['base'] !== 'SJ'){
+                mapObj['TAX'][figure] = baseFigures[figure];
+            }
+            mapObj['OTHERS'][figure] = baseFigures[figure];
+        }
+    },
+    getFigure: 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)){
+            //node.data.baseFigureClass = null;
+            return null;
+        }
+        else if(this.isFlag(node.data) && node.data.flagsIndex.fixed.flag === calcBase.fixedFlag.CONSTRUCTION_ORGANIZATION){
+            //node.data.baseFigureClass = 'CONSTRUCTION_ORGANIZATION';
+            return calcBase.baseFigureClass.CONSTRUCTION_ORGANIZATION;
+        }
+        else if(this.isFlag(node.data) && node.data.flagsIndex.fixed.flag === calcBase.fixedFlag.OTHER){
+            //node.data.baseFigureClass = 'OTHER';
+            return calcBase.baseFigureClass.OTHER;
+        }
+        else if(this.isFlag(node.data) && node.data.flagsIndex.fixed.flag === calcBase.fixedFlag.CHARGE){
+            //node.data.baseFigureClass = 'CHARGE';
+            return calcBase.baseFigureClass.CHARGE;
+        }
+        else if(this.isFlag(node.data) && node.data.flagsIndex.fixed.flag === calcBase.fixedFlag.TAX){
+            //node.data.baseFigureClass = 'TAX';
+            return calcBase.baseFigureClass.TAX;
+        }
+        else {
+            if(!parent){
+                //node.data.baseFigureClass = 'OTHERS';
+                return calcBase.baseFigureClass.OTHERS;
+            }
+            else {
+                return this.getFigure(parent);
+            }
+        }
+    },
+    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;
+        }
+        else if(this.isFlag(node.data) && node.data.flagsIndex.fixed.flag === calcBase.fixedFlag.OTHER){
+            return node;
+        }
+        else if(this.isFlag(node.data) && node.data.flagsIndex.fixed.flag === calcBase.fixedFlag.CHARGE){
+            return node;
+        }
+        else if(this.isFlag(node.data) && node.data.flagsIndex.fixed.flag === calcBase.fixedFlag.TAX){
+            return node;
+        }
+        else {
+            if(!parent){
+                return node;
+            }
+            else {
+                return this.getBaseBill(parent);
+            }
+        }
+    },
+    //获取清单(有基数计算)引用了的其他清单,(循环引用栈中的一块)
+    getStackBlock: function (billID) {
+        let tempBases = [], block = [];//存引用的清单ID
+        let node = getBill(billID);
+        if(!node){
+            return tempBases;
+        }
+        else {
+            getBase(node);
+            let bases = Array.from(new Set(tempBases));
+            for(let i = 0, len = bases.length; i < len; i++){
+                if(cbTools.isDef(calcBase.baseFigures[bases[i]])){
+                    block.push(calcBase.baseFigures[bases[i]]['fixedBill']['bill']['ID']);
+                }
+            }
+            return Array.from(new Set(block));
+        }
+        function getBase(node){
+            if(node && node.children.length === 0){
+                if(cbTools.isDef(node.data.calcBase)){
+                    let figures = cbParser.getFigure(node.data.calcBase);
+                    tempBases = tempBases.concat(figures);
+                }
+            }
+            else if(node && node.children.length > 0) {
+                for(let i = 0, len = node.children.length; i < len; i++){
+                    getBase(node.children[i]);
+                }
+            }
+        }
+        function getBill(ID){
+            for(let i = 0, len = calcBase.project.mainTree.items.length; i < len; i++){
+                if(calcBase.project.mainTree.items[i].data.ID === ID && calcBase.project.mainTree.items[i].sourceType === calcBase.project.Bills.getSourceType()){
+                    return calcBase.project.mainTree.items[i];
+                }
+            }
+            return null;
+        }
+    }
+};
+
+let baseFigureTemplate = {
+    'FBFXGCF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.SUB_ENGINERRING]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.common.totalFee || 0;
+    },
+    'FBFXDEJJRGF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.SUB_ENGINERRING]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.labour.totalFee || 0;
+    },
+    'FBFXDEJJCLF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.SUB_ENGINERRING]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.material.totalFee || 0;
+    },
+    'FBFXDEJJJXF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.SUB_ENGINERRING]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.machine.totalFee || 0;
+    },
+    'FBFXTZRGF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.SUB_ENGINERRING]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.adjustLabour.totalFee || 0;
+    },
+    'FBFXTZJSRGF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.SUB_ENGINERRING]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.adjustMachineLabour.totalFee || 0;
+    },
+    'FBFXZCF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.SUB_ENGINERRING]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.mainMaterial.totalFee || 0;
+    },
+    'FBFXSBF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.SUB_ENGINERRING]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.mainMaterial.totalFee || 0;
+    },
+    'FBFXWJJCLF': function () {
+        return 0;
+    },
+    'FBFXRGGR': function () {
+        return 0;
+    },
+    'FBFXGCLQDJJZJGCF': function () {
+        return this['FBFXDEJJRGF']() + this['FBFXDEJJCLF']() + this['FBFXDEJJJXF']();
+    },
+    'CSXMF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.MEASURE]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.common.totalFee || 0;
+    },
+    'ZZCSXMF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.CONSTRUCTION_ORGANIZATION]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.common.totalFee || 0;
+    },
+    'ZZCSXMDEJJZJGCF': function () {
+        return this['ZZCSXMDEJJRGF']() + this['ZZCSXMDEJJCLF']() + this['ZZCSXMDEJJJXF']()
+    },
+    'ZZCSXMDEJJRGF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.CONSTRUCTION_ORGANIZATION]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.labour.totalFee || 0;
+    },
+    'ZZCSXMDEJJCLF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.CONSTRUCTION_ORGANIZATION]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.material.totalFee || 0;
+    },
+    'ZZCSXMDEJJJXF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.CONSTRUCTION_ORGANIZATION]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.machine.totalFee || 0;
+    },
+    'JSCSXMF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.CONSTRUCTION_TECH]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.common.totalFee || 0;
+    },
+    'JSCSXMDEJJRGF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.CONSTRUCTION_TECH]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.labour.totalFee || 0;
+    },
+    'JSCSXMDEJJCLF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.CONSTRUCTION_TECH]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.material.totalFee || 0;
+    },
+    'JSCSXMDEJJJXF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.CONSTRUCTION_TECH]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.machine.totalFee || 0;
+    },
+    'JSCSXMTZRGF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.CONSTRUCTION_TECH]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.adjustLabour.totalFee || 0;
+    },
+    'JSCSXMTZJSRGF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.CONSTRUCTION_TECH]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.adjustMachineLabour.totalFee || 0;
+    },
+    'JSCSXMZCF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.CONSTRUCTION_TECH]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.mainMaterial.totalFee || 0;
+    },
+    'JSCSXMSBF': function () {
+        return 0;
+    },
+    'JSCSXMWJJCLF': function () {
+        return 0;
+    },
+    'JSCSXMRGGR': function () {
+        return 0;
+    },
+    'JSCSXMQDDEJJZJGCF': function () {
+        return this['JSCSXMDEJJRGF']() + this['JSCSXMDEJJCLF']() + this['JSCSXMDEJJJXF']();
+    },
+    'QTXMF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.OTHER]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.common.totalFee || 0;
+    },
+    'GF': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.CHARGE]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.common.totalFee || 0;
+    },
+    'SJ': function () {
+        let bill = calcBase.fixedBills[calcBase.fixedFlag.TAX]['bill'];
+        if(cbTools.isUnDef(bill)) return 0;
+        if(cbTools.isUnDef(bill.feesIndex) || Object.keys(bill.feesIndex).length === 0) return 0;
+        return bill.feesIndex.common.totalFee || 0;
+    }
+};
+
+let baseFigureMap = {
+    '分部分项工程费': {base: 'FBFXGCF', fixedFlag: fixedFlag.SUB_ENGINERRING},
+    '分部分项定额基价人工费': {base: 'FBFXDEJJRGF', fixedFlag: fixedFlag.SUB_ENGINERRING},
+    '分部分项定额基价材料费': {base: 'FBFXDEJJCLF', fixedFlag: fixedFlag.SUB_ENGINERRING},
+    '分部分项定额基价机械费': {base: 'FBFXDEJJJXF', fixedFlag: fixedFlag.SUB_ENGINERRING},
+    '分部分项调整人工费': {base: 'FBFXTZRGF', fixedFlag: fixedFlag.SUB_ENGINERRING},
+    '分部分项调整机上人工费': {base: 'FBFXTZJSRGF', fixedFlag: fixedFlag.SUB_ENGINERRING},
+    '分部分项主材费': {base: 'FBFXZCF', fixedFlag: fixedFlag.SUB_ENGINERRING},
+    '分部分项设备费': {base: 'FBFXSBF', fixedFlag: fixedFlag.SUB_ENGINERRING},
+    '分部分项未计价材料费': {base: 'FBFXWJJCLF', fixedFlag: fixedFlag.SUB_ENGINERRING},
+    '分部分项人工工日': {base: 'FBFXRGGR', fixedFlag: fixedFlag.SUB_ENGINERRING},
+    '分部分项工程量清单中的基价直接工程费': {base: 'FBFXGCLQDJJZJGCF', fixedFlag: fixedFlag.SUB_ENGINERRING},
+    '措施项目费': {base: 'CSXMF', fixedFlag: fixedFlag.MEASURE},
+    '组织措施项目费': {base: 'ZZCSXMF', fixedFlag: fixedFlag.CONSTRUCTION_ORGANIZATION},
+    '组织措施项目定额基价直接工程费': {base: 'ZZCSXMDEJJZJGCF', fixedFlag: fixedFlag.CONSTRUCTION_ORGANIZATION},
+    '组织措施项目定额基价人工费': {base: 'ZZCSXMDEJJRGF', fixedFlag: fixedFlag.CONSTRUCTION_ORGANIZATION},
+    '组织措施项目定额基价材料费': {base: 'ZZCSXMDEJJCLF', fixedFlag: fixedFlag.CONSTRUCTION_ORGANIZATION},
+    '组织措施项目定额基价机械费': {base: 'ZZCSXMDEJJJXF', fixedFlag: fixedFlag.CONSTRUCTION_ORGANIZATION},
+    '技术措施项目费': {base: 'JSCSXMF', fixedFlag: fixedFlag.CONSTRUCTION_TECH},
+    '技术措施项目定额基价人工费': {base: 'JSCSXMDEJJRGF', fixedFlag: fixedFlag.CONSTRUCTION_TECH},
+    '技术措施项目定额基价材料费': {base: 'JSCSXMDEJJCLF', fixedFlag: fixedFlag.CONSTRUCTION_TECH},
+    '技术措施项目定额基价机械费': {base: 'JSCSXMDEJJJXF', fixedFlag: fixedFlag.CONSTRUCTION_TECH},
+    '技术措施项目调整人工费': {base: 'JSCSXMTZRGF', fixedFlag: fixedFlag.CONSTRUCTION_TECH},
+    '技术措施项目调整机上人工费': {base: 'JSCSXMTZJSRGF', fixedFlag: fixedFlag.CONSTRUCTION_TECH},
+    '技术措施项目主材费': {base: 'JSCSXMZCF', fixedFlag: fixedFlag.CONSTRUCTION_TECH},
+    '技术措施项目设备费': {base: 'JSCSXMSBF', fixedFlag: fixedFlag.CONSTRUCTION_TECH},
+    '技术措施项目未计价材料费': {base: 'JSCSXMWJJCLF', fixedFlag: fixedFlag.CONSTRUCTION_TECH},
+    '技术措施项目人工工日': {base: 'JSCSXMRGGR', fixedFlag: fixedFlag.CONSTRUCTION_TECH},
+    '技术措施项目清单中的定额基价直接工程费': {base: 'JSCSXMQDDEJJZJGCF', fixedFlag: fixedFlag.CONSTRUCTION_TECH},
+    '其他项目费': {base: 'QTXMF',  fixedFlag: fixedFlag.OTHER},
+    '规费': {base: 'GF', fixedFlag: fixedFlag.CHARGE},
+    '税金': {base: 'SJ', fixedFlag: fixedFlag.TAX}
+};
+
+//输入式分析器
+let cbAnalyzer = {
+    standar: function (exp) {
+        //去空格
+        exp = exp.replace(/\s/g, '');
+        //( to (
+        exp = exp.replace(/(/g, '(');
+        //)to )
+        exp = exp.replace(/)/g, ')');
+        return exp;
+    },
+    //输入合法性
+    inputLegal: function (exp) {
+        let ilegalRex = /[^0-9,\u4e00-\u9fa5,\+,\-,\/,\*,\(,\),.]/g;
+        return !ilegalRex.test(exp);
+    },
+    //基数合法性、存在性
+    baseLegal: function (baseFigures, exp) {
+        let expFigures = cbParser.getFigure(exp);
+        for(let i = 0, len = expFigures.length; i < len; i++){
+            if(cbTools.isUnDef(baseFigures[expFigures[i]])){
+                return false;
+            }
+        }
+        return true;
+    },
+    //循环计算
+    cycleCalc: function (node, baseFigures, exp) {
+        let stack = [];
+        if(node.sourceType !== calcBase.project.Bills.getSourceType()){
+            return false;
+        }
+        let sbillID = cbTools.getBaseBill(node).data.ID;
+        let expFigures = cbParser.getFigure(exp);
+        for(let i = 0, len = expFigures.length; i < len; i++){
+            let figure = expFigures[i];
+            if(cbTools.isDef(baseFigures[figure])){
+                let bill = baseFigures[figure]['fixedBill']['bill'];
+                if(checkStack(getRefStack([bill.ID]), sbillID)){
+                    console.log('循环计算');
+                    return true;
+                }
+            }
+        }
+        return false;
+        function checkStack(stack, startBillID){
+            //引用栈发现了初始引用
+            return stack.indexOf(startBillID) !== -1;
+        }
+        function getRefStack(billIDs){
+            stack = Array.from(new Set(stack.concat(billIDs)));
+            for(let i = 0, len = billIDs.length; i < len; i++){
+                let block = cbTools.getStackBlock(billIDs[i]);
+                if(block.length > 0){
+                    stack = Array.from(new Set(stack.concat(block)));
+                    getRefStack(block);
+                }
+            }
+            return stack;
+        }
+    },
+    //四则运算合法性,前端控制不允许重复出现运算符,这里主要判断()的使用问题,这里再判断一次
+    arithmeticLeagl: function (exp) {
+        let ilegalRex = /[\+,\-,\*,\/]{2}/g;
+        return !ilegalRex.test(exp);
+    },
+    //
+    legalExp: function (node) {
+        let exp = this.standar(node.data.userCalcBase);
+        if(this.inputLegal(exp)){
+            if(this.baseLegal(cbTools.getFigure(node), exp)){
+                if(!this.cycleCalc(node, cbTools.getFigure(node), exp)){
+                    if(this.arithmeticLeagl(exp)){
+                        return exp;
+                    }
+                }
+            }
+            return null;
+        }
+        return null;
+    },
+
+    isCN: function(v){
+        let regex = /[\u4e00-\u9fa5]/g;
+        return (regex.test(v));
+    }
+};
+
+//输入式转换器
+let cbParser = {
+    //获取表达式中的基数
+    getFigure: function(expr){
+        let rst = [];
+        let cnRex = /[^\u4e00-\u9fa5]/;
+        let temp = expr.split(cnRex);
+        for(let i = 0, len = temp.length; i < len; i++){
+            if(temp[i] !== '' && rst.indexOf(temp[i]) === -1){
+                rst.push(temp[i]);
+            }
+        }
+        return rst;
+    },
+
+    //将表达式转换为可编译的表达式
+    toCompileExpr: function(v){
+        let strs = this.getFigure(v);
+        let exps = [];
+        for(let i = 0, len = strs.length; i < len; i++){
+            let exp = Object.create(null);
+            exp.orgExp = strs[i];
+            exps.push(exp);
+        }
+        for(let i = 0, len = exps.length;i < len; i++){
+            exps[i].compileExp = '$CBC.base(\'' + exps[i].orgExp;
+            exps[i].compileExp = exps[i].compileExp + '\')';
+            v = v.replace(new RegExp(exps[i].orgExp,"g"), exps[i].compileExp);
+        }
+        return v;
+    }
+};
+
+let cbCalctor = {
+    //计算基数
+    base: function (figure) {
+        return baseFigureTemplate[calcBase.baseFigures[figure]['base']]();
+    },
+    //计算
+    exec: function () {
+
+    }
+};
+
+let calcBase = {
+    success: false,
+    //清单固定行
+    fixedFlag: null,
+    fixedBills: Object.create(null),
+    //清单基数
+    baseFigures: Object.create(null),
+    //清单可选基数映射,分两类:组织措施项目:排除父项和计算的父项; 其他项目、规费、税金、工程造价,及新增部分:显示所有计算基数
+    baseFigureClass: Object.create(null),
+    //初始化
+    init: function (project) {
+        let me = this;
+        me.project = project;
+        me.fixedFlag = fixedFlag;
+        cbTools.setFixedBills(project, me.fixedBills, me.fixedFlag);
+        me.baseFigures = baseFigureMap;
+        cbTools.setBaseBills(me.baseFigures, me.fixedBills);
+        //me.baseFigures.fixedBills = me.fixedBills;
+        cbTools.setBaseFigureClass(me.baseFigures, me.baseFigureClass);
+    },
+    getBase: function (figure) {
+       return cbCalctor.base(figure);
+
+    },
+    getBaseByClass: function (node) {
+        return cbTools.getFigure(node);
+    },
+    calculate: function (node) {
+        let me = calcBase,
+            $CBA = cbAnalyzer,
+            $CBP = cbParser,
+            $CBC = cbCalctor;
+        try {
+            me.success = false;
+            //分析输入式合法性
+            let exp = $CBA.legalExp(node);
+            if(!exp){
+                throw '表达式不正确';
+            }
+            //输入式转换表达式
+            let compileExp = $CBP.toCompileExpr(exp);
+
+            //计算
+            let calcBaseValue = eval(compileExp);
+            if(!cbTools.isNum(calcBaseValue)){
+                throw '表达式不正确';
+            }
+            //存储
+            me.success = true;
+            node.data.calcBase = exp;
+            node.data.calcBaseValue = parseFloat(calcBaseValue).toDecimal(decimalObj.decimal('totalPrice', node));
+            me.project.calcProgram.calculate(node);
+            me.project.calcProgram.saveNode(node);
+        }
+        catch (err){
+            alert(err);
+        }
+    },
+};

+ 373 - 114
web/building_saas/main/js/models/calc_program.js

@@ -6,9 +6,9 @@
  *  用到费率的规则必须有feeRateID属性,当有该属性时,会自动显示费率值。
  */
 
-let defaultBillTemplate = {
+/*let defaultBillTemplate = {
     ID: 15,
-    name: "清单缺省",
+    name: "清单公式",
     calcItems: [
         {
             ID: 1,
@@ -98,7 +98,57 @@ let defaultBillTemplate = {
             memo: ''
         }
     ]
-};
+};*/
+
+const baseCalcType = {baseCalc: 0, adjustCalc: 1, budgetCalc: 2, diffCalc: 3,  offerCalc: 4};
+
+let rationCalcBase = [
+    {
+        'dispName': '定额基价人工费',
+        'calcType': baseCalcType.baseCalc,
+        'gljTypes': [gljType.LABOUR]
+    },
+    {
+        'dispName': '定额基价材料费',
+        'calcType': baseCalcType.baseCalc,
+        'gljTypes': [gljType.GENERAL_MATERIAL, gljType.CONCRETE, gljType.MORTAR, gljType.MIX_RATIO, gljType.COMMERCIAL_CONCRETE, gljType.COMMERCIAL_MORTAR]
+    },
+    {
+        'dispName': '定额基价机械费',
+        'calcType': baseCalcType.baseCalc,
+        'gljTypes': [gljType.GENERAL_MACHINE]
+    },
+    {
+        'dispName': '定额基价机上人工费',
+        'calcType': baseCalcType.baseCalc,
+        'gljTypes': [gljType.MACHINE_LABOUR]
+    },
+    {
+        'dispName': '人工费价差',
+        'calcType': baseCalcType.diffCalc,
+        'gljTypes': [gljType.LABOUR]
+    },
+    {
+        'dispName': '材料费价差',
+        'calcType': baseCalcType.diffCalc,
+        'gljTypes': [gljType.GENERAL_MATERIAL, gljType.CONCRETE, gljType.MORTAR, gljType.MIX_RATIO, gljType.COMMERCIAL_CONCRETE, gljType.COMMERCIAL_MORTAR]
+    },
+    {
+        'dispName': '机械费价差',
+        'calcType': baseCalcType.diffCalc,
+        'gljTypes': [gljType.GENERAL_MACHINE]
+    },
+    {
+        'dispName': '主材费',
+        'calcType': baseCalcType.budgetCalc,
+        'gljTypes': [gljType.MAIN_MATERIAL]
+    },
+    {
+        'dispName': '设备费',
+        'calcType': baseCalcType.budgetCalc,
+        'gljTypes': [gljType.EQUIPMENT]
+    }
+];
 
 let analyzer = {
     calcTemplate: null,
@@ -255,23 +305,19 @@ let executeObj = {
     },
     base: function(calcBaseName) {
         let me = executeObj, rst = 0,
-            //base = getRationCalcBase(calcBaseName);
             base = me.calcBase[calcBaseName];
 
         if (base != null) {
-            let price = 0, aprice = 0, mprice = 0, tmpSum = 0, mdSum = 0;
-
             function isSubset(sub, arr){
-                // if(!(sub instanceof Array) || !(arr instanceof Array)) return false;
-                // if(sub.length > arr.length) return false;
                 for(var i = 0, len = sub.length; i < len; i++){
                     if(arr.indexOf(sub[i]) == -1) return false;
                 }
                 return true;
-            }
-
+            };
             // 机上人工费:多一层
-            if (isSubset(base.gljTypes, [gljType.MACHINE_LABOUR])) {
+            function machineLabourFee() {
+                if (!me.treeNode.data.gljList) return 0;
+                let result = 0, mdSum = 0;
                 for (let glj of me.treeNode.data.gljList) {
                     if (glj.type == gljType.GENERAL_MACHINE) {
                         // 获取机械组成物
@@ -279,44 +325,71 @@ let executeObj = {
                         if (!mds) mds = [];
                         for (let md of mds){
                             if (base.gljTypes.indexOf(md.glj_type) >= 0) {
-                                price = md["base_price"];
-                                if (!price) price = 0;
-                                mdSum = mdSum + (md["consumption"] * price).toDecimal(me.digit);
-                                mdSum = (mdSum).toDecimal(me.digitDefault);
+                                let q = md["consumption"] ? md["consumption"] : 0;
+                                let p = md["base_price"] ? md["base_price"] : 0;
+                                mdSum = mdSum + (q * p).toDecimal(decimalObj.process);
+                                mdSum = (mdSum).toDecimal(decimalObj.process);
                             }
                         };
-                        tmpSum = tmpSum + (glj["quantity"] * mdSum).toDecimal(me.digitDefault);
-                        tmpSum = (tmpSum).toDecimal(me.digitDefault);
-                    }
+                        result = result + (glj["quantity"] * mdSum).toDecimal(decimalObj.process);
+                        result = (result).toDecimal(decimalObj.process);
+                    };
                 };
-            }else{
+                return result;
+            };
+            function commonGLJFee(){
+                if (!me.treeNode.data.gljList) return 0;
+                let result = 0;
                 for (let glj of me.treeNode.data.gljList) {
+                    let price = 0;
                     if (base.gljTypes.indexOf(glj.type) >= 0) {
-                        if (base.calcType == baseCalc){ price = glj["basePrice"];}
-                        else if (base.calcType == adjustCalc){price = glj["adjustPrice"];}
-                        else if (base.calcType == budgetCalc){price = glj["marketPrice"];}
-                        else if (base.calcType == diffCalc){
-                            aprice = glj["adjustPrice"];
-                            if (!aprice) aprice = 0;
-                            mprice = glj["marketPrice"];
-                            if (!mprice) mprice = 0;
-                            price = mprice - aprice;
+                        if (base.calcType == baseCalcType.baseCalc){ price = parseFloat(glj["basePrice"]);}
+                        else if (base.calcType == baseCalcType.adjustCalc){price = parseFloat(glj["adjustPrice"]);}
+                        else if (base.calcType == baseCalcType.budgetCalc){price = parseFloat(glj["marketPrice"]);}
+                        else if (base.calcType == baseCalcType.diffCalc){
+                            let aprice = glj["adjustPrice"] ? glj["adjustPrice"] : 0;
+                            let mprice = glj["marketPrice"] ? glj["marketPrice"] : 0;
+                            price = (parseFloat(mprice) - parseFloat(aprice)).toDecimal(decimalObj.process);
                         };
-                        if (!price) price = 0;
-                        tmpSum = tmpSum + (glj["quantity"] * price).toDecimal(me.digitDefault);
-                        tmpSum = (tmpSum).toDecimal(me.digitDefault);
+                        result = result + (glj["quantity"] * price).toDecimal(decimalObj.process);
+                        result = (result).toDecimal(decimalObj.process);
                     };
                 };
+                return result;
+            };
+            // 量价没有具体的工料机类型,但仍然要用定额的计算程序,所以要给计算基数直接指定。
+            function volumePriceFee() {
+                let result = 0;
+                if (
+                    ( me.treeNode.data.subType === gljType.LABOUR && base.dispName === '定额基价人工费') ||
+                    ( me.treeNode.data.subType === gljType.GENERAL_MATERIAL && base.dispName === '定额基价材料费') ||
+                    ( me.treeNode.data.subType === gljType.GENERAL_MACHINE && base.dispName === '定额基价机械费') ||
+                    ( me.treeNode.data.subType === gljType.MAIN_MATERIAL && base.dispName === '主材费') ||
+                    ( me.treeNode.data.subType === gljType.EQUIPMENT && base.dispName === '设备费')
+                ) result = me.treeNode.data.marketUnitFee ? me.treeNode.data.marketUnitFee : 0;
+
+                return result;
             };
 
-            rst = (tmpSum).toDecimal(me.digitDefault);
+            if (me.treeNode.data.type == rationType.volumePrice || me.treeNode.data.type == rationType.gljRation){
+                rst = volumePriceFee();
+            }
+            else{
+                if (isSubset(base.gljTypes, [gljType.MACHINE_LABOUR]))
+                    rst = machineLabourFee()
+                else
+                    rst = commonGLJFee();
+            }
         };
 
         return rst;
     },
     HJ: function () {
         let me = this;
-        return me.treeNode.calcBaseValue;
+        let p = me.treeNode.data.calcBaseValue ? me.treeNode.data.calcBaseValue : 0;
+        let q = me.treeNode.data.quantity ? me.treeNode.data.quantity : 1;
+        let u = (p / q).toDecimal(decimalObj.decimal('unitPrice', me.treeNode));
+        return u;
     }
 };
 
@@ -334,24 +407,24 @@ class CalcProgram {
 
     loadData (datas) {
         this.datas = datas;
+        this.compileAllTemps();
     };
 
     doAfterUpdate (err, data) {
         if(!err){
-            // do
+            $.bootstrapLoading.end();
         }
     };
 
     // 经测试,全部编译一次耗时0.003~0.004秒。耗时基本忽略不计。
     compileAllTemps(){
         let me = this;
-        me.digit = 2;
-        me.digitDefault = 6;
-
         me.compiledFeeRates = {};
         me.compiledLabourCoes = {};
         me.compiledTemplates = {};
-        me.compiledFeeTypes = {};
+        me.compiledTemplateMaps = {};
+        me.compiledTemplateNames = [];
+        me.compiledFeeTypeMaps = {};
         me.compiledFeeTypeNames = [];
         me.compiledCalcBases = {};
         me.saveForReports = [];
@@ -362,7 +435,7 @@ class CalcProgram {
         me.calcBases = rationCalcBase;
         me.templates = this.project.calcProgram.datas.templates;
 
-        me.templates.push(defaultBillTemplate);
+        // me.templates.push(defaultBillTemplate);
         // 先编译公用的基础数据
         me.compilePublics();
         for (let t of me.templates){
@@ -391,8 +464,8 @@ class CalcProgram {
         }
 
         for (let ft of me.feeTypes) {
-            me.compiledFeeTypes[ft.type] = ft.name;
-            me.compiledFeeTypes[ft.name] = ft.type;    // 中文预编译,可靠性有待验证
+            me.compiledFeeTypeMaps[ft.type] = ft.name;
+            me.compiledFeeTypeMaps[ft.name] = ft.type;    // 中文预编译,可靠性有待验证
             me.compiledFeeTypeNames.push(ft.name);
         }
 
@@ -404,6 +477,9 @@ class CalcProgram {
     compileTemplate(template){
         let me = this;
         me.compiledTemplates[template.ID] = template;
+        me.compiledTemplateMaps[template.ID] = template.name;
+        me.compiledTemplateMaps[template.name] = template.ID;
+        me.compiledTemplateNames.push(template.name);
         template.hasCompiled = false;
         template.errs = [];
 
@@ -487,7 +563,7 @@ class CalcProgram {
                 };
 
                 // 字段名映射
-                item.displayFieldName = me.compiledFeeTypes[item.fieldName];
+                item.displayFieldName = me.compiledFeeTypeMaps[item.fieldName];
             }
         };
 
@@ -513,7 +589,37 @@ class CalcProgram {
         };
     };
 
-    // 内部调用,外部不能直接使用
+    isLeafBill(treeNode){
+        let me = this;
+        return treeNode.sourceType === me.project.Bills.getSourceType() &&
+               treeNode.source.children &&
+               treeNode.source.children.length === 0;
+    };
+
+    isNullBill(treeNode){
+        let me = this;
+        return me.isLeafBill(treeNode) && (treeNode.children.length ===0) && (!treeNode.data.calcBase);
+    };
+
+    initFeeField(treeNode, fieldName){
+        if (!treeNode.data.fees) {
+            treeNode.data.fees = [];
+            treeNode.data.feesIndex = {};
+        };
+        if (!treeNode.data.feesIndex[fieldName]) {
+            let fee = {
+                'fieldName': fieldName,
+                'unitFee': 0,
+                'totalFee': 0,
+                'tenderUnitFee': 0,
+                'tenderTotalFee': 0
+            };
+            treeNode.data.fees.push(fee);
+            treeNode.data.feesIndex[fieldName] = fee;
+        };
+    };
+
+  // 仅内部调用。注意:外部不能直接使用,因为这里传入的树节点必须有一定的初始化。
     InnerCalc(treeNode){
         let me = this;
         let project = me.project;
@@ -554,58 +660,156 @@ class CalcProgram {
             };
         };
 
+        function isBaseFeeType(type){
+            return ['labour', 'material', 'machine', 'mainMaterial', 'equipment'].indexOf(type) > -1;
+        };
+
         // 汇总定额或子清单的费用类别
-        if (treeNode.calcType == treeNodeCalcType.ctGatherRations || treeNode.calcType == treeNodeCalcType.ctGatherBills){
+        if (treeNode.calcType == treeNodeCalcType.ctGatherRationsFees || treeNode.calcType == treeNodeCalcType.ctGatherBillsFees){
+            treeNode.data.programID = null;
             initFees(treeNode);
 
-            let objsArr = (treeNode.calcType == treeNodeCalcType.ctGatherRations) ? project.Ration.getRationsByNode(treeNode) : treeNode.children;
+            let objsArr = (treeNode.calcType == treeNodeCalcType.ctGatherRationsFees) ? project.Ration.getRationsByNode(treeNode) : treeNode.children;
             let rst = [];
             for (let ft of feeType) {
                 let ftObj = {};
                 ftObj.fieldName = ft.type;
                 ftObj.name = ft.name;
-                let uf = 0, tf = 0, tuf = 0, ttf = 0;
-                for (let item of objsArr) {
-                    let data = (treeNode.calcType == treeNodeCalcType.ctGatherRations) ? item : item.data;
-                    if (data.feesIndex && data.feesIndex[ft.type]) {
-                        uf = (uf + parseFloat(data.feesIndex[ft.type].unitFee)).toDecimal(me.digitDefault);
-                        tf = (tf + parseFloat(data.feesIndex[ft.type].totalFee)).toDecimal(me.digitDefault);
-                        tuf = (tuf + parseFloat(data.feesIndex[ft.type].tenderUnitFee)).toDecimal(me.digitDefault);
-                        ttf = (ttf + parseFloat(data.feesIndex[ft.type].tenderTotalFee)).toDecimal(me.digitDefault);
+                let buf = 0, btf = 0, btuf = 0, bttf = 0;
+
+                if (treeNode.calcType == treeNodeCalcType.ctGatherBillsFees){
+                    for (let item of objsArr) {
+                        let data = item.data;
+                        if (data.feesIndex && data.feesIndex[ft.type]) {
+                            buf = (buf + parseFloatPlus(data.feesIndex[ft.type].unitFee)).toDecimal(decimalObj.process);
+                            btf = (btf + parseFloatPlus(data.feesIndex[ft.type].totalFee)).toDecimal(decimalObj.process);
+                            btuf = (btuf + parseFloatPlus(data.feesIndex[ft.type].tenderUnitFee)).toDecimal(decimalObj.process);
+                            bttf = (bttf + parseFloatPlus(data.feesIndex[ft.type].tenderTotalFee)).toDecimal(decimalObj.process);
+                        };
+                    };
+                }
+                else if (treeNode.calcType == treeNodeCalcType.ctGatherRationsFees){     // 这里的算法要配合冷姐姐的神图才能看懂^_^
+                    let sum_rtf = 0, sum_rttf = 0;
+                    let bq = parseFloat(treeNode.data.quantity ? treeNode.data.quantity : 1);
+
+                    for (let data of objsArr) {
+                        let rq = parseFloat(data.quantity ? data.quantity : 0);
+
+                        let ruf = 0, rtuf = 0, rtf = 0, rttf = 0;
+                        if (data.feesIndex && data.feesIndex[ft.type]) {
+                            ruf = parseFloat(data.feesIndex[ft.type].unitFee);
+                            rtuf = parseFloat(data.feesIndex[ft.type].tenderUnitFee);
+                            rtf = parseFloat(data.feesIndex[ft.type].totalFee);
+                            rttf = parseFloat(data.feesIndex[ft.type].tenderTotalFee);
+                        };
+
+                        if (me.project.property.billsCalcMode === leafBillGetFeeType.rationContent) {
+                            buf = (buf + (ruf * rq / bq).toDecimal(decimalObj.process)).toDecimal(decimalObj.process);
+                            btuf = (btuf + (rtuf * rq / bq).toDecimal(decimalObj.process)).toDecimal(decimalObj.process);
+                        };
+
+                        sum_rtf = (sum_rtf + rtf).toDecimal(decimalObj.process);
+                        sum_rttf = (sum_rttf + rttf).toDecimal(decimalObj.process);
+                    };
+
+                    if (me.project.property.billsCalcMode === leafBillGetFeeType.rationPriceConverse || me.project.property.billsCalcMode === leafBillGetFeeType.rationPrice) {
+                        buf = (sum_rtf / bq).toDecimal(decimalObj.process);
+                        btuf = (sum_rttf / bq).toDecimal(decimalObj.process);
+                    };
+                    if (isBaseFeeType(ft.type) || (me.project.property.billsCalcMode === leafBillGetFeeType.rationPrice && ft.type == "common")){
+                        btf = sum_rtf;
+                        bttf = sum_rttf;
+                    }
+                    else{
+                        btf = (buf * bq).toDecimal(decimalObj.process);
+                        bttf = (btuf * bq).toDecimal(decimalObj.process);
                     };
                 };
-                ftObj.unitFee = uf.toDecimal(me.digit);
-                ftObj.totalFee = tf.toDecimal(me.digit);
-                ftObj.tenderUnitFee = tuf.toDecimal(me.digit);
-                ftObj.tenderTotalFee = ttf.toDecimal(me.digit);
 
+                ftObj.unitFee = buf.toDecimal(decimalObj.bills.unitPrice);
+                ftObj.totalFee = btf.toDecimal(decimalObj.bills.totalPrice);
+                ftObj.tenderUnitFee = btuf.toDecimal(decimalObj.bills.unitPrice);
+                ftObj.tenderTotalFee = bttf.toDecimal(decimalObj.bills.totalPrice);
                 checkFee(treeNode, ftObj);
 
                 rst.push(ftObj);
             };
             treeNode.data.calcTemplate = {"calcItems": rst};
         }
+        // 叶子清单的手工综合单价计算
+        else if (treeNode.calcType == treeNodeCalcType.ctCommonUnitFee){
+            delete treeNode.data.gljList;
+            if (treeNode.data.calcBase) treeNode.data.calcBase = null;  // 不能直接删除该属性,否则无法冲掉库中已存储的值
+            if (treeNode.data.calcBaseValue) treeNode.data.calcBaseValue = null;  // 不能直接删除该属性,否则无法冲掉库中已存储的值
+            if (treeNode.data.programID) treeNode.data.programID = null;
+
+            let uf = (treeNode.data.feesIndex && treeNode.data.feesIndex.common && treeNode.data.feesIndex.common.unitFee) ? treeNode.data.feesIndex.common.unitFee : 0;
+            let tuf = (treeNode.data.feesIndex && treeNode.data.feesIndex.common && treeNode.data.feesIndex.common.tenderUnitFee) ? treeNode.data.feesIndex.common.tenderUnitFee : 0;
+            let q = treeNode.data.quantity ? treeNode.data.quantity : 0;
+            let tf = (uf * q).toDecimal(decimalObj.bills.totalPrice);
+            let ttf = (tuf * q).toDecimal(decimalObj.bills.totalPrice);
+
+            delete treeNode.data.fees;    // 直接删掉再新增,不用一个个费判断更新,效率更高。
+            delete treeNode.data.feesIndex;
+            me.initFeeField(treeNode, 'common');
+            treeNode.data.feesIndex.common.unitFee = uf.toDecimal(decimalObj.bills.unitPrice);
+            treeNode.data.feesIndex.common.totalFee = tf.toDecimal(decimalObj.bills.totalPrice);
+            treeNode.data.feesIndex.common.tenderUnitFee = tuf.toDecimal(decimalObj.bills.unitPrice);
+            treeNode.data.feesIndex.common.tenderTotalFee = ttf.toDecimal(decimalObj.bills.totalPrice);
+            treeNode.changed = true;
+            treeNode.data.calcTemplate = {"calcItems": []};
+        }
+        // 叶子清单的计算基数计算
+        else if (treeNode.calcType == treeNodeCalcType.ctCalcBaseValue){
+            delete treeNode.data.gljList;
+            if (treeNode.data.programID) treeNode.data.programID = null;
+
+            let f = treeNode.data.feeRate ? treeNode.data.feeRate : 100;
+            let q = treeNode.data.quantity ? treeNode.data.quantity : 0;
+            let b = treeNode.data.calcBaseValue ? treeNode.data.calcBaseValue : 0;
+            let uf = (b * f * q / 100).toDecimal(decimalObj.bills.unitPrice);
+            let tuf = uf;
+            let tf = (me.project.property.billsCalcMode === leafBillGetFeeType.rationPrice) ? (b * f / 100).toDecimal(decimalObj.bills.totalPrice) : (uf * q).toDecimal(decimalObj.bills.totalPrice);
+            let ttf = tf;
+
+            delete treeNode.data.fees;    // 直接删掉再新增,不用一个个费判断更新,效率更高。
+            delete treeNode.data.feesIndex;
+            me.initFeeField(treeNode, 'common');
+            treeNode.data.feesIndex.common.unitFee = uf;
+            treeNode.data.feesIndex.common.totalFee = tf;
+            treeNode.data.feesIndex.common.tenderUnitFee = tuf;
+            treeNode.data.feesIndex.common.tenderTotalFee = ttf;
+            treeNode.changed = true;
+            treeNode.data.calcTemplate = {"calcItems": []};
+        }
+        // 定额或清单自己的计算程序计算
         else{
-            // 叶子清单的缺省计算程序需要提供总金额作为计算基数(不需要工料机),然后每条按比例(费率)计算,不需要工料机明细。
-            if (treeNode.calcType == treeNodeCalcType.ctCalcBaseValue){
-                delete treeNode.data.gljList;
+            if (treeNode.calcType == treeNodeCalcType.ctRationCalcProgram) {
+                if (treeNode.data.type == rationType.volumePrice){
+                    delete treeNode.data.gljList;
+                    let muf = treeNode.data.marketUnitFee ? treeNode.data.marketUnitFee : 0;
+                    let q = treeNode.data.quantity ? treeNode.data.quantity : 0;
+                    treeNode.data.marketTotalFee = (muf * q).toDecimal(decimalObj.ration.totalPrice);
+                }
+                else if (treeNode.data.type == rationType.gljRation){
 
-                if (treeNode.data.programID == undefined){
-                    treeNode.data.programID = defaultBillTemplate.ID;
-                };
-            }
-            else {
-                if (treeNode.sourceType === project.Ration.getSourceType()) {
-                    treeNode.data.gljList = project.ration_glj.getGljArrByRation(treeNode.data.ID);
                 }
-                else if (treeNode.sourceType === project.Bills.getSourceType()) {
-                    let rations = project.Ration.getBillsSortRation(treeNode.source.getID());
-                    treeNode.data.gljList = project.ration_glj.getGatherGljArrByRations(rations);
+                else{
+                    treeNode.data.gljList = me.project.ration_glj.getGljArrByRation(treeNode.data.ID);
                 };
 
                 if (treeNode.data.programID == undefined){
-                    treeNode.data.programID = 1;
+                    treeNode.data.programID = projectInfoObj.projectInfo.property.engineering;
                 };
+            }
+            else if (treeNode.calcType == treeNodeCalcType.ctBillCalcProgram) {
+                let rations = project.Ration.getBillsSortRation(treeNode.source.getID());
+                treeNode.data.gljList = project.ration_glj.getGatherGljArrByRations(rations);
+
+                // if (treeNode.data.programID == undefined || treeNode.data.programID == defaultBillTemplate.ID){
+                if (treeNode.data.programID == undefined){
+                    treeNode.data.programID = projectInfoObj.projectInfo.property.engineering;
+                }
             };
 
             let template = me.compiledTemplates[treeNode.data.programID];
@@ -624,55 +828,46 @@ class CalcProgram {
 
                     let feeRate = calcItem.feeRate;
                     if (!feeRate) feeRate = 100;    // 100%
-                    calcItem.unitFee = (eval(calcItem.compiledExpr) * feeRate * 0.01).toDecimal(me.digit);   // 如果eval()对清单树有影响,就换成小麦的Expression对象再试
+                    calcItem.unitFee = (eval(calcItem.compiledExpr) * feeRate * 0.01).toDecimal(decimalObj.decimal('unitPrice', treeNode));   // 如果eval()对清单树有影响,就换成小麦的Expression对象再试
 
                     let quantity = treeNode.data.quantity;
                     if (!quantity) quantity = 0;
-                    calcItem.totalFee = (calcItem.unitFee * quantity).toDecimal(me.digit);
+                    calcItem.totalFee = (calcItem.unitFee * quantity).toDecimal(decimalObj.decimal('totalPrice', treeNode));
 
                     checkFee(treeNode, calcItem);
                 };
             }
         };
-    }
+    };
 
     // 计算本节点(默认同时递归计算所有父节点,可选)
     calculate(treeNode, calcParents = true){
         let me = this;
-
         let isRation = treeNode.sourceType === me.project.Ration.getSourceType();
         let isBill = treeNode.sourceType === me.project.Bills.getSourceType();
-        let isLeafBill = isBill && treeNode.source.children && treeNode.source.children.length === 0;
-        let isBillPriceCalc = me.project.projSetting.billsCalcMode === billsPrice;
 
-        if (isRation)
-            treeNode.calcType = treeNodeCalcType.ctRationCalcProgram
-        else if (isLeafBill) {
+        if (isRation){
+            treeNode.calcType = treeNodeCalcType.ctRationCalcProgram;
+        }
+        else  if (me.isNullBill(treeNode)){
+            treeNode.calcType = treeNodeCalcType.ctCommonUnitFee;
+        }
+        else if (me.isLeafBill(treeNode)) {
             if (treeNode.children && treeNode.children.length > 0){
-                if (treeNode.children[0].sourceType == me.project.Ration.getSourceType()){
-                    if (isBillPriceCalc)                   // 清单单价计算模式下的叶子清单:取自己的计算程序ID,找到自己的计算程序计算
-                        treeNode.calcType = treeNodeCalcType.ctBillCalcProgram;
-                    else                                  // 前三种计算模式下的叶子清单:汇总定额的计算程序的费用类别
-                        treeNode.calcType = treeNodeCalcType.ctGatherRations;
-                }
-                else if (treeNode.children[0].sourceType == me.project.VolumePrice.getSourceType()){
-                    let value = 20000;
-                    // if (treeNode.data.feesIndex && treeNode.data.feesIndex.common && treeNode.data.feesIndex.common.unitFee != 0)
-                    //     value = treeNode.data.feesIndex.common.unitFee;
-                    treeNode.calcType = treeNodeCalcType.ctCalcBaseValue;
-                    treeNode.calcBaseValue = value;
-                };
+                // me.calcLeafBillChildren(treeNode);
+
+                // 清单单价计算模式下的叶子清单:取自己的计算程序ID,找到自己的计算程序计算。(汇总清单所有定额的工料机)
+                if (me.project.property.billsCalcMode === leafBillGetFeeType.billsPrice)
+                    treeNode.calcType = treeNodeCalcType.ctBillCalcProgram;
+                else                                        // 前三种计算模式下的叶子清单:汇总定额的计算程序的费用类别
+                    treeNode.calcType = treeNodeCalcType.ctGatherRationsFees;
             }
             else{                                          // 公式计算
-                let value = 20000;
-                // if (treeNode.data.feesIndex && treeNode.data.feesIndex.common && treeNode.data.feesIndex.common.unitFee != 0)
-                //     value = treeNode.data.feesIndex.common.unitFee;
                 treeNode.calcType = treeNodeCalcType.ctCalcBaseValue;
-                treeNode.calcBaseValue = value;
             };
         }
         else if (isBill)                                 // 父清单:汇总子清单的费用类别
-            treeNode.calcType = treeNodeCalcType.ctGatherBills;
+            treeNode.calcType = treeNodeCalcType.ctGatherBillsFees;
 
         me.InnerCalc(treeNode);
 
@@ -686,29 +881,93 @@ class CalcProgram {
     saveNode(treeNode, saveParents = true) {
         if (!treeNode.changed) return;
         let me = this;
-        let newDataArr = [], nodesArr = [];
-
-        me.project.beginUpdate('');
+        let nodesArr = [];
         let curNode = treeNode;
         while (curNode) {
-            if (curNode.changed){
+            if (curNode.changed){nodesArr.push(curNode)};
+            if (saveParents) curNode = curNode.parent
+            else break;
+        };
+        me.saveNodes(nodesArr);
+    };
+
+    // 多个树结点入库存储,刷新界面显示。
+    saveNodes(treeNodes){
+        if (treeNodes.length < 1) return;
+
+        let me = this;
+
+        me.project.beginUpdate('');
+        for (let node of treeNodes){
+            if (node.changed){
                 let data = {
-                    ID: curNode.data.ID,
+                    ID: node.data.ID,
                     projectID: me.project.ID(),
-                    quantity: curNode.data.quantity,
-                    fees: curNode.data.fees
+                    /*  subType、quantity、calcBase、programID、marketUnitFee等等字段较为特殊,它们的改变一定会触发计算并导致计算
+                    结果的变化,从而引发保存动作。将这些字段放在该位置跟计算结果一起保存,可减少前端跟后端的通讯频率。              */
+                    subType: node.data.subType,
+                    quantity: node.data.quantity,
+                    calcBase: node.data.calcBase,
+                    calcBaseValue: node.data.calcBaseValue,
+                    programID: node.data.programID,
+                    marketUnitFee: node.data.marketUnitFee,
+                    marketTotalFee: node.data.marketTotalFee,
+                    fees: node.data.fees,
+                    isFromDetail:node.data.isFromDetail,
+                    feeRate: node.data.feeRate,
+                    feeRateID: node.data.feeRateID
                 };
-                let newDta = {'updateType': 'ut_update', 'updateData': data};
-                newDataArr.push(newDta);
-                nodesArr.push(curNode);
-                me.project.push(curNode.sourceType, newDataArr);
-            };
-            if (saveParents) curNode = curNode.parent
-            else break;
+                let newData = {'updateType': 'ut_update', 'updateData': data};
+                me.project.push(node.sourceType, [newData]);
+            }
         };
         me.project.endUpdate();
 
-        for (let node of nodesArr){delete node.changed;};
-        projectObj.mainController.refreshTreeNode(nodesArr);
+        for (let node of treeNodes){delete node.changed};
+        projectObj.mainController.refreshTreeNode(treeNodes);
+
+        if (activeSubSheetIs(subSheetIndex.ssiCalcProgram)) {
+            calcProgramObj.showData(me.project.mainTree.selected, false);
+        };
+    };
+
+/*    计算所有树结点(分3种情况),并将发生计算改动的结点入库存储。
+    参数取值如下:
+    calcAllType.catAll       计算所有树结点 (不指定参数时的默认值)
+    calcAllType.catBills     计算所有清单 (改变项目属性中清单取费算法时会用到)
+    calcAllType.catRations   计算所有定额、工料机形式的定额、量价,因为它们都走自己的计算程序 (改变人工系数、费率值、工料机单价时会用到) */
+    calcAllNodes(calcType = calcAllType.catAll){
+        let me = this;
+        let needSaveNodes = [];
+
+        function calcNodes(nodes) {
+            for (let node of nodes) {
+                if (node.children.length > 0) {
+                    calcNodes(node.children);
+                };
+
+                if ((calcType == calcAllType.catAll) || (calcType == node.sourceType)) {
+                    me.calculate(node, false);
+                    if (node.changed) needSaveNodes.push(node);
+                };
+            }
+        };
+
+        calcNodes(me.project.mainTree.roots);
+        me.saveNodes(needSaveNodes);
+    };
+
+    // 重新计算叶子清单下的所有子结点:如定额、工料机定额等(calculate算法基于定额、工料机定额的计算结果是正确的,实际上有时它们的计算结果并不是最新的)
+    calcLeafBillChildren(treeNode){
+        let me = this;
+        if(!me.isLeafBill(treeNode)) return;
+        if (treeNode.children && treeNode.children.length > 0) {
+            let needSaveNodes = [];
+            for (let child of treeNode.children){
+                me.calculate(child, false);
+                if (child.changed) needSaveNodes.push(child);
+            };
+            me.saveNodes(needSaveNodes);
+        };
     };
 }

+ 36 - 21
web/building_saas/main/js/models/fee_rate.js

@@ -50,6 +50,7 @@ var FeeRate = {
             }
         };
         FeeRate.prototype.getFeeRateByID=function (ID) {
+            ID=parseInt(ID);
             var rates = this.getActivateFeeRate().rates;
             return _.find(rates,{'ID':ID})
         };
@@ -123,12 +124,13 @@ var FeeRate = {
                 rateIndex:params.sourceIndex,
                 rate:params.dataItem
             }
-            doc.rate.rate =doc.rate.rate.toDecimal(feeRate_consts.decimal);
+            doc.rate.rate =doc.rate.rate.toDecimal(getDecimal("feeRate"));
             this.updateFeeRate(query,doc);
             if(this.ifRateChange(params)){
                 //this.synchronizeFeeRate();
                 this.onFeeRateChange(params.dataItem.ID,params.dataItem.rate);
             }
+            $.bootstrapLoading.end();
         };
 
         FeeRate.prototype.batchUpdateFeeRate = function (items,feerate) {
@@ -190,6 +192,7 @@ var FeeRate = {
                     calcProgramObj.showData(node);
                 }
             }
+            project.calcProgram.calcAllNodes(calcAllType.catBills);
             socket.emit('feeRateChangeNotify', this.getActivateFeeRateFileID());
         };
         FeeRate.prototype.onFeeRateFileChange=function () {
@@ -201,6 +204,7 @@ var FeeRate = {
                     calcProgramObj.showData(node);
                 }
             }
+            project.calcProgram.calcAllNodes(calcAllType.catBills);
             socket.emit('feeRateChangeNotify', this.getActivateFeeRateFileID());
         };
 
@@ -211,7 +215,7 @@ var FeeRate = {
                     if(n.data.hasOwnProperty("feeRateID")&&n.data.feeRateID){
                         var rate = me.getFeeRateByID(n.data.feeRateID);
                         if(rate){
-                            n.data.feeRate=number_util.roundToString(rate.rate,feeRate_consts.decimal);
+                            n.data.feeRate=number_util.roundToString(rate.rate,getDecimal("feeRate"));
                             return true;
                         }else {
                             n.data.feeRate=null;
@@ -256,7 +260,7 @@ var FeeRate = {
         FeeRate.prototype.refreshBillsByRateID=function(rateID,value){
             var nodes = _.filter(projectObj.project.mainTree.items,function (n) {
                 if(n.sourceType==ModuleNames.bills&&n.data.feeRateID==rateID){
-                    n.data.feeRate=number_util.roundToString(value,feeRate_consts.decimal);
+                    n.data.feeRate=number_util.roundToString(value,getDecimal("feeRate"));
                     return true;
                 }else {
                     return false;
@@ -338,19 +342,23 @@ var FeeRate = {
         FeeRate.prototype.updateFeeRateFromBills=function(value,node){
             var me =this;
             if(node.sourceType === project.Bills.getSourceType()){
-                var value= number_util.checkNumberValue(value,feeRate_consts.decimal);
-                if(value){
+                var fee_value= number_util.checkNumberValue(value,getDecimal("feeRate"));
+                if(fee_value!=null){
                     var bill = node.data;
                     var rate =me.getFeeRateByID(bill.feeRateID);
-                    var data=me.getfbUpdateData(rate,bill,value);
+                    var data=me.getfbUpdateData(rate,bill,fee_value,value);
+                    if(data==null){//只更改清单的值的情况下,由计算程序更新
+                        project.calcProgram.calculate(node);
+                        project.calcProgram.saveNode(node);
+                    }
                     this.setFeeRateToBill(data,function (result) {
                         if(data.hasOwnProperty('feeRate')){
-                            rate.rate=value;
-                            me.onFeeRateChange(rate.ID,value);
-                        }else {
+                            rate.rate=fee_value;
+                            me.onFeeRateChange(rate.ID,fee_value);
+                        }/*else {
                             bill.feeRate=value;
                             projectObj.mainController.refreshTreeNode([node])
-                        }
+                        }*/
                     });
                 }else {
                     projectObj.mainController.refreshTreeNode([node]);
@@ -359,7 +367,7 @@ var FeeRate = {
         };
 
         FeeRate.prototype.updateFeeRateFromCalc=function (value,editInfo) {
-            var value= number_util.checkNumberValue(value,feeRate_consts.decimal);
+            var value= number_util.checkNumberValue(value,getDecimal("feeRate"));
             if(value){
                 if(editInfo.calcItem.feeRateID){
                     var rate = projectObj.project.FeeRate.getFeeRateByID(editInfo.calcItem.feeRateID);
@@ -396,9 +404,10 @@ var FeeRate = {
             });
         }
 
-        FeeRate.prototype.getfbUpdateData=function (rate,bill,value) {
-            var data={};
-            if(bill.feeRateID){
+        FeeRate.prototype.getfbUpdateData=function (rate,bill,value,editText) {
+            var data=null;
+            if(bill.feeRateID&&editText!=null){
+                data = {};
                 data.feeRate={
                     query:{
                         'ID':this.getActivateFeeRateID(),
@@ -408,8 +417,12 @@ var FeeRate = {
                         'rates.$.rate':value
                     }
                 }
-            }else {
-                data.bills={
+            }else  if(editText==null){
+                bill.feeRateID = null;
+                bill.feeRate =null;
+            }else { //这里只改变当前清单的费率值,不在这里提交后台,交给计算程序处理。
+                bill["feeRate"]=value
+              /*  data.bills={
                     query:{
                         ID:bill.ID,
                         projectID:bill.projectID,
@@ -418,7 +431,7 @@ var FeeRate = {
                     doc:{
                         feeRate:value
                     }
-                }
+                }*/
             }
             return data;
         };
@@ -462,15 +475,17 @@ var FeeRate = {
        
 
         FeeRate.prototype.setFeeRateToBill=function(data,callback){
-            CommonAjax.post('/feeRates/setFeeRateToBill', data, function (data) {
-                callback(data);
-            });
+            if(data){
+                CommonAjax.post('/feeRates/setFeeRateToBill', data, function (data) {
+                    callback(data);
+                });
+            }
         };
         FeeRate.prototype.loadFeeRateToBill=function (node) {
             if(node.data.feeRateID){
                 var feeRate = this.getFeeRateByID(node.data.feeRateID);
                 if(feeRate){
-                    node.data.feeRate=number_util.roundToString(feeRate.rate,feeRate_consts.decimal);// parseFloat(feeRate.rate).toFixed(feeRate_consts.decimal);
+                    node.data.feeRate=number_util.roundToString(feeRate.rate,getDecimal("feeRate"));// parseFloat(feeRate.rate).toFixed(feeRate_consts.decimal);
                 }
             }
         };

+ 99 - 10
web/building_saas/main/js/models/main_consts.js

@@ -11,16 +11,43 @@ const ModuleNames = {
     ration_coe:'ration_coe',
     ration_ass:'ration_ass',
     quantity_detail:'quantity_detail',
-    volume_price: 'volume_price',
-    projectGLJ: 'project_glj',
+    // volume_price: 'volume_price',
     labour_coe: 'labour_coe',
     calc_program: 'calc_program'
 };
 
-const feeRate_consts={
-    decimal:3
+const gljType = {
+    // 人工
+    LABOUR: 1,
+    // ==============材料类型=================
+    // 普通材料
+    GENERAL_MATERIAL: 201,
+    // 混凝土
+    CONCRETE: 202,
+    // 砂浆
+    MORTAR: 203,
+    // 配合比
+    MIX_RATIO: 204,
+    // 商品混凝土
+    COMMERCIAL_CONCRETE: 205,
+    // 商品砂浆
+    COMMERCIAL_MORTAR: 206,
+    // ==============材料类型=================
+    // ==============机械类型=================
+    // 普通机械
+    GENERAL_MACHINE: 301,
+    // 机械组成物
+    MACHINE_COMPOSITION: 302,
+    // 机上人工
+    MACHINE_LABOUR: 303,
+    // ==============机械类型=================
+    // 主材
+    MAIN_MATERIAL: 4,
+    // 设备
+    EQUIPMENT: 5
 };
 
+
 const CP_Col_Width = {          // 多处计算程序界面的列宽统一设置
     rowHeader: 30,
     colHeader: 30,              // 这个是标题栏高度不是宽度,也写在一起
@@ -36,10 +63,72 @@ const CP_Col_Width = {          // 多处计算程序界面的列宽统一设置
     totalFee: 90
 };
 
-treeNodeCalcType = {
+const treeNodeCalcType = {
     ctRationCalcProgram: 1,
-    ctBillCalcProgram: 2,
-    ctGatherRations: 3,
-    ctGatherBills: 4,
-    ctCalcBaseValue: 5
-};
+    ctBillCalcProgram: 2,       // 汇总清单下所有定额的工料机
+    ctGatherRationsFees: 3,     // 汇总定额的各个费
+    ctGatherBillsFees: 4,       // 汇总清单的各个费
+    ctCalcBaseValue: 5,
+    ctCommonUnitFee: 6
+};
+
+const calcAllType = {
+    catAll: 'all',
+    catBills: 'bills',
+    catRations: 'ration'
+};
+
+const subSheetIndex = {
+    ssiRationGLJ: 0,
+    ssiRationCoe: 1,
+    ssiRationAssistant: 2,
+    ssiQuantityDetail: 3,
+    ssiCalcProgram: 4,
+    ssiMemo: 5,
+    ssiFeature: 6
+};
+
+const volumePriceMaps = {
+    "量人": gljType.LABOUR,
+    "量材": gljType.GENERAL_MATERIAL,
+    "量机": gljType.GENERAL_MACHINE,
+    "量主": gljType.MAIN_MATERIAL,
+    "量设": gljType.EQUIPMENT,
+
+    "人工": gljType.LABOUR,
+    "材料": gljType.GENERAL_MATERIAL,
+    "机械": gljType.GENERAL_MACHINE,
+    "主材": gljType.MAIN_MATERIAL,
+    "设备": gljType.EQUIPMENT,
+
+    1: "量人",
+    201: "量材",
+    301: "量机",
+    4: "量主",
+    5: "量设"
+};
+
+const gljTypeMap = {
+   201:'材',
+   4:'主',
+   5:'设'
+}
+
+
+const rationType = {
+    ration: 1,
+    volumePrice: 2,
+    gljRation: 3
+};
+
+const leafBillGetFeeType = {
+    rationContent: 0,
+    rationPriceConverse: 1,
+    rationPrice: 2,
+    billsPrice: 3
+};
+
+const zanguCalcType = {
+    common: 0,
+    gatherMaterial: 1
+};

+ 38 - 22
web/building_saas/main/js/models/project.js

@@ -39,6 +39,8 @@ var PROJECT = {
                 } else if (item.moduleName === me.projSetting) {
                     me._project.projSetting = item.data;
                     me._project.projSetting.moduleName = me.projSetting;
+                }else if(item.moduleName === ModuleNames.projectGLJ){
+                    me._project.projectGLJ.loadToCache(item.data);
                 }
             });
             for (module in counter) {
@@ -46,6 +48,7 @@ var PROJECT = {
                     me.modules[module].setMaxID(counter[module]);
                 }
             }
+
             me._project.loadMainTree();
             //me.test(result[0].data[0]);
             callback(0);
@@ -77,22 +80,17 @@ var PROJECT = {
             this.ration_ass = ration_ass.createNew(this);
             this.quantity_detail = quantity_detail.createNew(this);
             this.FeeRate = FeeRate.createNew(this);
-            this.VolumePrice = VolumePrice.createNew(this);
+            // this.VolumePrice = VolumePrice.createNew(this);
             this.projectGLJ = new ProjectGLJ();
-            this.projectGLJ.loadData();
+           // this.projectGLJ.loadData();
             this.composition = new Composition();
             this.composition.loadData();
-            this.Decimal = {
-                common: {
-                    quantity: 4,
-                    unitFee: 2,
-                    totalFee: 2
-                }
-            };
             this.labourCoe = new LabourCoe(this);
             this.calcProgram = new CalcProgram(this);
+            this.calcBase = calcBase;
 
-            this.masterField = {ration: 'billsItemID', volumePrice: 'billsItemID'};
+            // this.masterField = {ration: 'billsItemID', volumePrice: 'billsItemID'};
+            this.masterField = {ration: 'billsItemID'};
         };
 
         // prototype用于定义public方法
@@ -126,6 +124,17 @@ var PROJECT = {
 
         project.prototype.loadMainTree = function () {
             var that = this;
+            let loadRationGLJNode = function (cacheNode) {
+                var newNode, bj = that.ration_glj.getMainAndEquGLJ(cacheNode.source.ID), i;
+                for(i=0;i<bj.length;i++){
+                    that.ration_glj.transferToNodeData(bj[i]);
+                    newNode = that.mainTree.addNode(cacheNode);
+                    newNode.source = bj[i];
+                    newNode.sourceType = that.ration_glj.getSourceType();
+                    newNode.data = bj[i];
+                }
+            };
+
             var loadRationNode = function (rations, cacheNode) {
                 var newNode, br = that.Ration.getBillsSortRation(cacheNode.source.getID()), i;
                 for (i = 0; i < br.length; i++) {
@@ -133,9 +142,12 @@ var PROJECT = {
                     newNode.source = br[i];
                     newNode.sourceType = that.Ration.getSourceType();
                     newNode.data = br[i];
+                    if(projectInfoObj.projectInfo.property.displaySetting.disPlayMainMateria==true){
+                        loadRationGLJNode(newNode);
+                    }
                 }
             };
-            let loadVolumePriceNode = function (cacheNode) {
+/*            let loadVolumePriceNode = function (cacheNode) {
                 let newNode = null, bv = that.VolumePrice.getBillsSortVolumePrice(cacheNode.source.getID());
                 for (let v of bv) {
                     newNode = that.mainTree.addNode(cacheNode);
@@ -143,7 +155,8 @@ var PROJECT = {
                     newNode.sourceType = that.VolumePrice.getSourceType();
                     newNode.data = v;
                 }
-            };
+            };*/
+
             var loadIdTreeNode = function (nodes, parent) {
                 var newNode, i;
                 for (i = 0; i < nodes.length; i++) {
@@ -154,7 +167,7 @@ var PROJECT = {
                     that.FeeRate.loadFeeRateToBill(newNode);
                     if (nodes[i].children.length === 0) {
                         loadRationNode(that.Ration.datas, newNode);
-                        loadVolumePriceNode(newNode);
+                        // loadVolumePriceNode(newNode);
                     } else {
                         loadIdTreeNode(nodes[i].children, newNode);
                     }
@@ -292,22 +305,24 @@ var PROJECT = {
             }
         };
 
-        project.prototype.setBillsCalcMode = function (calcMode) {
-            this.projSetting.billsCalcMode = calcMode;
+/*        project.prototype.setBillsCalcMode = function (calcMode) {
+            this.property.billsCalcMode = calcMode;
             this.initCalcFields();
-        };
+        };*/
 
-        project.prototype.initCalcFields = function () {
-            let settingConst = this.projSetting.settingConst;
+        /*project.prototype.initCalcFields = function () {
+            // let settingConst = this.projSetting.settingConst;
             if (this.calcFields) {
                 for (let field of this.calcFields) {
                     // unitFeeCalcFlag
                     if (field.type === 'zangu') {
                         field.unitFeeFlag = converseUnitFeeFlag;
                     } else {   
-                        if (this.projSetting.billsCalcMode === settingConst.billsCalcMode.rationContent) {
+                        // if (this.projSetting.billsCalcMode === settingConst.billsCalcMode.rationContent) {
+                        if (this.property.billsCalcMode === leafBillGetFeeType.rationContent) {
                             field.unitFeeFlag = rationContentUnitFeeFlag;
-                        } else if ( this.projSetting.billsCalcMode === settingConst.billsCalcMode.billsPrice) {
+                        // } else if ( this.projSetting.billsCalcMode === settingConst.billsCalcMode.billsPrice) {
+                        } else if ( this.property.billsCalcMode === leafBillGetFeeType.billsPrice) {
                             field.unitFeeFlag = billsPriceUnitFeeFlag;
                         } else {
                             field.unitFeeFlag = averageQtyUnitFeeFlag;
@@ -315,7 +330,8 @@ var PROJECT = {
                     }
                     // totalFeeCalcFlag
                     if (field.type === 'common') {
-                        if (this.projSetting.billsCalcMode === settingConst.billsCalcMode.rationPriceConverse) {
+                        // if (this.projSetting.billsCalcMode === settingConst.billsCalcMode.rationPriceConverse) {
+                        if (this.property.billsCalcMode === leafBillGetFeeType.rationPriceConverse) {
                             field.totalFeeFlag = sumTotalFeeFlag;
                         } else {
                             field.totalFeeFlag = totalFeeFlag;
@@ -325,7 +341,7 @@ var PROJECT = {
                     }
                 }
             }
-        }
+        }*/
 
         return new project();
     }

+ 23 - 2
web/building_saas/main/js/models/project_glj.js

@@ -51,6 +51,12 @@ ProjectGLJ.prototype.loadData = function (callback = null) {
     });
 };
 
+ProjectGLJ.prototype.loadToCache = function (data) {
+    this.datas = data;
+    projectObj.project.projectGLJ=this;
+}
+
+
 /**
  * 获取对应工料机数据
  *
@@ -180,7 +186,7 @@ ProjectGLJ.prototype.refreshRationGLJPrice=function (glj) {
         if(ration_glj.projectGLJID ==glj.id){
             ration_glj.basePrice=glj.unit_price.base_price;
             ration_glj.marketPrice=glj.unit_price.market_price;
-            ration_glj.adjustPrice=glj.adjust_price;
+            ration_glj.adjustPrice=this.getAdjustPrice(glj);
         }
     }
 
@@ -212,7 +218,8 @@ ProjectGLJ.prototype.setAdjustPrice=function(glj){
     switch (glj.unit_price.type + '') {
         // 人工: 调整基价=基价单价*调整系数
         case GLJTypeConst.LABOUR:
-            glj.adjust_price = scMathUtil.roundTo(parseFloat(glj.adjustment * glj.unit_price.base_price), -2);
+        case GLJTypeConst.MACHINE_LABOUR:
+            glj.adjust_price = this.getAdjustPrice(glj);
             break;
         // 机械类型的算法
         case GLJTypeConst.MACHINE:
@@ -222,4 +229,18 @@ ProjectGLJ.prototype.setAdjustPrice=function(glj){
         default:
             glj.adjust_price = glj.unit_price.base_price;
     }
+}
+
+ProjectGLJ.prototype.getAdjustPrice = function (glj) {
+    GLJTypeConst = this.datas.constData.GLJTypeConst !== undefined ? JSON.parse(this.datas.constData.GLJTypeConst) : GLJTypeConst;
+    if(glj.unit_price.type==GLJTypeConst.LABOUR||glj.unit_price.type==GLJTypeConst.MACHINE_LABOUR){
+        let labour = projectObj.project.calcProgram.compiledLabourCoes[glj.adjCoe];
+        //let labour=1;
+        let coe = labour&&labour.coe?labour.coe:1;
+        let decimal = getDecimal("glj.unitPrice");
+        return scMathUtil.roundTo(parseFloat(coe*glj.unit_price.base_price),-decimal);
+    }else {
+        return glj.unit_price.base_price
+    }
+
 }

+ 106 - 68
web/building_saas/main/js/models/quantity_detail.js

@@ -41,20 +41,16 @@ var quantity_detail = {
             }
         };
         quantity_detail.prototype.refreshAfterSave=function(data){
-            console.log(data);
             var me = this;
             if(data.hasOwnProperty('resort')){
                 this.resortData(data.doc,1);
                 _.forEach(data.update_task,function (item) {
                     me.refreshEachItme(item.query,item.doc);
                 })
-                gljOprObj.detailData.push(data.doc);
                 this.datas.push(data.doc);
             }else {
                 this.datas.push(data);
-                gljOprObj.detailData.push(data);
             }
-            gljOprObj.detailData=_.sortBy(gljOprObj.detailData,'seq');
             this.refreshSheetData();
         };
         quantity_detail.prototype.resortData=function(data,req){
@@ -106,30 +102,15 @@ var quantity_detail = {
                 me.refreshEachItme(item.query,item.doc);
             });
             _.remove(this.datas,{ID:data.doc.ID});
-            _.remove(gljOprObj.detailData,{ID:data.doc.ID});
             this.refreshSheetData();
         };
         quantity_detail.prototype.refreshSheetData=function () {
-            sheetCommonObj.showData(gljOprObj.detailSheet,gljOprObj.detailSetting,gljOprObj.detailData);
-        };
-        quantity_detail.prototype.getUpdateData=function(type,query,doc,callfunction){
-            var updateData = [];
-            var newobj = {
-                'updateType': type,
-                'query': query,
-            }
-            if(doc){
-                newobj['doc']=doc;
-            }
-            if(callfunction){
-                newobj['updateFunction']=callfunction;
-            }
-            updateData.push(newobj);
-            return updateData;
+            gljOprObj.showQuantityDetailData();
         };
-        quantity_detail.prototype.saveQuantityDetail=function (args,dataCode) {
+        quantity_detail.prototype.saveQuantityDetail=function (args,dataCode,selected) {
+            var me = this;
             var doc={};
-            var selected = projectObj.project.mainTree.selected;
+            var selected = selected?selected:projectObj.project.mainTree.selected;
             if(selected.sourceType==ModuleNames.ration){
                 doc.rationID=selected.data.ID;
             }
@@ -141,6 +122,7 @@ var quantity_detail = {
             doc.seq=args.row;
             if(dataCode=='regex'){
                 if(!this.regexChecking(args.editingText)||!this.referenceChecking(args.editingText,args.row,doc)){
+                    gljOprObj.showQuantityDetailData();
                     return;
                 }
                 doc.refreshQuantity=true;
@@ -152,13 +134,27 @@ var quantity_detail = {
                 }
 
             }
-            var updateData
-            if(args.hasOwnProperty("insertRecode")){
-                updateData = this.getUpdateData('ut_update',null,doc,'insertRecode');
+            var url="";
+            $.bootstrapLoading.start();
+            if(args.hasOwnProperty("insertRecode")){//右键插入或者是通过直接编辑保存
+                url = "/quantity_detail/insertRecode";
             }else{
-                updateData = this.getUpdateData('ut_create',null,doc);
+                url = "/quantity_detail/save";
+            }
+            var callback = function (data) {
+                if(doc.refreshQuantity==false){//清空数据
+                    me.cleanQuantityDetail();
+                }else {
+                    data.newRecord?me.refreshAfterSave(data.newRecord):me.refreshAfterSave(data);
+                    data.node?gljOprObj.refreshTreeNode(data.node):"";
+                    //gljOprObj.detailSheet.setActiveCell(0,0);
+                    //gljOprObj.detailSheet.clearSelection();
+                }
+                $.bootstrapLoading.end();
             }
-            project.pushNow('saveQuantityDetail',[this.getSourceType()],updateData);
+            CommonAjax.post(url,doc,callback,function () {
+                $.bootstrapLoading.end();
+            });
         };
         quantity_detail.prototype.insertQuantityDetail = function (row) {
             var args = {
@@ -169,20 +165,27 @@ var quantity_detail = {
                 args.insertRecode = true;
             }
             this.saveQuantityDetail(args,'isSummation');
-
         };
 
         quantity_detail.prototype.deleteQuantityDetail = function (row) {
+           var me = this;
            var deleteable = this.checkReference(row);
            if(deleteable){
                var recode = gljOprObj.detailData[row];
-               var updateData = this.getUpdateData('ut_delete',null,recode);
-               project.pushNow('deleteQuantityDetail',[this.getSourceType()],updateData);
+               $.bootstrapLoading.start();
+               var callback=function (result) {
+                   me.refreshAfterDelete(result.data);
+                   result.node?gljOprObj.refreshTreeNode(result.node):"";
+                   $.bootstrapLoading.end();
+               }
+               CommonAjax.post("/quantity_detail/deleteRecode",recode,callback,function () {
+                   $.bootstrapLoading.end();
+               });
            }else {
                alert("当前行已被引用,不可删除。");
            }
-
         };
+
         quantity_detail.prototype.checkReference = function (row) {
             var deleteable = true;
            for(var i =0;i<gljOprObj.detailData.length;i++){
@@ -231,25 +234,19 @@ var quantity_detail = {
                     update_task.push({query:{ID:item.ID,projectID:item.projectID},doc:{regex:regex,referenceIndexs:item.referenceIndexs}});
                 }
             })
-
-            var updateData=[];
-            update_task.forEach(function (task) {
-                updateData.push({'updateType': 'ut_update', 'query': task.query,'doc':task.doc});
-            })
-            project.pushNow('updateQuantityDetail',[this.getSourceType()],[updateData]);
-
+            me.commonUpdate("/quantity_detail/swapRow",update_task);
         };
         quantity_detail.prototype.replaceAll=function(FindText, RepText,str) {
             let regExp = new RegExp(FindText, "g");
             return str.replace(regExp, RepText);
         };
-        quantity_detail.prototype.updateQuantityDetail=function (args,dataCode,recode) {
+        quantity_detail.prototype.updateQuantityDetail=function (args,dataCode,recode,selected) {
             var doc ={};
             var query={
                 ID:recode.ID,
                 projectID:recode.projectID
             };
-            var selected = projectObj.project.mainTree.selected;
+            var selected = selected?selected:projectObj.project.mainTree.selected;
             doc[dataCode]=args.editingText;
             if (dataCode == 'regex') {
                 if(recode.hasOwnProperty('rationID')){
@@ -261,7 +258,9 @@ var quantity_detail = {
                 if(!selected.data.hasOwnProperty('isFromDetail')||selected.data.isFromDetail==0){
                     var c = confirm("确定要使用工程量明细替换原工程量吗?");
                     if(!c){
-                        query.refreshQuantity=false;
+                        //query.refreshQuantity=false;
+                        this.cleanQuantityDetail(selected,true);
+                        return;
                     }
                 }
                 query.index = args.row;
@@ -281,8 +280,7 @@ var quantity_detail = {
                 }
             }
             if(needupdate){
-                var updateData = this.getUpdateData('ut_update',query,doc,'updateQuantityRegex');
-                project.pushNow('updateQuantityDetail',[this.getSourceType()],updateData);
+                this.commonUpdate("/quantity_detail/updateRegex",{query:query,doc:doc});
             }else {
                 var sheet = subSpread.getActiveSheet();
                 sheet.getCell(args.row,args.col).value(gljOprObj.detailData[args.row].regex);
@@ -312,9 +310,21 @@ var quantity_detail = {
             };
             this.normalUpdate(query,doc);
         };
+        quantity_detail.prototype.commonUpdate = function (url,postData) {
+            var me = this;
+            $.bootstrapLoading.start();
+            var callback = function (data) {
+                me.refreshAfterUpdate(data);
+                data.node?gljOprObj.refreshTreeNode(data.node):"";
+                $.bootstrapLoading.end();
+            }
+            CommonAjax.post(url,postData,callback,function () {
+                $.bootstrapLoading.end();
+            });
+        };
         quantity_detail.prototype.normalUpdate=function(query,doc){
-            var updateData = this.getUpdateData('ut_update',query,doc);
-            project.pushNow('updateQuantityDetail',[this.getSourceType()],updateData);
+            var url = "/quantity_detail/update";
+            this.commonUpdate(url,{query:query,doc:doc});
         };
         quantity_detail.prototype.regexChecking=function(text){
             var regex=/^[0-9Cc\+\-\*\^/\(\)\.]*$/g;
@@ -447,32 +457,60 @@ var quantity_detail = {
                 this.datas = newList;
             }
         };
+        quantity_detail.prototype.cleanQuantityDetail = function (node,needSave) {
+           node =node?node:projectObj.project.mainTree.selected;
+           var query={projectID:node.data.projectID};
+            if(node.sourceType === project.Bills.getSourceType()){
+                query.billID = node.data.ID;
+                this.deleteByBills([{type:'delete',data:node.data}]);
+            }else if(node.sourceType === project.Ration.getSourceType()){
+                this.deleteByRation(node.data);
+                query.rationID = node.data.ID;
+            }
+            if(needSave===true){
+                query.refreshQuantity=false;
+                CommonAjax.post("/quantity_detail/save",query);
+            }
+            gljOprObj.detailData=[];
+            sheetCommonObj.showData(gljOprObj.detailSheet,gljOprObj.detailSetting,[]);
+
+        };
         quantity_detail.prototype.quantityEditChecking = function(value,node,fieldName){
             var validate = true;
-            if (value && value != calcFees.getFee(node.data, fieldName)){
-                if(fieldName=='quantity'){
-                   if(node.data.hasOwnProperty('isFromDetail')&&node.data.isFromDetail==1){
-                       var c = confirm('已有工程量明细,是否清空明细表,采用手工输入的表达式?')
-                        if(c){
-                            node.data.isFromDetail=0;
-                            if(node.sourceType === project.Bills.getSourceType()){
-                                this.deleteByBills([{type:'delete',data:node.data}]);
-                            }else if(node.sourceType === project.Ration.getSourceType()){
-                                this.deleteByRation(node.data);
-                            }
-                            gljOprObj.detailData=[];
-                            sheetCommonObj.showData(gljOprObj.detailSheet,gljOprObj.detailSetting,[]);
-
-                            validate = true;
-                        }else {
-                            validate = false;
-                        }
-                   }
-                }
+            if(fieldName=='quantity'){
+               if(node.data.hasOwnProperty('isFromDetail')&&node.data.isFromDetail==1){
+                   var c = confirm('已有工程量明细,是否清空明细表,采用手工输入的表达式?')
+                    if(c){
+                        validate = true;
+                    }else {
+                        validate = false;
+                    }
+               }
             }
             return validate;
-
-        }
+        };
+        quantity_detail.prototype.autoTransformQuantity = function(value,node){
+            let data = node.data;
+            let option = optionsOprObj.getOption(optionsOprObj.optionsTypes.GENERALOPTS,'rationQuanACToRationUnit');
+            console.log(option);
+            if(option==true&&node.sourceType === project.Ration.getSourceType()&&data.unit) {//还需加入判读是否转换
+                let times = parseInt(data.unit);
+                if (isNaN(times)) {
+                    times = 1
+                }
+                value = value / times;
+            }
+            return value;
+        };
+        quantity_detail.prototype.getDecimal=function (node) {
+            var decimal = 3;
+            if(node.sourceType === project.Bills.getSourceType()){
+                decimal = billsQuanDecimal.decimal(node.data.unit);
+            }else {
+                decimal = decimalObj.ration.quantity
+            }
+            return;
+        };
         return new quantity_detail(project);
     }
 

+ 15 - 17
web/building_saas/main/js/models/ration.js

@@ -101,9 +101,14 @@ var Ration = {
             });
             controller.sheet.getCell(selected[0].row,col).value(data[fieldName]);
         };
-        ration.prototype.getTempRationData = function (id, billsID, serialNo) {
+        ration.prototype.getTempRationData = function (id, billsID, serialNo, rType) {
             var newData = {'ID': id, 'serialNo': serialNo, projectID: this.project.ID()};
             newData[project.masterField.ration] = billsID;
+            newData['type'] = rType;
+            if (rType == rationType.volumePrice){
+                newData['subType'] = gljType.GENERAL_MATERIAL;   // 默认的量价类型为材料
+                newData['programID'] = projectInfoObj.projectInfo.property.engineering;
+            };
             return newData;
         };
 
@@ -151,17 +156,17 @@ var Ration = {
             return rations;
         };
 
-        ration.prototype.getInsertRationData = function (billsID, preRation) {
+        ration.prototype.getInsertRationData = function (billsID, preRation, rationType) {
             var br = this.getBillsSortRation(billsID);
             var updateData = [];
             if (preRation) {
                 var preIndex = br.indexOf(preRation), i;
-                updateData.push({updateType: 'ut_create', updateData: this.getTempRationData(this.maxRationID() + 1, billsID, preIndex < br.length - 1 ? br[preIndex + 1].serialNo : br[preIndex].serialNo + 1)});
+                updateData.push({updateType: 'ut_create', updateData: this.getTempRationData(this.maxRationID() + 1, billsID, preIndex < br.length - 1 ? br[preIndex + 1].serialNo : br[preIndex].serialNo + 1, rationType)});
                 for (i = preIndex + 1; i < br.length; i++) {
-                    updateData.push({updateType: 'ut_update', updateData: this.getTempRationData(br[i].ID, billsID, i < br.length - 1 ? br[i+1].serialNo : br[i].serialNo + 1)});
+                    updateData.push({updateType: 'ut_update', updateData: this.getTempRationData(br[i].ID, billsID, i < br.length - 1 ? br[i+1].serialNo : br[i].serialNo + 1, rationType)});
                 }
             } else {
-                updateData.push({updateType: 'ut_create', updateData: this.getTempRationData(this.maxRationID() + 1, billsID, br.length > 0 ? br[br.length - 1].serialNo + 1 : 1)});
+                updateData.push({updateType: 'ut_create', updateData: this.getTempRationData(this.maxRationID() + 1, billsID, br.length > 0 ? br[br.length - 1].serialNo + 1 : 1, rationType)});
             }
             return updateData;
         };
@@ -174,27 +179,27 @@ var Ration = {
             }
             return updateData;
         };
-        ration.prototype.insertRation = function (billsID, preRation) {
+        ration.prototype.insertRation = function (billsID, preRation, rationType) {
             var br = this.getBillsSortRation(billsID);
             this.project.pushNow('insertRation', [this.getSourceType(), this.project.projCounter()],
-                [this.getInsertRationData(billsID, preRation), this.getCounterData()]);
+                [this.getInsertRationData(billsID, preRation, rationType), this.getCounterData()]);
 
             var newRation = null;
             if (preRation) {
                 var preIndex = br.indexOf(preRation), i;
-                newRation = this.getTempRationData(this.getNewRationID(), billsID, preIndex < br.length - 1 ? br[preIndex + 1].serialNo : br[preIndex].serialNo + 1);
+                newRation = this.getTempRationData(this.getNewRationID(), billsID, preIndex < br.length - 1 ? br[preIndex + 1].serialNo : br[preIndex].serialNo + 1, rationType);
                 this.datas.push(newRation);
                 for (i = preIndex + 1; i < br.length; i++) {
                     br[i].serialNo = i < br.length - 1 ? br [i + 1].serialNo : br[i].serialNo + 1;
                 }
             } else {
-                newRation = this.getTempRationData(this.getNewRationID(), billsID, br.length > 0 ? br[br.length - 1].serialNo + 1 : 1);
+                newRation = this.getTempRationData(this.getNewRationID(), billsID, br.length > 0 ? br[br.length - 1].serialNo + 1 : 1, rationType);
                 this.datas.push(newRation);
             }
             return newRation;
         };
         ration.prototype.insertStdRation = function (billsID, preRation, std) {
-            var br = this.getBillsSortRation(billsID), updateData = this.getInsertRationData(billsID, preRation), newRation = null, that = this;
+            var br = this.getBillsSortRation(billsID), updateData = this.getInsertRationData(billsID, preRation, rationType.ration), newRation = null, that = this;
             updateData.forEach(function (data) {
 
                 if (data.updateType === 'ut_create') {
@@ -373,13 +378,6 @@ var Ration = {
 
             this.project.endUpdate();
         };
-
-        ration.prototype.calcAll = function (){
-            for (let ration of this.datas){
-                let node = this.project.mainTree.findNode(ration.ID);
-                 this.project.calcProgram.calculate(node);
-            };
-        };
         
         return new ration(project);
     }

+ 110 - 19
web/building_saas/main/js/models/ration_glj.js

@@ -41,9 +41,11 @@ var ration_glj = {
         };
 
         ration_glj.prototype.getGljArrByRation = function (rationID) {
-            return this.datas.filter(function (data) {
+            let result =  this.datas.filter(function (data) {
                 return data.rationID === rationID;
             })
+            result = gljOprObj.combineWithProjectGlj(result);
+            return result;
         };
         ration_glj.prototype.getGatherGljArrByRations = function (rations) {
             let result = [];
@@ -98,8 +100,9 @@ var ration_glj = {
         ration_glj.prototype.refreshAfterSave=function(data){
             let neRecodes=[];
             if(data){
-                neRecodes=data.newRecords;
-                gljOprObj.sheetData=data.showDatas;
+               // neRecodes=data.newRecords;//原来是显示和缓存分开的,后来发现会导致数据不一致的问题所以改成统一的了,这里也只是会作为显示。
+                neRecodes = data.showDatas;
+                gljOprObj.sheetData=neRecodes;
             }
             if(projectObj.project.ration_glj.datas&&Array.isArray(projectObj.project.ration_glj.datas)){
                 if(data){
@@ -109,6 +112,14 @@ var ration_glj = {
                 projectObj.project.ration_glj.datas = neRecodes;
             }
             gljOprObj.showRationGLJSheetData(true);
+            //add to mainTree;
+
+            let node = project.mainTree.selected;
+            project.calcProgram.calculate(node);
+            project.calcProgram.saveNode(node);
+            if (activeSubSheetIs(subSheetIndex.ssiCalcProgram)) {
+                calcProgramObj.showData(node, false);
+            };
         };
         ration_glj.prototype.refreshAfterUpdate=function(data){
             var me = this;
@@ -131,7 +142,6 @@ var ration_glj = {
             })
             _.forEach(doc, function(n, key) {
                 glj_list[glj_index][key] = n;
-                gljOprObj.sheetData[sheet_index][key]=n;
             });
             return glj_list[glj_index].rationID;
         };
@@ -181,7 +191,7 @@ var ration_glj = {
                 for(let i=0;i<data.rationGljList.length;i++){
                     let temdata = data.rationGljList[i];
                     let newGLJ = {};
-                    newGLJ.projectID = newRation.projectID;
+                    newGLJ.projectID = parseInt(newRation.projectID);
                     newGLJ.GLJID = temdata.gljId;
                     newGLJ.rationID = newRation.ID;
                     newGLJ.billsItemID=newRation.billsItemID,
@@ -270,23 +280,34 @@ var ration_glj = {
             updateData.push(newobj);
             return updateData;
         };
-        ration_glj.prototype.updateRationGLJByEdit=function (recode,updateField,newval) {
+        ration_glj.prototype.updateRationGLJByEdit=function (recode,updateField,newval,node) {
             var me=this;
             $.bootstrapLoading.start();
             var callback=function (data) {
+                let initShow = false;//是否需要表格初始化显示
                 if(updateField=='customQuantity'){
-                    me.refreshAfterQuantityUpdate(data);
+                    console.log(data);
+                    me.refreshAfterQuantityUpdate(data,node);
                 }else {
                     var doc = data.doc;
                     for(var key in doc){
                         recode[key] = doc[key];
                     }
+                    console.log(data);
                     if(data.hasOwnProperty('adjustState')){//更新定额调整状态
-                        me.updateRationAdjustState(data.adjustState);
+                        me.updateRationAdjustState(data.adjustState,recode.rationID,node);
+                    }
+                    if(recode.subList&&recode.subList.length>0){
+                        initShow = true;
                     }
                 }
-                gljOprObj.showRationGLJSheetData();
+                if(initShow==false){//不需要初始化,只需耍新当前显示就可以了
+                    gljOprObj.showRationGLJSheetData();
+                }
                 projectObj.project.projectGLJ.loadData(function () {//等项目工料机加载完成后再给用户编辑
+                    if(initShow==true){
+                        gljOprObj.refreshView();
+                    }
                     $.bootstrapLoading.end();
                 });
             }
@@ -301,21 +322,33 @@ var ration_glj = {
             }
             var doc = {};
             doc[updateField]=newval;
+            if(updateField=="type"){
+                doc.shortName = gljTypeMap[newval];
+            }
             CommonAjax.post("/rationGlj/updateRationGLJByEdit",{query:query,doc:doc,priceInfo:priceInfo},callback,function (err) {
                 $.bootstrapLoading.end();
             });
         }
-        ration_glj.prototype.refreshAfterQuantityUpdate=function (data) {
+        ration_glj.prototype.refreshAfterQuantityUpdate=function (data,node) {
             var me=this;
             data.glj_result.forEach(function (item) {
                 me.refreshEachItme(item.doc,item.query);
             })
-            me.updateRationAdjustState(data.adjustState);
+            me.updateRationAdjustState(data.adjustState,data.rationID,node);
         };
-        ration_glj.prototype.updateRationAdjustState=function(adjustState){
-            var selected = projectObj.project.mainTree.selected;
-            selected.data.adjustState=adjustState;
-            projectObj.mainController.refreshTreeNode([selected]);
+        ration_glj.prototype.updateRationAdjustState=function(adjustState,rationID,rnode){
+            var nodes=[];
+            var node = _.find(projectObj.project.mainTree.items,function (n) {
+                return n.sourceType==ModuleNames.ration&&n.data.ID==rationID;
+            })
+            if(node){
+                node.data.adjustState=adjustState;
+                nodes.push(node)
+            }
+            if(rnode){
+                nodes.push(rnode);
+            }
+            projectObj.mainController.refreshTreeNode(nodes);
         };
         ration_glj.prototype.getGLJData = function(cb){
             CommonAjax.get('/rationGlj/getGLJData', function (data) {
@@ -337,20 +370,29 @@ var ration_glj = {
                   quantity:0,
                   name:glj.name,
                   code:glj.code,
+                  original_code:glj.code,
                   unit:glj.unit,
                   specs:glj.specs,
                   basePrice:glj.basePrice,
                   shortName:glj.shortName,
                   type:glj.gljType,
+                  adjCoe:glj.adjCoe,
                   createType:'add',
                   repositoryId:glj.repositoryId
               }
               if(glj.hasOwnProperty("compilationId")){
                   ration_glj.from="cpt";
+                  if(glj.code.indexOf('-')!=-1){//这条工料机是用户通过修改包称、规格、型号等保存到补充工料机库的
+                      ration_glj.original_code = glj.code.split('-')[0];//取-前的编号作为原始编号
+                  }
               }
+
               gljList.push(ration_glj);
           });
-            CommonAjax.post("/rationGlj/addGLJ",gljList,callback);
+          $.bootstrapLoading.start();
+            CommonAjax.post("/rationGlj/addGLJ",gljList,callback,function () {
+                $.bootstrapLoading.end();
+            });
         };
         ration_glj.prototype.replaceGLJ=function (selectCode,oldData,callback) {
             var allGLJ=gljOprObj.AllRecode;
@@ -366,16 +408,23 @@ var ration_glj = {
             oldData.rationItemQuantity=0;
             oldData.name=glj.name;
             oldData.code=glj.code;
+            oldData.original_code=glj.code;
             oldData.unit=glj.unit;
             oldData.specs=glj.specs;
             oldData.basePrice=glj.basePrice;
             oldData.repositoryId=glj.repositoryId;
             if(glj.hasOwnProperty("compilationId")){
                 oldData.from="cpt";
+                if(glj.code.indexOf('-')!=-1){//这条工料机是用户通过修改包称、规格、型号等保存到补充工料机库的
+                    oldData.original_code = glj.code.split('-')[0];//取-前的编号作为原始编号
+                }
             }else {
                 oldData.from="std";
             }
-            CommonAjax.post("/rationGlj/replaceGLJ",oldData,callback);
+            $.bootstrapLoading.start();
+            CommonAjax.post("/rationGlj/replaceGLJ",oldData,callback,function () {
+                $.bootstrapLoading.end();
+            });
         };
 
         ration_glj.prototype.mReplaceGLJ=function (selectCode,oldData,callback) {
@@ -387,7 +436,12 @@ var ration_glj = {
             var query={
                 projectID:oldData.projectID,
                 code:oldData.code,
-                name:oldData.name
+                name:oldData.name,
+                unit:oldData.unit,
+                type:oldData.type
+            }
+            if(oldData.specs&&oldData.specs!=''){
+                query.specs=oldData.specs;
             }
             var doc={
                 GLJID:glj.ID,
@@ -395,6 +449,7 @@ var ration_glj = {
                 rationItemQuantity:0,
                 name:glj.name,
                 code:glj.code,
+                original_code:glj.code,
                 unit:glj.unit,
                 specs:glj.specs,
                 type:glj.gljType,
@@ -409,12 +464,48 @@ var ration_glj = {
             }
             if(glj.hasOwnProperty("compilationId")){
                 doc.from="cpt";
+                if(glj.code.indexOf('-')!=-1){//这条工料机是用户通过修改包称、规格、型号等保存到补充工料机库的
+                    doc.original_code = glj.code.split('-')[0];//取-前的编号作为原始编号
+                }
             }else {
                 doc.from="std";
             }
-            CommonAjax.post("/rationGlj/mReplaceGLJ",{query:query,doc:doc},callback);
+            $.bootstrapLoading.start();
+            CommonAjax.post("/rationGlj/mReplaceGLJ",{query:query,doc:doc},callback,function () {
+                $.bootstrapLoading.end();
+            });
+        };
 
+        ration_glj.prototype.getMainAndEquGLJ = function (rationID) {
+           var gljList = _.filter(this.datas,function (n) {
+              return n.rationID == rationID&&(n.type==gljType.MAIN_MATERIAL||n.type==gljType.EQUIPMENT)
+           });
+           return gljOprObj.combineWithProjectGlj(gljList);
+        };
+        ration_glj.prototype.transferToNodeData = function (data) {
+            data.subType = data.type;
+            data.marketUnitFee = data.marketPrice;
+        };
+        ration_glj.prototype.updateFromMainSpread=function (value,node,fieldName) {
+            console.log(fieldName);
+            console.log(value);
+            if(node.data[fieldName]===value){
+                return;
+            }
+            if(fieldName=="marketUnitFee"){
 
+            }else {
+                if(value!==undefined&&value!==null){
+                    if(fieldName=="subType"){
+                        node.data.subType = value;
+                        fieldName = "type";//转换成更新工料机类型
+                    }
+                    this.updateRationGLJByEdit(node.data,fieldName,value,node);
+                    return;
+                }
+            }
+           // node.data.subType = value;
+            projectObj.mainController.refreshTreeNode([node]);
         };
         return new ration_glj(project);
     }

+ 4 - 2
web/building_saas/main/js/models/volume_price.js

@@ -51,6 +51,8 @@ var VolumePrice = {
             getTempVolumePrice (newID, billsID, serialNo) {
                 var newData = {'ID': newID, 'serialNo': serialNo, projectID: tools.owner.ID()};
                 newData[project.masterField.volumePrice] = billsID;
+                newData.type = '材料';
+                newData.programID = projectInfoObj.projectInfo.property.engineering;
                 return newData;
             };
             getBillsSortVolumePrice (billsID) {
@@ -75,7 +77,7 @@ var VolumePrice = {
             getInsertVolumePriceData (billsID, pre) {
                 let bv = this.getBillsSortVolumePrice(billsID);
                 let updateData = [];
-                if (pre) {
+                if (pre && bv.indexOf(pre) > -1) {
                     let preIndex = bv.indexOf(pre), i;
                     updateData.push({updateType: 'ut_create', updateData: this.getTempVolumePrice(this.maxID() + 1, billsID, preIndex < bv.length - 1 ? bv[preIndex + 1].serialNo : bv[preIndex].serialNo + 1)});
                     for (i = preIndex + 1; i < bv.length; i++) {
@@ -90,7 +92,7 @@ var VolumePrice = {
                 tools.owner.pushNow('insertVolumePrice', [this.getSourceType(), this.getProject().projCounter()], [this.getInsertVolumePriceData(billsID, pre), this.getCounterData()]);
 
                 let bv = this.getBillsSortVolumePrice(billsID), newVP = null;
-                if (pre) {
+                if (pre && bv.indexOf(pre) > -1) {
                     let preIndex = bv.indexOf(pre);
                     newVP = this.getTempVolumePrice(this.getNewID(), billsID, preIndex < bv.length - 1 ? bv[preIndex + 1].serialNo : bv[preIndex].serialNo + 1);
                     this.datas.push(newVP);

+ 301 - 0
web/building_saas/main/js/views/calc_base_view.js

@@ -0,0 +1,301 @@
+/**
+ * Created by Zhong on 2017/12/1.
+ */
+/*
+* 清单计算基数
+* */
+let calcBaseView = {
+    //可用计算基数的清单固定列映射(与fixedFlag)
+    inputExpr: $('#calcBaseExp'),
+    confirmBtn: $('#calcBaseConf'),
+    noBaseBills: [1, 3],
+    editingCell: null,
+    workBook: null,
+    setting:{
+        header: [
+            {name: '计算基础名称', dataCode: 'base', width: 280, vAlign: 'center', hAlign: 'left'},
+            {name: '金额', dataCode: 'price', width: 120, vAlign: 'center', hAlign: 'left'}
+        ],
+        options: {
+            tabStripVisible:  false,
+            allowCopyPasteExcelStyle : false,
+            allowExtendPasteRange: false,
+            allowUserDragDrop : false,
+            allowUserDragFill: false,
+            scrollbarMaxAlign : true
+        },
+        dateRows: [],
+        locked: {
+            rows: [],
+            cols: [0, 1]
+        }
+    },
+
+    renderSheetFuc: function (sheet, fuc) {
+        sheet.suspendPaint();
+        sheet.suspendEvent();
+        fuc();
+        sheet.resumePaint();
+        sheet.resumeEvent();
+    },
+
+    setOptions: function (workbook, opts) {
+        for(let opt in opts){
+            workbook.options[opt] = opts[opt];
+        }
+    },
+
+    buildHeader: function (sheet, headers) {
+        let me = calcBaseView;
+        let fuc = function () {
+            sheet.options.clipBoardOptions = GC.Spread.Sheets.ClipboardPasteOptions.values;
+            sheet.options.isProtected = true;
+            sheet.setColumnCount(headers.length);
+            sheet.setRowHeight(0, 40, GC.Spread.Sheets.SheetArea.colHeader);
+            for(let i = 0, len = headers.length; i < len; i++){
+                sheet.setValue(0, i, headers[i].name, GC.Spread.Sheets.SheetArea.colHeader);
+                sheet.setColumnWidth(i, headers[i].width, GC.Spread.Sheets.SheetArea.colHeader);
+            }
+        };
+        me.renderSheetFuc(sheet, fuc);
+    },
+
+    buildSheet: function () {
+        if(!this.workBook){
+            this.workBook = new GC.Spread.Sheets.Workbook($('#billsBaseSpread')[0], {sheetCount: 1});
+            this.setOptions(this.workBook, this.setting.options);
+            this.buildHeader(this.workBook.getActiveSheet(), this.setting.header);
+            this.bindEvent(this.workBook);
+        }
+    },
+
+    bindEvent: function (workBook) {
+        const _events = GC.Spread.Sheets.Events;
+        let sheet = workBook.getActiveSheet();
+        sheet.bind(_events.CellDoubleClick, this.onCellDoubleClick);
+    },
+
+    showData(datas){
+        let me = calcBaseView;
+        let sheet = this.workBook.getActiveSheet();
+        let cols = this.setting.header;
+        let fuc = function () {
+            sheet.setRowCount(datas.length);
+            sheet.setFormatter(-1, 1, '@');
+            for(let col = 0, cLen = cols.length; col < cLen; col++){
+                sheet.getRange(-1, col, -1, 1).hAlign(GC.Spread.Sheets.HorizontalAlign[cols[col]['hAlign']]);
+                sheet.getRange(-1, col, -1, 1).hAlign(GC.Spread.Sheets.VerticalAlign[cols[col]['vAlign']]);
+                for(let row = 0, rLen = datas.length; row < rLen; row++){
+                    sheet.setValue(row, col, datas[row][cols[col]['dataCode']]);
+                }
+            }
+        };
+        this.renderSheetFuc(sheet, fuc);
+    },
+
+    onCellDoubleClick: function (sender, args) {
+        let me = calcBaseView;
+        if(args.col === 0){
+            let baseFigure = args.sheet.getValue(args.row, args.col);
+            if(baseFigure.trim() !== ''){
+                //在光标后面插入
+                let insertStr = me.insertStr(baseFigure);
+                me.inputExpr.val(insertStr);
+            }
+        }
+        me.inputExpr.focus();
+    },
+    isDef: function (v) {
+        return v !== undefined && v !== null;
+    },
+    isFlag: function (v) {
+        return this.isDef(v.flagsIndex) && this.isDef(v.flagsIndex.fixed);
+    },
+    ifEdit: function () {
+        var selected = projectObj.project.mainTree.selected;
+        return MainTreeCol.readOnly.forCalcBase(selected)?false:true;
+    },
+    canBase: function (node) {
+        return node.sourceType === projectObj.project.Bills.getSourceType() && node.children.length === 0;
+    },
+    //计算基数转换为显示数据Obj to Array
+    toViewData: function (obj) {
+        let rst = [];
+        for(let figure in obj){
+            let figureObj = Object.create(null);
+            figureObj.base = figure;
+            figureObj.price = projectObj.project.calcBase.getBase(figure);
+            rst.push(figureObj);
+        }
+        return rst;
+    },
+
+    initCalctor: function (node) {
+        let me = calcBaseView;
+        //输入框显示原本的
+        if(me.isDef(node.data.calcBase)){
+            me.inputExpr.val(node.data.calcBase);
+        }
+        me.buildSheet();
+        let baseObj = projectObj.project.calcBase.getBaseByClass(node);
+        me.showData(me.toViewData(baseObj));
+
+    },
+
+    getInputExpr: function () {
+        return this.inputExpr.val();
+    },
+
+    //四则运算符控制,不可连续出现: ++ +*...
+    arithmeticLeagl: function (v) {
+        let rex = /[\+,\-,\*,\/]{2}/g;
+        return !rex.test(v);
+    },
+
+    //运算符点击显示到运算窗口
+    clickOpr: function (operators) {
+        let me = calcBaseView;
+        for(let i = 0, len = operators.length; i < len; i++){
+            operators[i].bind('click', function () {
+                let v = $(this)[0].textContent;
+                let insertStr = me.insertStr(v);
+                if(me.arithmeticLeagl(insertStr)){
+                    me.inputExpr.val(insertStr);
+                }
+                me.inputExpr.focus();
+            });
+        }
+    },
+
+    //光标处插入(替换选中)
+    insertStr: function (v) {
+        let me = calcBaseView;
+        //在光标后面插入
+        let exp = me.getInputExpr();
+        let startIdx = me.inputExpr[0].selectionStart;
+        let endIdx = me.inputExpr[0].selectionEnd;
+        let startStr = exp.substring(0, startIdx);
+        let endStr = exp.substring(endIdx, exp.length);
+        return startStr + v + endStr;
+    },
+
+    //输入窗口控制
+    inputControl: function () {
+        let me = calcBaseView;
+        me.inputExpr.keydown(function (e) {
+            if(!me.arithmeticLeagl(me.inputExpr.val() + e.key)){
+                return false;
+            }
+        });
+    },
+
+    //确认按钮
+    calcBaseConf: function () {
+        let me = calcBaseView;
+        me.confirmBtn.bind('click', function () {
+            let selected = projectObj.project.mainTree.selected;
+            selected.data.userCalcBase = me.getInputExpr();
+            projectObj.project.calcBase.calculate(selected);
+            if(projectObj.project.calcBase.success){
+                $('#qd-jsjs').modal('hide');
+            }
+        });
+    },
+
+    getCalcBaseCellType:function () {
+        var ns = GC.Spread.Sheets;
+        function CalcBaseCellType() {
+            var init=false;
+        }
+        CalcBaseCellType.prototype = new ns.CellTypes.Text();
+        CalcBaseCellType.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
+            if(value!=null){
+                // ctx.fillText(value,x+3+ctx.measureText(value).width,y+h-3);
+                ctx.fillText(value,x+w,y+h-3);
+            }
+            if(calcBaseView.editingCell){
+                if(calcBaseView.editingCell.row==options.row&&calcBaseView.editingCell.col==options.col){
+                    var image = document.getElementById('f_btn'),imageMagin = 3;
+                    var imageHeight = h-2*imageMagin;
+                    var imageWidth = w*2/7;
+                    var imageX = x + w - imageWidth- imageMagin, imageY = y + h / 2 - imageHeight / 2;
+                    ctx.save();
+                    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();
+                }
+            }
+        };
+        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(me.editingCell==null){
+                var showSelectBtn = true;
+                if(hitinfo.sheet.name()!='calc_detail'){
+                    showSelectBtn=me.ifEdit();
+                }
+                if(showSelectBtn){
+                    me.editingCell={
+                        row:hitinfo.row,
+                        col:hitinfo.col
+                    }
+                    hitinfo.sheet.invalidateLayout();
+                    hitinfo.sheet.repaint();
+                }
+            }else if(hitinfo.row==me.editingCell.row){
+                var offset=hitinfo.cellRect.x+hitinfo.cellRect.width-6;
+                var imageMagin=3;
+                var imageHeight = hitinfo.cellRect.height-2*imageMagin;
+                var imageWidth = hitinfo.cellRect.width*2/7;
+                if(hitinfo.x<offset&&hitinfo.x>offset-imageWidth){
+                    $('#qd-jsjs').modal({show: true});
+                }
+            }
+        };
+        CalcBaseCellType.prototype.processMouseLeave = function (hitinfo) {
+            calcBaseView.editingCell=null;
+            hitinfo.sheet.invalidateLayout();
+            hitinfo.sheet.repaint();
+        }
+        return new CalcBaseCellType();
+    },
+};
+
+$(document).ready(function () {
+   $('#qd-jsjs').on('shown.bs.modal', function () {
+       calcBaseView.initCalctor(projectObj.project.mainTree.selected);
+       calcBaseView.workBook.refresh();
+   });
+
+    $('#qd-jsjs').on('hidden.bs.modal', function () {
+        //清空输入框
+        calcBaseView.inputExpr.val('');
+        calcBaseView.workBook.destroy();
+        calcBaseView.workBook = null;
+    });
+
+    //bind operator click function
+    calcBaseView.clickOpr([$('#addOpr'), $('#subOpr'), $('#mulOpr'), $('#divOpr'), $('#leftOpr'), $('#rightOpr')]);
+
+    //bind input control
+    calcBaseView.inputControl();
+
+    //confirmBtn
+    calcBaseView.calcBaseConf();
+});

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

@@ -26,14 +26,14 @@ let rationPM = {
             {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.feeRate, dataCode:"feeRate", dataType: "Number",hAlign: "right",tofix: feeRate_consts.decimal},
+            {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"},
             {headerName:"备注",headerWidth:CP_Col_Width.memo, dataCode:"memo", dataType: "String"}
         ],
         view:{
             comboBox:[],
-            lockColumns:[0,1,2,4,5,6],
+            lockColumns:[0,1,2,5,6],
             colHeaderHeight: CP_Col_Width.colHeader,
             rowHeaderWidth: CP_Col_Width.rowHeader
         }

+ 6 - 4
web/building_saas/main/js/views/calc_program_view.js

@@ -225,12 +225,14 @@ let calcProgramObj = {
         sheetCommonObj.initSheet(me.sheet, me.setting, 1);
     },
 
-    showData: function (treeNode) {
+    showData: function (treeNode, needCalc = true) {
         var me = this;
         me.treeNode = treeNode;
-        projectObj.project.calcProgram.calculate(treeNode);
-        projectObj.project.calcProgram.saveNode(treeNode);
-        me.datas = treeNode.data.calcTemplate.calcItems;
+        if (needCalc){
+            projectObj.project.calcProgram.calculate(treeNode);
+            projectObj.project.calcProgram.saveNode(treeNode);
+        };
+        me.datas = treeNode.data.calcTemplate ? treeNode.data.calcTemplate.calcItems : [];
         sheetCommonObj.initSheet(me.sheet, me.setting, me.datas.length);
         sheetCommonObj.showData(me.sheet, me.setting, me.datas);
 

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

@@ -19,7 +19,7 @@ var feeRateObject={
     sheetSetting: {
         header: [
             {headerName: "专业名称", headerWidth: 200, dataCode: "name", dataType: "String"},
-            {headerName: "值%", headerWidth: 150, dataCode: "rate", dataType: "Number",hAlign: "right",tofix:feeRate_consts.decimal},
+            {headerName: "值%", headerWidth: 150, dataCode: "rate", dataType: "Number",hAlign: "right",decimalField:"feeRate"},
             {headerName: "备注", dataCode: "memo", dataType: "String"}
         ],
         view: {
@@ -39,7 +39,6 @@ var feeRateObject={
             id: 'rate',
             caption: '值%',
             dataField: 'rate',
-            format: '0.000',
             width: 120,
             minWidth: 50,
             allowEditing: true
@@ -113,7 +112,6 @@ var feeRateObject={
             if(!$('#cascadeSet').prop('checked')||params.hasOwnProperty('viewIndex')){
                 projectObj.project.FeeRate.updateFeeRateByEdit(params,feeRateObject.activateFeeRate);
             }
-
         }
     },
     createSheet:function(){
@@ -151,10 +149,12 @@ var feeRateObject={
             for (var row = 0; row < data.length; row++) {
                 var val = data[row][setting.header[col].dataCode];
                 if(val&&setting.header[col].dataType === "Number"){
-                    if(setting.header[col].hasOwnProperty('tofix')){
-                        val =parseFloat(val).toFixed(setting.header[col].tofix);
+                    if(setting.header[col].hasOwnProperty('decimalField')){
+                        var decimal = getDecimal(setting.header[col].decimalField);
+                        val =scMathUtil.roundToString(val,decimal);
+                        sheet.setFormatter(-1, col,getFormatter(decimal), GC.Spread.Sheets.SheetArea.viewport);
                     }else {
-                        val =parseFloat(val).toFixed(2);
+                        val =scMathUtil.roundToString(val,2);
                     }
                 }
                 sheet.setValue(row, col, val, ch);
@@ -253,7 +253,7 @@ var feeRateObject={
         FeeRateEditCellType.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
             if(value!=null){
                // ctx.fillText(value,x+3+ctx.measureText(value).width,y+h-3);
-                ctx.fillText(value,x+w,y+h-3);
+                ctx.fillText(value,x+w-3,y+h-3);
             }
             if(feeRateObject.editingCell){
                 if(feeRateObject.editingCell.row==options.row&&feeRateObject.editingCell.col==options.col){
@@ -345,7 +345,7 @@ var feeRateObject={
     },
     ifFeeRateEdit:function () {
         var selected = projectObj.project.mainTree.selected;
-        return MainTreeCol.readOnly.forCalcBase(selected)?false:true;
+        return MainTreeCol.readOnly.forFeeRate(selected)?false:true;
     },
     createSpreadView:function () {
         if (this.mainViews) {
@@ -358,6 +358,8 @@ var feeRateObject={
         }
         this.activateFeeRate = projectObj.project.FeeRate.getActivateFeeRate();
         this.datas = this.activateFeeRate.rates;
+        var rateColSetting = _.find(this.columns,{"id":"rate"});
+        rateColSetting?rateColSetting.format=getFormatter(getDecimal("feeRate")):"";
         this.mainViews = new GC.Spread.Views.DataView($('#divFee')[0],
             this.dataSource, this.columns, new GC.Spread.Views.Plugins.GridLayout(this.options));
         this.mainViews["rowClick"].addHandler(subRateObject.reFreshRateViews);
@@ -648,8 +650,11 @@ var feeRateObject={
        var selected = projectObj.project.mainTree.selected;
         projectObj.project.FeeRate.submitFeeRateFromBill(rate,selected.data,function (data) {
             selected.data.feeRateID=rate.ID.toString();
-            selected.data.feeRate=parseFloat(rate.rate).toFixed(feeRate_consts.decimal);
-            projectObj.mainController.refreshTreeNode([selected]);
+            selected.data.feeRate=scMathUtil.roundToString(rate.rate,getDecimal("feeRate"));
+            selected.changed = true;
+            projectObj.project.calcProgram.calculate(selected);
+            projectObj.project.calcProgram.saveNode(selected);
+            //projectObj.mainController.refreshTreeNode([selected]);
             $("#fee_rate_tree").modal('hide');
         });
     },

+ 230 - 57
web/building_saas/main/js/views/glj_view.js

@@ -15,12 +15,9 @@ var gljOprObj = {
     detailSheet:null,
     detailData:[],
     GLJSelection:[],
+    selectedGLJClass:null,
     parentNodeIds:{},
     activeTab:'#linkGLJ',
-    decimalSetting:{
-        marketPrice:2,
-        customQuantity:3
-    },
     setting: {
         header: [
             {headerName: "编码", headerWidth: 100, dataCode: "code", dataType: "String", formatter: "@"},
@@ -28,18 +25,19 @@ var gljOprObj = {
             {headerName: "规格型号", headerWidth: 120, dataCode: "specs", dataType: "String", hAlign: "left"},
             {headerName: "单位", headerWidth: 45, dataCode: "unit", dataType: "String", hAlign: "center"},
             {headerName: "类型", headerWidth: 45, dataCode: "shortName", dataType: "String", hAlign: "center"},
-            {headerName: "定额消耗量", headerWidth: 80, dataCode: "rationItemQuantity", dataType: "Number", hAlign: "right",formatter:"0.000",tofix:3},    // dataType: "Number", formatter: "0.00"
-            {headerName: "自定义消耗量", headerWidth: 80, dataCode: "customQuantity", dataType: "Number", hAlign: "right",formatter:"0.000",tofix:3},
-            {headerName: "消耗量", headerWidth: 80, dataCode: "quantity", dataType: "Number", hAlign: "right",formatter:"0.000",tofix:3},
-            {headerName: "基价单价", headerWidth: 80, dataCode: "basePrice", dataType: "Number", hAlign: "right",formatter:"0.00"},
-            {headerName: "调整基价", headerWidth: 80, dataCode: "adjustPrice", dataType: "Number", hAlign: "right",formatter:"0.00"},
-            {headerName: "市场单价", headerWidth: 80, dataCode: "marketPrice", dataType: "Number", hAlign: "right",formatter:"0.00"},
+            {headerName: "定额消耗", headerWidth: 80, dataCode: "rationItemQuantity", dataType: "Number", hAlign: "right",decimalField:"glj.quantity"},    // dataType: "Number", formatter: "0.00"
+            {headerName: "自定消耗", headerWidth: 80, dataCode: "customQuantity", dataType: "Number", hAlign: "right",decimalField:"glj.quantity"},
+            {headerName: "消耗量", headerWidth: 80, dataCode: "quantity", dataType: "Number", hAlign: "right",decimalField:"glj.quantity"},
+            {headerName: "总消耗量", headerWidth: 80, dataCode: "totalQuantity", dataType: "Number", hAlign: "right",decimalField:"glj.quantity"},
+            {headerName: "定额价", headerWidth: 80, dataCode: "basePrice", dataType: "Number", hAlign: "right",decimalField:"glj.unitPrice"},
+            {headerName: "调整价", headerWidth: 80, dataCode: "adjustPrice", dataType: "Number", hAlign: "right",decimalField:"glj.unitPrice"},
+            {headerName: "市场价", headerWidth: 80, dataCode: "marketPrice", dataType: "Number", hAlign: "right",decimalField:"glj.unitPrice"},
             {headerName: "是否暂估", headerWidth: 65, dataCode: "isEstimate", dataType: "String", hAlign: "center",vAlign:"center",cellType:"checkBox"}
         ],
         view: {
             comboBox: [{row: -1, col: 12, rowCount: -1, colCount: 1}],
             lockedCells: [{row: -1, col: 3, rowCount: -1, colCount: 1}],
-            lockColumns:[0,4,5,7,8,9,11]
+            lockColumns:[0,4,5,7,8,9,10,12]
         },
         notEditedType: ['砼','桨','配比','机']
     },
@@ -56,8 +54,8 @@ var gljOprObj = {
     assSetting:{
         header:[
             {headerName: "调整名称", headerWidth: 100, dataCode: "name", dataType: "String"},
-            {headerName: "定额值", headerWidth: 120, dataCode: "stdValue", dataType: "String"},
-            {headerName: "实际值", headerWidth: 120, dataCode: "actualValue", dataType: "String"}
+            {headerName: "定额值", headerWidth: 120, dataCode: "stdValue", hAlign: "right", dataType: "String"},
+            {headerName: "实际值", headerWidth: 120, dataCode: "actualValue", hAlign: "right", dataType: "String"}
         ],
         view:{
             lockColumns:[0,1]
@@ -67,7 +65,7 @@ var gljOprObj = {
         header:[
             {headerName: "名称", headerWidth: 100, dataCode: "name", dataType: "String"},
             {headerName: "计算式", headerWidth: 120, dataCode: "regex", dataType: "String"},
-            {headerName: "结果(C)", headerWidth: 120, dataCode: "result", dataType: "Number",formatter:"0.0000",tofix:4},
+            {headerName: "结果(C)", headerWidth: 120, dataCode: "result", dataType: "Number",decimalField:"quantity_detail"},
             {headerName: "累加", headerWidth: 120, dataCode: "isSummation", dataType: "String",cellType:"checkBox"}
         ],
         view:{
@@ -107,12 +105,23 @@ var gljOprObj = {
         },
         callback:{
             onClick: function(event,treeId,treeNode) {
-                let me = gljOprObj, gljTypeId = treeNode.ID;
-                if(me.gljCurTypeId !== treeNode.ID){
-                    me.gljCurTypeId = treeNode.ID;
-                    me.filterLibGLJSheetData();
-                    me.showLibGLJSheetData();
+                if(treeId=='gljTree'){
+                    let me = gljOprObj, gljTypeId = treeNode.ID;
+                    if(me.gljCurTypeId !== treeNode.ID){
+                        me.gljCurTypeId = treeNode.ID;
+                        me.filterLibGLJSheetData();
+                        me.showLibGLJSheetData();
+                    }
+                }else {
+                    if(treeNode.isParent){
+                        $('#class_selected_conf').attr("disabled","disabled");
+                        $('#selected_class').val("");
+                    }else {
+                        $('#class_selected_conf').removeAttr("disabled");
+                        $('#selected_class').val(treeNode.ID);
+                    }
                 }
+
             }
         }
     },
@@ -161,8 +170,10 @@ var gljOprObj = {
         var me = this;
         me.detailSheet = sheet;
         sheetCommonObj.initSheet(me.detailSheet, me.detailSetting, 30);
+       // me.detailSheet.selectionUnit(0);//0 cell,1 row,2 col;
         sheet.name('quantity_detail');
         me.bindSheetEvent(sheet);
+
     },
     showCoeData:function(sheet,setting,datas){
         sheet.floatingObjects.remove("customerCoe");
@@ -220,12 +231,18 @@ var gljOprObj = {
         if(args.row==me.detailData.length&&args.editingText==null){
             return;
         }
-       if(args.row==me.detailData.length){
-           projectObj.project.quantity_detail.saveQuantityDetail(args,me.detailSetting.header[args.col].dataCode);
-       }
-        if(args.row<me.detailData.length){
-            projectObj.project.quantity_detail.updateQuantityDetail(args,me.detailSetting.header[args.col].dataCode,me.detailData[args.row]);
-        }
+        var selected = projectObj.project.mainTree.selected;//因为使用了延时方法,所以要先取得选中行;
+        var detailList = me.detailData;
+        args.editingText = args.editingText.replace(/(/g,"(");//替换中文左右括号;
+        args.editingText = args.editingText.replace(/)/g,")");
+        setTimeout(function () {//这里须用延时执行的办法,不然的弹窗确认窗口会和spreadjs 的事件有冲突,造成定额工料机数据不会根据树结点更新的问题
+            if(args.row==detailList.length){
+                projectObj.project.quantity_detail.saveQuantityDetail(args,me.detailSetting.header[args.col].dataCode,selected);
+            }
+            if(args.row<detailList.length){
+                projectObj.project.quantity_detail.updateQuantityDetail(args,me.detailSetting.header[args.col].dataCode,detailList[args.row],selected);
+            }
+        },100);
 
     },
     onEditGLJSheet:function(args){
@@ -339,7 +356,7 @@ var gljOprObj = {
         $('#manual').val(data.coes[1].amount);
         $('#material').val(data.coes[2].amount);
         $('#manchine').val(data.coes[3].amount);
-        $('#main').val(data.coes[4].amount);
+        $('#mainM').val(data.coes[4].amount);
         $('#equipment').val(data.coes[5].amount);
     },
     onInputChange(id,name){
@@ -355,7 +372,7 @@ var gljOprObj = {
                 $('#manual').val(newValue);
                 $('#material').val(newValue);
                 $('#manchine').val(newValue);
-                $('#main').val(newValue);
+                $('#mainM').val(newValue);
                 $('#equipment').val(newValue);
             }else {
                 $('#'+id).val(newValue);
@@ -375,7 +392,7 @@ var gljOprObj = {
         var manual=$('#manual').val();
         var material = $('#material').val();
         var manchine= $('#manchine').val();
-        var main = $('#main').val();
+        var mainM = $('#mainM').val();
         var equipment=$('#equipment').val();
         if(coe_ration!=data.coes[0].amount){
             result.isNeed =true;
@@ -393,9 +410,9 @@ var gljOprObj = {
             result.isNeed =true;
             data.coes[3].amount =manchine;
         }
-        if(main!=data.coes[4].amount){
+        if(mainM!=data.coes[4].amount){
             result.isNeed =true;
-            data.coes[4].amount =main;
+            data.coes[4].amount =mainM;
         }
         if(equipment!=data.coes[5].amount){
             result.isNeed =true;
@@ -418,6 +435,11 @@ var gljOprObj = {
         var me = gljOprObj;
         var header = me.setting.header;
         var disable = null;
+        if(me.sheetData[args.row]!=undefined){
+            if(me.sheetData[args.row].isMixRatio){
+                disable = true;
+            }
+        }
         if(header[args.col]&&header[args.col].dataCode=='marketPrice'){
             var type = me.sheetData[args.row].shortName;
             var index= _.indexOf(me.setting.notEditedType,type);
@@ -481,7 +503,7 @@ var gljOprObj = {
         newString += this.getOneRow('人工',1,'manual');
         newString += this.getOneRow('材料',2,'material');
         newString += this.getOneRow('机械',3,'manchine');
-        newString += this.getOneRow('主材',4,'main');
+        newString += this.getOneRow('主材',4,'mainM');
         newString += this.getOneRow('设备',5,'equipment');
         newString +="</table></form>";
 
@@ -534,22 +556,32 @@ var gljOprObj = {
         }
     },
     showRationGLJSheetData:function (init) {
-        if(init){
-            this.sheet.getRange(0,-1,this.sheet.getRowCount(),-1).visible(true);
-            this.sheetData=_.sortBy(this.sheetData,'type');
-            this.addMixRatioToShow();
-            this.initRationTree();
-        }
+        this.sheet.setRowCount(0);
+        //console.log(+new Date())
+        //this.sheet.getRange(0,-1,this.sheet.getRowCount(),-1).visible(true); //这个方法导致加载缓慢
+        this.sheetData=_.sortBy(this.sheetData,'type');
+        this.sumQuantity();//计算总消耗量
+        this.addMixRatioToShow();//显示组成物信息
+        this.initRationTree(init);
         sheetCommonObj.showData(this.sheet,this.setting,this.sheetData);
 
     },
-    initRationTree:function () {
+    initRationTree:function (init) {
         this.sheet.getRange(-1, 0, -1, 1).cellType(this.getTreeNodeCellType(this.sheetData));
         for(var i =0;i<this.sheetData.length;i++){
             if(this.sheetData[i].hasOwnProperty('subList')){
-                this.sheet.setTag(i,0,true);
-                this.sheet.getRange(i+1, -1, this.sheetData[i].subList.length, -1).visible(false);
-                this.sheet.getRange(i+1, -1, this.sheetData[i].subList.length, -1).locked(true);
+                var collapsed = false;
+                if(init){
+                    this.sheetData[i].collapsed=true;
+                    collapsed = true;
+                }else {
+                    collapsed = this.sheetData[i].collapsed==undefined?true:this.sheetData[i].collapsed;
+                }
+                if(collapsed==true){
+                    this.sheet.getRange(i+1, -1, this.sheetData[i].subList.length, -1).visible(false);
+                }
+                //this.sheet.getRange(i+1, -1, this.sheetData[i].subList.length, -1).visible(!collapsed);// this.sheet.getRange(i+1, -1, this.sheetData[i].subList.length, -1).locked(true);
+                //这个方法导致加载缓慢
             }
         }
     },
@@ -564,6 +596,25 @@ var gljOprObj = {
         this.sheetData=this.combineWithProjectGlj(gljList);
         this.showRationGLJSheetData(true);
     },
+    sumQuantity:function (node) {
+      if(this.sheetData.length>0){
+          node=node?node:projectObj.project.mainTree.selected;
+          let ration = node.data;
+          let quantity = ration.quantity;
+          quantity = (quantity==0||quantity==undefined||quantity==null||quantity=="")?1:quantity;
+          for(let glj of this.sheetData){
+              if(glj.isMixRatio==true){//如果是用于显示的组成物,则不用计算,跳过
+                  continue;
+              }
+              glj.totalQuantity = scMathUtil.roundToString(quantity*glj.quantity,getDecimal("glj.quantity"));
+              if(glj.hasOwnProperty('subList')){//需要计算glj下挂的组成物的总消耗量
+                  for(let subG of glj.subList){
+                      subG.totalQuantity = scMathUtil.roundToString(subG.rationItemQuantity*glj.totalQuantity,getDecimal("glj.quantity"));
+                  }
+              }
+          }
+      }
+    },
     addMixRatioToShow:function () {
         var newList=[];
         _.remove(this.sheetData,{'isMixRatio':true});
@@ -585,9 +636,10 @@ var gljOprObj = {
                 if(glj){
                     ration_gljs[i].basePrice=glj.unit_price.base_price;
                     ration_gljs[i].marketPrice=glj.unit_price.market_price;
-                    ration_gljs[i].adjustPrice=glj.adjust_price;
+                    //ration_gljs[i].adjustPrice=glj.adjust_price;
                     ration_gljs[i].isEstimate=glj.is_evaluate;
                     ration_gljs[i].isAdd=glj.unit_price.is_add;
+                    ration_gljs[i].adjustPrice=projectObj.project.projectGLJ.getAdjustPrice(glj);
                     var connect_index = this.getIndex(glj,['code','name','specs','unit','type'])
                     if(mixRatioMap.hasOwnProperty(connect_index)){
                         var mixRatios = this.getMixRationShowDatas(mixRatioMap[connect_index],projectGljs);
@@ -626,7 +678,8 @@ var gljOprObj = {
                 adjustPrice:pg.adjust_price,
                 isEstimate:pg.is_evaluate,
                 isMixRatio:true,
-                isAdd:pg.unit_price.is_add
+                isAdd:pg.unit_price.is_add,
+                GLJID:pg.glj_id
             }
             temRationGLJs.push(tem);
         }
@@ -650,6 +703,7 @@ var gljOprObj = {
     },
     showQuantityDetailData:function (node) {
         var details=[];
+        node =node?node:projectObj.project.mainTree.selected;
         var quantity_detail =projectObj.project.quantity_detail;
         if(node.sourceType==ModuleNames.ration){
             details=_.filter(quantity_detail.datas,{'rationID':node.data.ID});
@@ -696,10 +750,15 @@ var gljOprObj = {
         var recode = me.sheetData[args.row];
         var newval;
         if(updateField=='marketPrice'||updateField=='customQuantity'||updateField=='basePrice'){
-            newval = number_util.checkNumberValue(args.editingText,this.decimalSetting[updateField]);
-            if(!newval){
-                me.sheet.getCell(args.row, args.col).value(recode[updateField]);
-                return;
+            if(args.editingText==null){
+                newval="";
+            }else {
+                var decimal = updateField=='customQuantity'?getDecimal("glj.quantity"):getDecimal("glj.unitPrice");
+                newval = number_util.checkNumberValue(args.editingText,decimal);
+                if(newval==null){
+                    me.sheet.getCell(args.row, args.col).value(recode[updateField]);
+                    return;
+                }
             }
         }else {
              if(updateField=='name'||updateField=='unit'){
@@ -711,6 +770,9 @@ var gljOprObj = {
              }
             newval=args.editingText==null?"":args.editingText;
         }
+        if(newval === recode[updateField]){//如果值完全相等,则不需要更新
+            return
+        }
         if(updateField=='marketPrice'||updateField=='basePrice'){
             projectObj.project.projectGLJ.updatePriceFromRG(recode,updateField,newval);
         } else {
@@ -886,15 +948,17 @@ var gljOprObj = {
             return _.find(gljOprObj.sheetData,{'code':n})?false:true;
         })
         if(gljOprObj.GLJSelection.length>0&&selected&&selected.sourceType==ModuleNames.ration){
+            $("#glj_tree_div").modal('hide');
             project.ration_glj.addGLJByLib(gljOprObj.GLJSelection,selected.data,function (result) {
                 if(result){
                     selected.data.adjustState=result.adjustState;
-                    project.ration_glj.datas = project.ration_glj.datas.concat(result.newRecodes);
+                    //project.ration_glj.datas = project.ration_glj.datas.concat(result.newRecodes);//显示和缓存统一,这样的话就不用更新两个位置了
+                    project.ration_glj.datas = project.ration_glj.datas.concat(result.showData);
                     gljOprObj.sheetData = gljOprObj.sheetData.concat(result.showData)
                     gljOprObj.showRationGLJSheetData();
                     project.projectGLJ.loadData();
                     projectObj.mainController.refreshTreeNode([selected]);
-                    $("#glj_tree_div").modal('hide');
+                    $.bootstrapLoading.end();
                 }
             });//doc.rationID=selected.data.ID;
         }else {
@@ -908,6 +972,7 @@ var gljOprObj = {
         var project= projectObj.project;
         var selectCode=gljOprObj.GLJSelection[0];
         var selected = projectObj.project.mainTree.selected;
+        $("#glj_tree_div").modal('hide');
         project.ration_glj.replaceGLJ(selectCode,oldData,function (result) {
             if(result){
                 //result.adjustState;
@@ -919,7 +984,7 @@ var gljOprObj = {
                 selected.data.adjustState=result.adjustState;
                 projectObj.mainController.refreshTreeNode([selected]);
             }
-            $("#glj_tree_div").modal('hide');
+            $.bootstrapLoading.end();
         })
     },
     doMReplaceGLJ:function () {
@@ -927,18 +992,21 @@ var gljOprObj = {
         var oldData=me.sheetData[gljContextMenu.selectedRow];
         var project= projectObj.project;
         var selectCode=me.GLJSelection[0];
+        $("#glj_tree_div").modal('hide');
         project.ration_glj.mReplaceGLJ(selectCode,oldData,function (result) {
             var data=result.data;
             var stateList= result.stateList;
+            var n_index = me.getIndex(data.query,['code','name','specs','unit','type']);
             _.forEach(project.ration_glj.datas,function (t) {
-                if(t.code==data.query.code&&t.name==data.query.name){
+                var t_index =me.getIndex(t,['code','name','specs','unit','type']);
+                if(n_index==t_index){
                     me.updateProperty(t,data.doc);
                 }
             })
             me.showRationGLJSheetData();
             project.projectGLJ.loadData();
             me.refreshStateAfterMreplace(stateList);
-            $("#glj_tree_div").modal('hide');
+            $.bootstrapLoading.end();
         })
     },
     updateProperty:function (obj,doc) {
@@ -962,6 +1030,33 @@ var gljOprObj = {
     refreshView:function () {
         this.showRationGLJData();
     },
+    //
+    refreshTreeNode:function (obj) {
+    if(!obj){
+        return;
+    }
+    var objectArray =[];
+    var nodes =[];
+    if(obj instanceof Array){
+        objectArray.concat(obj);
+    }else {
+        objectArray.push(obj);
+    }
+    for(let o of objectArray ){
+        var node = _.find(projectObj.project.mainTree.items,function (n) {
+            return n.sourceType==o.type&&n.data.ID==o.ID;
+        })
+        if(node){
+            for (var k in o.data){
+                node.data[k] = o.data[k];
+            }
+            nodes.push(node);
+        }
+    }
+
+    projectObj.mainController.refreshTreeNode(nodes);
+
+    },
     getTreeNodeCellType:function (data) {
         var ns = GC.Spread.Sheets;
         var rectW = 10;
@@ -1030,7 +1125,7 @@ var gljOprObj = {
                 var recode = data[options.row];
                 if(recode&&recode.hasOwnProperty('subList')){
                     drowRect(ctx,x,y,w,h);
-                    var collapsed = options.sheet.getTag(options.row,options.col);
+                    var collapsed = recode.collapsed==undefined?true:recode.collapsed;//options.sheet.getTag(options.row,options.col);
                     drowSymbol(ctx,x,y,w,h,collapsed);
                 }else if(recode&&recode.isMixRatio){
                     offset= drowSubItem(ctx,x,y,w,h,offset,data[options.row+1]);
@@ -1056,9 +1151,10 @@ var gljOprObj = {
             if(recode&&recode.hasOwnProperty('subList')){
                var hoffset= hitinfo.cellRect.x+3;
                 if (hitinfo.x > hoffset && hitinfo.x < hoffset + 10){
-                    var collapsed =  hitinfo.sheet.getTag(hitinfo.row,hitinfo.col,hitinfo.sheetArea);
-                    collapsed = !collapsed;
-                    hitinfo.sheet.setTag(hitinfo.row,hitinfo.col,collapsed);
+                    var collapsed = recode.collapsed==undefined?true:recode.collapsed;
+                    collapsed = !collapsed
+                    recode.collapsed=collapsed;
+                    //hitinfo.sheet.setTag(hitinfo.row,hitinfo.col,collapsed);
                     hitinfo.sheet.getRange(hitinfo.row+1, -1, recode.subList.length, -1).visible(!collapsed);
                     hitinfo.sheet.invalidateLayout();
                     hitinfo.sheet.repaint();
@@ -1119,7 +1215,84 @@ $(function(){
             gljOprObj.doMReplaceGLJ();
         }
     })
-})
 
+    $('#class_selected_conf').click(function () {
+        var gljClass =  $('#selected_class').val();
+        var glj = gljOprObj.selectedGLJClass;
+        if(glj&&gljClass&&gljClass!=""){
+            //保存到我的工料机库
+            /*1 检查是否有组成物
+            * 2 如果有,则检查组成物中是否有新增的记录,如果有,查看是否已经保存了,没有的话,要先添加组成物到补充工料机库
+            * 3 保存
+
+            * */
+            var newItem={
+                code:glj.code,
+                name:glj.name,
+                specs:glj.specs,
+                unit:glj.unit,
+                basePrice:glj.basePrice,
+                gljType:glj.type,
+                shortName:glj.shortName,
+                component:[],
+                gljClass:gljClass
+            };
+            if(glj.hasOwnProperty("subList")&&glj.subList.length>0){//有组成物,检查组成物信息,目前组成物不允许修改,所以暂时不用考虑组成物是新增的情况
+                for(var i=0;i<glj.subList.length;i++ ){
+                    let tem={
+                        ID:glj.subList[i].GLJID,
+                        consumeAmt:glj.rationItemQuantity,
+                        isStd:true
+                    }
+                    newItem.component.push(tem);
+                }
+            }
+            var data = getcmpUpdateData([newItem]);
+            $.bootstrapLoading.start();
+            var callback = function (data) {
+                $("#glj_class_div").modal('hide');
+                $.bootstrapLoading.end();
+            }
+            CommonAjax.post("complementartGlj/api/mixUpdateGljItems", data, callback, function () {
+                $.bootstrapLoading.end();
+            });
+        }
+    })
+
+    $('#glj_class_div').on('hidden.bs.modal', function (e){
+        gljOprObj.selectedGLJClass=null;
+        $('#class_selected_conf').attr("disabled","disabled");
+        $('#selected_class').val("");
+    })
 
+    function getcmpUpdateData(items) {
+        var data ={
+            "updateItems": [],
+            "removeIds": []
+        }
+        data.addItems = items;
+        return data;
+    }
+})
+
+function getDecimal(fieldID,node) {
+    if(node){
+        return decimalObj.decimal(fieldID,node);
+    }else if(fieldID.indexOf(".")!=-1){
+        var keyArray = fieldID.split(".");
+        return decimalObj[keyArray[0]][keyArray[1]];
+    }else {
+        return decimalObj.decimal(fieldID);
+    }
+}
 
+function getFormatter(decimal) {
+    var pre = "0.";
+    if(decimal<=0){
+        return "0";
+    }
+    for(i=0;i<decimal;i++){
+        pre += "0"
+    }
+    return pre;
+}

+ 0 - 0
web/building_saas/main/js/views/glj_view_contextMenu.js


Some files were not shown because too many files changed in this diff