Ver código fonte

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

zhangyin 7 anos atrás
pai
commit
45652608b4
100 arquivos alterados com 3353 adições e 188886 exclusões
  1. 2 1
      .gitignore
  2. 12 7
      config/gulpConfig.js
  3. 2 2
      gulpfile.js
  4. 17 1
      modules/common/base/base_model.js
  5. 16 12
      modules/complementary_glj_lib/controllers/gljController.js
  6. 27 4
      modules/complementary_glj_lib/models/gljModel.js
  7. 1 1
      modules/complementary_glj_lib/models/schemas.js
  8. 37 1
      modules/fee_rates/controllers/fee_rates_controller.js
  9. 5 2
      modules/fee_rates/facade/fee_rates_facade.js
  10. 1 0
      modules/fee_rates/models/fee_rates.js
  11. 2 0
      modules/fee_rates/routes/fee_rates_route.js
  12. 32 9
      modules/glj/controllers/glj_controller.js
  13. 258 92
      modules/glj/models/glj_list_model.js
  14. 1 1
      modules/glj/models/mix_ratio_model.js
  15. 11 3
      modules/glj/models/schemas/glj.js
  16. 20 4
      modules/glj/models/schemas/mix_ratio.js
  17. 21 2
      modules/glj/models/schemas/unit_price.js
  18. 2 1
      modules/glj/models/schemas/unit_price_file.js
  19. 12 1
      modules/glj/models/unit_price_file_model.js
  20. 113 24
      modules/glj/models/unit_price_model.js
  21. 1 0
      modules/glj/routes/glj_router.js
  22. 4 3
      modules/main/controllers/bills_controller.js
  23. 14 1
      modules/main/controllers/calc_program_controller.js
  24. 18 2
      modules/main/controllers/labour_coe_controller.js
  25. 4 0
      modules/main/controllers/project_controller.js
  26. 34 2
      modules/main/facade/calc_program_facade.js
  27. 34 2
      modules/main/facade/labour_coe_facade.js
  28. 1 1
      modules/main/models/calc_program_model.js
  29. 1 1
      modules/main/models/labour_coe_model.js
  30. 28 5
      modules/main/models/project.js
  31. 2 1
      modules/main/models/project_consts.js
  32. 1 0
      modules/main/routes/calc_program_route.js
  33. 1 0
      modules/main/routes/labour_coe_route.js
  34. 5 4
      modules/options/controllers/optionsController.js
  35. 90 43
      modules/pm/controllers/pm_controller.js
  36. 222 6
      modules/pm/models/project_model.js
  37. 51 0
      modules/pm/models/project_property_bills_quantity_decimal.js
  38. 4 0
      modules/pm/routes/pm_route.js
  39. 20 2
      modules/ration_glj/controllers/ration_glj_controller.js
  40. 5 5
      modules/ration_glj/facade/glj_calculate_facade.js
  41. 4 6
      modules/ration_glj/facade/quantity_detail_facade.js
  42. 1 0
      modules/ration_glj/facade/ration_coe_facade.js
  43. 89 115
      modules/ration_glj/facade/ration_glj_facade.js
  44. 8 4
      modules/ration_glj/models/ration_glj.js
  45. 1 0
      modules/ration_glj/routes/ration_glj_route.js
  46. 1 1
      modules/ration_repository/models/ration_item.js
  47. 44 35
      modules/reports/controllers/rpt_controller.js
  48. 3 0
      modules/reports/controllers/rpt_tpl_controller.js
  49. 10 4
      modules/reports/routes/report_router.js
  50. 17 15
      modules/reports/rpt_component/helper/jpc_helper_flow_tab.js
  51. 6 0
      modules/reports/rpt_component/helper/jpc_helper_text.js
  52. 4 3
      modules/reports/rpt_component/jpc_data.js
  53. 18 7
      modules/reports/rpt_component/jpc_ex.js
  54. 444 33
      modules/reports/rpt_component/jpc_flow_tab.js
  55. 42 4
      modules/reports/rpt_component/jpc_rte.js
  56. 275 73
      modules/reports/util/rpt_construct_data_util.js
  57. 196 69
      public/calc_util.js
  58. 8 8
      public/fsUtil.js
  59. 1 1
      public/scMathUtil.js
  60. 42 42
      public/stringUtil.js
  61. 82 0
      public/web/PerfectLoad.js
  62. 0 23
      public/web/calculation/calc_util.js
  63. 12 3
      public/web/number_util.js
  64. 14 1
      public/web/rpt_value_define.js
  65. 10 1
      public/web/scMathUtil.js
  66. 21 21
      public/web/sheet/sheet_common.js
  67. 10 0
      public/web/sheet/sheet_data_helper.js
  68. 7 7
      public/web/string_util_light.js
  69. 1 1
      public/web/tree_table/tree_table.js
  70. 13 0
      test/calculation/testArrayCalc.js
  71. 337 0
      test/unit/reports/rpt_cfg.js
  72. 16 0
      test/unit/reports/rpt_test_decimal.js
  73. 65 18
      test/unit/reports/test_tpl_09_1.js
  74. 0 18062
      tmp/07_1.page.js
  75. 0 14
      tmp/excel_test_raw_data/08-2/[Content_Types].xml
  76. 0 6
      tmp/excel_test_raw_data/08-2/_rels/.rels
  77. 0 26
      tmp/excel_test_raw_data/08-2/docProps/app.xml
  78. 0 7
      tmp/excel_test_raw_data/08-2/docProps/core.xml
  79. 0 7
      tmp/excel_test_raw_data/08-2/xl/_rels/workbook.xml.rels
  80. 0 17365
      tmp/excel_test_raw_data/08-2/xl/sharedStrings.xml
  81. 0 205
      tmp/excel_test_raw_data/08-2/xl/styles.xml
  82. 0 281
      tmp/excel_test_raw_data/08-2/xl/theme/theme1.xml
  83. 0 12
      tmp/excel_test_raw_data/08-2/xl/workbook.xml
  84. 0 151683
      tmp/excel_test_raw_data/08-2/xl/worksheets/sheet1.xml
  85. 1 0
      web/building_saas/complementary_glj_lib/html/tools-gongliaoji.html
  86. 89 97
      web/building_saas/complementary_glj_lib/js/glj.js
  87. 5 4
      web/building_saas/complementary_glj_lib/js/gljComponent.js
  88. 6 9
      web/building_saas/css/main.css
  89. 3 11
      web/building_saas/fee_rates/fee_rate.html
  90. 2 2
      web/building_saas/glj/html/glj_index.html
  91. 3 2
      web/building_saas/glj/js/common_spread.js
  92. 4 0
      web/building_saas/glj/js/composition.js
  93. 11 3
      web/building_saas/glj/js/composition_spread.js
  94. 19 11
      web/building_saas/glj/js/project_glj.js
  95. 23 16
      web/building_saas/glj/js/project_glj_spread.js
  96. 2 2
      web/building_saas/js/global.js
  97. 6 6
      web/building_saas/main/html/calc_program_manage.html
  98. 232 311
      web/building_saas/main/html/main.html
  99. 13 3
      web/building_saas/main/js/calc/bills_calc.js
  100. 0 0
      web/building_saas/main/js/controllers/project_controller.js

+ 2 - 1
.gitignore

@@ -1,4 +1,5 @@
 node_modules/
 .git/
 dist/
-.idea/
+.idea/
+tmp/

+ 12 - 7
config/gulpConfig.js

@@ -9,7 +9,8 @@ module.exports = {
         'lib/popper/popper.min.js',
         'lib/bootstrap/bootstrap.min.js',
         'web/building_saas/js/*.js',
-        'public/web/scMathUtil.js'
+        'public/web/scMathUtil.js',
+        'public/web/PerfectLoad.js'
     ],
     common_css:[
         'lib/bootstrap/css/bootstrap.min.css',
@@ -39,6 +40,7 @@ module.exports = {
         '!lib/JSExpressionEval_src/JsHashMap.js',
         'lib/jquery-contextmenu/*.js',
         'lib/lodash/lodash.js',
+        'web/building_saas/main/js/models/main_consts.js',
         'web/building_saas/glj/js/project_glj.js',
         'web/building_saas/glj/js/composition.js',
         'web/building_saas/glj/js/common_spread.js',
@@ -48,6 +50,8 @@ module.exports = {
         'public/web/socket/connection.js',
         'public/web/uuid.js',
         'public/web/sheet/sheet_common.js',
+        'web/building_saas/main/js/models/calc_program.js',
+        'web/building_saas/main/js/views/calc_program_manage.js',
         'public/web/common_ajax.js',
         'public/web/url_util.js',
         'public/web/number_util.js',
@@ -57,12 +61,12 @@ module.exports = {
        // 'lib/spreadjs/views/gc.spread.views.dataview.10.0.0.min.js',
        // "lib/spreadjs/views/common/gc.spread.common.10.0.0.min.js",
       //  'lib/spreadjs/views/plugins/gc.spread.views.gridlayout.10.0.0.min.js',
-        'web/building_saas/main/js/models/main_consts.js',
         'web/building_saas/main/js/models/project.js',
         'web/building_saas/main/js/models/bills.js',
         'web/building_saas/main/js/models/ration.js',
         'web/building_saas/main/js/models/glj.js',
         'web/building_saas/main/js/models/project_glj.js',
+        'web/building_saas/main/js/models/composition.js',
         'web/building_saas/main/js/models/fee_rate.js',
         'web/building_saas/main/js/models/ration_glj.js',
         'web/building_saas/main/js/models/ration_coe.js',
@@ -70,7 +74,6 @@ module.exports = {
         'web/building_saas/main/js/models/volume_price.js',
         'web/building_saas/main/js/models/labour_coe.js',
         'public/web/id_tree.js',
-        'web/building_saas/main/js/models/calc_program.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',
@@ -80,11 +83,11 @@ module.exports = {
         'public/web/tree_sheet/tree_sheet_controller.js',
         'public/web/tree_sheet/tree_sheet_helper.js',
         'public/web/sheet/sheet_data_helper.js',
-        'web/building_saas/main/js/views/calc_program_manage.js',
         'web/building_saas/main/js/views/main_tree_col.js',
         '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_decimal_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',
@@ -95,11 +98,13 @@ module.exports = {
         'web/building_saas/main/js/views/glj_view_contextMenu.js',
         'web/building_saas/main/js/views/calc_program_view.js',
         'web/building_saas/main/js/views/confirm_modal.js',
+        'public/web/rpt_tpl_def.js',
         'public/web/treeDataHelper.js',
         'public/web/ztree_common.js',
-        'public/web/rpt_tpl_def.js',
-        'web/building_saas/main/js/rpt/rpt_main.js',
-        'web/building_saas/main/js/rpt/rpt_cfg_const.js',
+        'web/building_saas/report/js/rpt_main.js',
+        'web/building_saas/report/js/rpt_cfg_const.js',
+        'web/building_saas/report/js/jpc_output_value_define.js',
+        'web/building_saas/report/js/jpc_output.js',
         'web/building_saas/main/js/views/character_content_view.js',
         'web/building_saas/main/js/views/glj_view.js',
         'web/building_saas/main/js/views/sub_view.js',

+ 2 - 2
gulpfile.js

@@ -81,7 +81,7 @@ function minify(options) {
     if(options.jspaths){
         return gulp.src(options.jspaths)
             .pipe($.plumber())
-           // .pipe(uglify())
+            .pipe(uglify())
             .pipe($.concat(options.concatName+"."+version+".js"))
             .pipe(gulp.dest(scriptsDest));
     }
@@ -110,7 +110,7 @@ function inject(options) {
 
 function htmlmin(options) {
     return gulp.src(options.htmlDest+'/*.html')
-    //  .pipe($.htmlmin({collapseWhitespace: true}))
+       // .pipe($.htmlmin({collapseWhitespace: true}))
         .pipe(gulp.dest(options.htmlDest));
 }
 

+ 17 - 1
modules/common/base/base_model.js

@@ -61,7 +61,13 @@ class BaseModel {
         if (indexBy !== null && !singleData && result.length > 0) {
             let tmpResult = {};
             for(let tmp of result) {
-                tmpResult[tmp[indexBy]] = tmp;
+                let key="";
+                if (indexBy instanceof Array){
+                    key = this.getIndex(tmp,indexBy);
+                }else {
+                    key =tmp[indexBy]?tmp[indexBy]:"";
+                }
+                tmpResult[key] = tmp;
             }
             result = tmpResult;
         }
@@ -125,6 +131,16 @@ class BaseModel {
 
         return result.ok !== undefined && result.ok === 1;
     }
+    getIndex(obj,pops){
+        let t_index = '';
+        let k_arr=[];
+        for(let p of pops){
+            let tmpK = (obj[p]==undefined||obj[p]==null||obj[p]=='')?'null':obj[p];
+            k_arr.push(tmpK);
+        }
+        t_index=k_arr.join("|-|");
+        return t_index;
+    }
 
 }
 

+ 16 - 12
modules/complementary_glj_lib/controllers/gljController.js

@@ -13,6 +13,7 @@ let callback = function(req, res, err, message, data){
 
 class GljController extends BaseController{
     async redirectGlj(req, res){
+        console.log(req.s)
         let gljLibId = null, engineeringId, sessionCompilation = req.session.sessionCompilation,
             rationValuation = sessionCompilation.ration_valuation,
             billValuation = sessionCompilation.bill_valuation,
@@ -38,14 +39,15 @@ class GljController extends BaseController{
     getGljDistType (req, res) {
         let gljDistTypeCache = stdgljutil.getStdGljTypeCacheObj().toArray();
         if(gljDistTypeCache.length >0 ){
-            callback(req, res, null, '', gljDistTypeCache);
+            callback(req, res, 0, '', gljDistTypeCache);
         }
         else {
             callback(req, res, 1, 'Error', null);
         }
     }
     getGljTree(req,res){
-        let gljLibId = req.body.gljLibId;
+        let data = JSON.parse(req.body.data);
+        let gljLibId = data.gljLibId;
         gljDao.getGljTypes(gljLibId,function(err,data){
             callback(req,res,err, 'Get Tree', data)
         });
@@ -95,9 +97,10 @@ class GljController extends BaseController{
         });
     }
     getGljItems(req, res) {
-        let stdGljLibId = req.body.stdGljLibId,
-            userId = req.body.userId,
-            compilationId = req.body.compilationId;
+        let data = JSON.parse(req.body.data);
+        let stdGljLibId = data.stdGljLibId,
+            userId = data.userId,
+            compilationId = data.compilationId
             gljDao.getGljItems(stdGljLibId, userId, compilationId, function(err, data){
                 callback(req,res,err,'Get Items',data)
             });
@@ -123,16 +126,17 @@ class GljController extends BaseController{
         })
     }
     mixUpdateGljItems(req, res){
-        let userId = req.body.userId,
-            compilationId = req.body.compilationId,
-            updateItems = JSON.parse(req.body.updateItems),
-            addItems = JSON.parse(req.body.addItems),
-            removeIds = JSON.parse(req.body.removeIds);
-        gljDao.mixUpdateGljItems(userId, compilationId, updateItems, addItems, removeIds, function(err, message, rst){
+        let user_id = req.session.sessionUser.ssoId,
+            compilation_id = req.session.sessionCompilation._id;
+        let data = JSON.parse(req.body.data);
+        let updateItems = data.updateItems,
+            addItems = data.addItems,
+            removeIds = data.removeIds;
+        gljDao.mixUpdateGljItems(user_id, compilation_id, updateItems, addItems, removeIds, function(err, message, rst){
             if (err) {
                 callback(req, res, err, message, null);
             } else {
-                callback(req, res, err, message, rst);
+                callback(req, res, 0, message, rst);
             }
         });
     }

+ 27 - 4
modules/complementary_glj_lib/models/gljModel.js

@@ -10,7 +10,7 @@ class GljDao {
     getGljTypes (gljLibId, callback){
         gljClassModel.find({"repositoryId": gljLibId, "$or": [{"isDeleted": null}, {"isDeleted": false} ]},function(err,data){
             if(data.length) {
-                callback(false,data);
+                callback(0,data);
             }
             else  if(err) callback("获取工料机类型错误!",false);
         })
@@ -37,8 +37,29 @@ class GljDao {
             else callback(false, data);
         })
     };
+    _exist(data, attr){
+        return data && data[attr] !== 'undefined' && data[attr];
+    }
+
+    sortToNumber(datas){
+        for(let i = 0, len = datas.length; i < len; i++){
+            let data = datas[i]._doc;
+            if(this._exist(data, 'basePrice')){
+                data['basePrice'] = parseFloat(data['basePrice']);
+            }
+            if(this._exist(data, 'component')){
+                for(let j = 0, jLen = data['component'].length; j < jLen; j++){
+                    let comGljObj = data['component'][j]._doc;
+                    if(this._exist(comGljObj, 'consumeAmt')){
+                        comGljObj['consumeAmt'] = parseFloat(comGljObj['consumeAmt']);
+                    }
+                }
+            }
+        }
+    }
     //获得用户的补充工料机和用户当前所在编办的标准工料机
     getGljItems (stdGljLibId, userId, compilationId, callback){
+        let me = this;
         let rst = {stdGljs: [], complementaryGljs: []};
         async.parallel([
             function (cb) {
@@ -47,6 +68,7 @@ class GljDao {
                         cb(err);
                     }
                     else{
+                        me.sortToNumber(stdGljs);
                         rst.stdGljs = stdGljs;
                         cb(null);
                     }
@@ -58,6 +80,7 @@ class GljDao {
                         cb(err);
                     }
                     else{
+                        me.sortToNumber(complementaryGljs);
                         rst.complementaryGljs = complementaryGljs;
                         cb(null);
                     }
@@ -65,10 +88,10 @@ class GljDao {
             }
         ], function (err) {
             if(err){
-                callback(true, null);
+                callback(err, null);
             }
             else{
-                callback(false, rst);
+                callback(0, rst);
             }
         })
 
@@ -105,7 +128,7 @@ class GljDao {
                 callback(err, '更新组成物错误!', null);
             }
             else{
-                callback(null, '成功!', result);
+                callback(0, '成功!', result);
             }
         });
     }

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

@@ -9,7 +9,7 @@ let comGjlComponentSchema = new Schema(
     {
         isStd: Boolean, //组成物里的工料机是否是标准的,否则是补充的
         ID: Number,
-        consumeAmt: Number
+        consumeAmt: String
     },
     {_id: false},
     {versionKey: false}

+ 37 - 1
modules/fee_rates/controllers/fee_rates_controller.js

@@ -55,7 +55,9 @@ module.exports = {
     changeFeeRateFileFromCurrent:changeFeeRateFileFromCurrent,
     changeFeeRateFileFromOthers:changeFeeRateFileFromOthers,
     setFeeRateToBill:setFeeRateToBill,
-    updateFeeRate:updateFeeRate
+    updateFeeRate:updateFeeRate,
+    updateRates:updateRates,
+    feeRateFileSaveAs:feeRateFileSaveAs
 }
 
 function libNames(req, res) {
@@ -211,3 +213,37 @@ async function updateFeeRate(req,res) {
     }
     res.json(result);
 }
+
+function updateRates(req,res){
+    let result={
+        error:0
+    }
+    let data = req.body.data;
+    data=JSON.parse(data);
+    feeRateFacde.updateRates(data.user_id,data)(function (err,re) {
+        if(err){
+            result.error=1;
+            result.message = err.message;
+        }else {
+            result.data = re;
+        }
+        res.json(result);
+    })
+}
+
+function feeRateFileSaveAs(req,res){
+    let result={
+        error:0
+    }
+    let data = req.body.data;
+    data=JSON.parse(data);
+    feeRateFacde.feeRateFileSaveAs(data.user_id,data)(function (err,re) {
+        if(err){
+            result.error=1;
+            result.message = err.message;
+        }else {
+            result.data = re;
+        }
+        res.json(result);
+    })
+}

+ 5 - 2
modules/fee_rates/facade/fee_rates_facade.js

@@ -29,7 +29,9 @@ module.exports={
     getFeeRatesByProject:getFeeRatesByProject,
     getGCFeeRateFiles: getGCFeeRateFiles ,
     setFeeRateToBill:setFeeRateToBill,
-    updateFeeRate:updateFeeRate
+    updateFeeRate:updateFeeRate,
+    updateRates:update_rates,
+    feeRateFileSaveAs:feeRateFileSaveAs
 };
 let operationMap={
     'ut_create':create_fee_rate,
@@ -260,7 +262,7 @@ async function changeFeeRateStandard(jdata){
     return doc;
 }
 
-async function newFeeRateFile(updateData){
+async function newFeeRateFile(userId, updateData){
     if(updateData.property !== null){
         let property = updateData.property;
         logger.info("Create new feeRate file for project :"+updateData.ID);
@@ -278,6 +280,7 @@ async function newFeeRateFile(updateData){
             let temA = feeFile.id.split("-");
             let libID = temA[1];
             let doc={
+                userID: userId,
                 rootProjectID:rootProjectID,
                 name:name
             };

+ 1 - 0
modules/fee_rates/models/fee_rates.js

@@ -49,6 +49,7 @@ mongoose.model('fee_rates', feeRatesSchema, 'fee_rates');
 let feeRateFileSchema = new Schema({
     ID: String,
     rootProjectID:Number,//顶层项目ID
+    userID:Number,
     name:String,
     libID: String,
     libName: String,

+ 2 - 0
modules/fee_rates/routes/fee_rates_route.js

@@ -20,6 +20,8 @@ module.exports = function (app) {
     frRouter.post('/changeFeeRateFileFromOthers', frController.changeFeeRateFileFromOthers);
     frRouter.post('/setFeeRateToBill', frController.setFeeRateToBill);
     frRouter.post('/updateFeeRate', frController.updateFeeRate);
+    frRouter.post('/updateRates', frController.updateRates);
+    frRouter.post('/feeRateFileSaveAs', frController.feeRateFileSaveAs);
     app.use('/feeRates',frRouter);
 }
 

+ 32 - 9
modules/glj/controllers/glj_controller.js

@@ -11,6 +11,7 @@ import GLJListModel from "../models/glj_list_model";
 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;
 
 const ProjectModel = require('../../pm/models/project_model').project;
 class GLJController extends BaseController {
@@ -66,10 +67,11 @@ class GLJController extends BaseController {
 
             // 先获取对应标段的项目工料机数据
             let gljListModel = new GLJListModel();
-            let [gljList, mixRatioConnectData] = await gljListModel.getListByProjectId(projectId, unitPriceFileId);
+            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,
@@ -458,24 +460,25 @@ class GLJController extends BaseController {
             compositionData = JSON.parse(JSON.stringify(compositionData));
             // 查找对应的单价数据
             let unitPriceModel = new UnitPriceModel();
-            let unitPriceData = await unitPriceModel.findDataByCondition({unit_price_file_id: currentUnitPriceId}, null, false, 'code');
+            let unitPriceData = await unitPriceModel.findDataByCondition({unit_price_file_id: currentUnitPriceId}, null, false, 'glj_id');
 
             // 整理数据
             let result = {};
             for(let composition of compositionData) {
+                let tmpId = composition.glj_id !== undefined ? composition.glj_id : -1;
                 let tmpData = {
-                    market_price: unitPriceData[composition.connect_code] !== undefined ?
-                        unitPriceData[composition.connect_code].market_price : 0,
-                    base_price: unitPriceData[composition.connect_code] !== undefined ?
-                        unitPriceData[composition.connect_code].base_price : 0,
+                    market_price: unitPriceData[tmpId] !== undefined ?
+                        unitPriceData[tmpId].market_price : 0,
+                    base_price: unitPriceData[tmpId] !== undefined ?
+                        unitPriceData[tmpId].base_price : 0,
                     consumption: composition.consumption,
                     glj_type: composition.glj_type,
                     connect_code: composition.connect_code
                 };
-                if (result[composition.connect_code] === undefined) {
-                    result[composition.connect_code] = [];
+                if (result[tmpId] === undefined) {
+                    result[tmpId] = [];
                 }
-                result[composition.connect_code].push(tmpData);
+                result[tmpId].push(tmpData);
             }
             responseData.data = result;
         } catch (error) {
@@ -563,6 +566,26 @@ class GLJController extends BaseController {
         }
         response.end('success');
     }
+
+    async  updateUnitPrice(req, res){
+        let result={
+            error:0
+        }
+        try {
+            let data = req.body.data;
+            data = JSON.parse(data);
+            let unitPriceModel = new UnitPriceModel();
+            // 更新数据
+            let datas = await unitPriceModel.updateUnitPrice(data);
+            result.data=datas;
+        }catch (err){
+            logger.err(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
+    }
+
 }
 
 export default GLJController;

+ 258 - 92
modules/glj/models/glj_list_model.js

@@ -16,6 +16,7 @@ import STDGLJLibGLJListModel from "../../common/std/std_glj_lib_glj_list_model";
 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();
 
 class GLJListModel extends BaseModel {
 
@@ -75,6 +76,7 @@ class GLJListModel extends BaseModel {
     async getListByProjectId(projectId, unitPriceFileId) {
         let gljData = null;
         let mixRatioConnectData = {};
+        let mixRationMap={};
         try {
             // 首先获取对应标段下所有的项目工料机数据
             let condition = {project_id: projectId};
@@ -105,43 +107,50 @@ class GLJListModel extends BaseModel {
             // 整理数据
             for (let tmp of quantityData) {
                 let tmpNum = parseFloat(tmp.rationQuantity);
-                tmpNum = isNaN(tmpNum) ? 1 : tmpNum;
+                tmpNum = isNaN(tmpNum) ||tmpNum==0? 1 : tmpNum;
                 if (quantityList[tmp.projectGLJID] === undefined) {
                     quantityList[tmp.projectGLJID] = tmp.quantity * tmpNum;
                 } else {
                     quantityList[tmp.projectGLJID] += tmp.quantity * tmpNum;
                 }
             }
-            // 整理获取有组成物的项目工料机编码
-            let projectGLJCode = [];
+            // 整理获取有组成物的项目工料机的数据
+            let connect_keys = [];
             for(let tmp of gljData) {
                 // 有组成物的类型且消耗量大于0才查找
                 if (quantityList[tmp.id] !== undefined) {
-                    projectGLJCode.push(tmp.code);
+                   let key = this.getIndex(tmp,['code','name','specs','unit','type']);
+                    connect_keys.push(key);
                 }
             }
             // 查找组成物的消耗量
             let totalComposition = {};
             let mixRatioData = {};
-            if (projectGLJCode.length > 0) {
+            if (connect_keys.length > 0) {
                 let mixRatioModel = new MixRatioModel();
-                condition = {connect_code: {"$in": projectGLJCode}, unit_price_file_id: unitPriceFileId};
+                condition = {connect_key: {"$in": connect_keys}, unit_price_file_id: unitPriceFileId};
                 let mixRatioList = await mixRatioModel.findDataByCondition(condition, null, false);
                 for (let tmp of mixRatioList) {
-                    totalComposition[tmp.connect_code] = totalComposition[tmp.connect_code] === undefined ? tmp.consumption :
-                        totalComposition[tmp.connect_code] + tmp.consumption;
-                    totalComposition[tmp.connect_code] = Number(totalComposition[tmp.connect_code].toFixed(4));
-
-                    if (mixRatioData[tmp.glj_id] !== undefined) {
-                        mixRatioData[tmp.glj_id].push(tmp);
+                   let t_index = tmp.connect_key;
+                    let consumption=parseFloat(tmp.consumption);
+                    totalComposition[t_index] = totalComposition[t_index] === undefined ? consumption :
+                        totalComposition[t_index] + consumption;
+                    totalComposition[t_index] = scMathUtil.roundTo(totalComposition[t_index], -4);
+
+                    if (mixRatioData[t_index] !== undefined) {
+                        mixRatioData[t_index].push(tmp);
                     } else {
-                        mixRatioData[tmp.glj_id] = [tmp];
+                        mixRatioData[t_index] = [tmp];
+                    }
+                    if(mixRationMap[t_index]!=undefined){
+                        mixRationMap[t_index].push(tmp);
+                    }else {
+                        mixRationMap[t_index]=[tmp];
                     }
-
                     if (mixRatioConnectData[tmp.glj_id] !== undefined) {
-                        mixRatioConnectData[tmp.glj_id].push(tmp.connect_code);
+                        mixRatioConnectData[tmp.glj_id].push(tmp.connect_key);
                     } else {
-                        mixRatioConnectData[tmp.glj_id] = [tmp.connect_code];
+                        mixRatioConnectData[tmp.glj_id] = [tmp.connect_key];
                     }
                 }
             }
@@ -159,9 +168,8 @@ class GLJListModel extends BaseModel {
             gljData = [];
         }
 
-        return [gljData, mixRatioConnectData];
+        return [gljData, mixRatioConnectData,mixRationMap];
     }
-
     /**
      * 组合工料机数据和单价文件数据
      *
@@ -178,8 +186,9 @@ class GLJListModel extends BaseModel {
         if (Object.keys(mixRatioData).length > 0 && Object.keys(totalComposition).length > 0) {
             for(let index in mixRatioData) {
                 for(let tmp of mixRatioData[index]) {
-                    compositionConsumption[tmp.glj_id] = compositionConsumption[tmp.glj_id] === undefined ? tmp.consumption :
-                        compositionConsumption[tmp.glj_id] + tmp.consumption;
+                    let t_index = this.getIndex(tmp,['code','name','specs','unit','type']);//取做为组成物的工料机的总消耗量
+                    compositionConsumption[t_index] = compositionConsumption[t_index] === undefined ? tmp.consumption :
+                        compositionConsumption[t_index] + tmp.consumption;
                 }
 
             }
@@ -191,44 +200,47 @@ class GLJListModel extends BaseModel {
             if (glj.code === undefined) {
                 continue;
             }
-            glj.unit_price = unitPriceList !== null && unitPriceList[glj.code + glj.name] !== undefined ? unitPriceList[glj.code + glj.name] : null;
+            let g_index = this.getIndex(glj,['code','name','specs','unit','type']);
+            glj.unit_price = unitPriceList !== null && unitPriceList[g_index] !== undefined ? unitPriceList[g_index] : null;
 
             if (glj.unit_price === null) {
                 continue;
             }
-
             let gljId = glj.glj_id + '';
             let projectGljId = glj.id + '';
-
             // 消耗量赋值
             glj.quantity = quantityList[projectGljId] !== undefined ? quantityList[projectGljId] : 0;
-            glj.quantity = totalComposition[glj.code] !== undefined ? totalComposition[glj.code] : glj.quantity;
-            glj.quantity = compositionConsumption[gljId] !== undefined ?  glj.quantity + compositionConsumption[gljId] : glj.quantity;
-            glj.quantity = parseFloat(glj.quantity).toFixed(3);
+            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);
 
             // 组成物数据
-            gljList[index].ratio_data = mixRatioData[gljId] !== undefined ? mixRatioData[gljId] : [];
-
-            glj.unit_price.base_price = parseFloat(glj.unit_price.base_price).toFixed(2);
-            glj.unit_price.market_price = parseFloat(glj.unit_price.market_price).toFixed(2);
-            // 计算调整基价
-            switch (glj.unit_price.type + '') {
-                // 人工: 调整基价=基价单价*调整系数
-                case GLJTypeConst.LABOUR:
-                    glj.adjust_price = parseFloat(glj.adjustment * glj.unit_price.base_price).toFixed(2);
-                    break;
-                // 机械类型的算法
-                case GLJTypeConst.MACHINE:
-                    console.log('机械');
-                    break;
-                // 材料、主材、设备
-                default:
-                    glj.adjust_price = glj.unit_price.base_price;
-            }
+            gljList[index].ratio_data = mixRatioData[g_index] !== undefined ? mixRatioData[g_index] : [];
+            //因为schema中设置base_price 为string 类型,所以要通过中间变量转换为数字再做计算,不然会自动变成字符串类型
+            this.getGLJPrice(glj);
             result.push(glj);
         }
         return result;
     }
+    getGLJPrice(glj){
+        let glj_basePrice = scMathUtil.roundTo(parseFloat(glj.unit_price.base_price), -2);
+        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 + '') {
+            // 人工: 调整基价=基价单价*调整系数
+            case GLJTypeConst.LABOUR:
+                glj.adjust_price = scMathUtil.roundTo(parseFloat(glj.adjustment * glj_basePrice), -2);
+                break;
+            // 机械类型的算法
+            case GLJTypeConst.MACHINE:
+                console.log('机械');
+                break;
+            // 材料、主材、设备
+            default:
+                glj.adjust_price = glj.unit_price.base_price;
+        }
+    }
 
     /**
      * 新增项目工料机数据(包括新增单价文件) 定额工料机新增时调用
@@ -242,8 +254,15 @@ class GLJListModel extends BaseModel {
             if (Object.keys(data).length <= 0) {
                 throw '新增数据为空';
             }
-            // 首先查找是否有同编码同名称的工料机数据
-            let projectGljData = await this.findDataByCondition({code: data.code, project_id: data.project_id});
+            let condition={
+                code: data.code,
+                project_id: data.project_id,
+                name:data.name,
+                specs:data.specs,
+                type:data.type,
+                unit:data.unit
+            };
+            let projectGljData = await this.findDataByCondition(condition);
             let isAddProjectGLJ = false;
             // 如果找不到数据则新增
             if (!projectGljData) {
@@ -261,12 +280,17 @@ class GLJListModel extends BaseModel {
             if (unitPriceFileId <= 0) {
                 throw '没有对应的单价文件';
             }
-
+            let CompositionGLJ=[];
             // 判断类型,如果是混凝土、砂浆或者配合比则查找对应的组成物(前提是没有对应的项目工料机数据)
-            if (isAddProjectGLJ && (data.type === GLJTypeConst.CONCRETE || data.type === GLJTypeConst.MORTAR ||
-                data.type === GLJTypeConst.MIX_RATIO || data.type === GLJTypeConst.GENERAL_MACHINE)) {
-                this.compositionInit(data, unitPriceFileId);
+            if (data.type === GLJTypeConst.CONCRETE || data.type === GLJTypeConst.MORTAR ||
+                data.type === GLJTypeConst.MIX_RATIO || data.type === GLJTypeConst.GENERAL_MACHINE) {
+                //如果是新增
+                if(isAddProjectGLJ ){
+                    await this.compositionInit(data, unitPriceFileId);
+                }
+                CompositionGLJ=await this.getCompositionGLJByData(data,unitPriceFileId);
             }
+            projectGljData.subList=CompositionGLJ;
 
             // 新增单价文件
             let unitPriceModel = new UnitPriceModel();
@@ -313,6 +337,87 @@ class GLJListModel extends BaseModel {
     }
 
     /**
+     * 修改名称、规格型号、单位、市场单价等
+     * @param data
+     * @returns {Promise.<void>}
+     */
+    async modifyGLJ(data,ration_glj){
+        let unitPriceFileModel = new UnitPriceFileModel();
+        let unitPriceFile = await unitPriceFileModel.getDataByProject(data.project_id);
+        if (!unitPriceFile) {
+            throw '没有对应的单价文件';
+        }
+        //查找单价信息,有则返回,没有则新增并返回
+        let unitPriceFileId = unitPriceFile.id;
+        let unitPriceModel = new UnitPriceModel();
+        let [unitPriceData, isAdd] = await unitPriceModel.addUnitPrice(data, unitPriceFileId);
+        let gljData={};
+        if(isAdd){ //如果是新增,则新增一条新的项目工料机
+            data.code = unitPriceData.code;
+            gljData  = await this.insertGLJWhenIsAdd(data,ration_glj,unitPriceFileId);
+        }else { //如果不是新增,则查找是否有对应的项目工料机,有则返回,没有则新增
+            let condition = {
+                project_id:data.project_id,
+                original_code: data.original_code,
+                name:data.name,
+                specs:data.specs,
+                type:data.type,
+                unit:data.unit
+            }
+            gljData = await this.findDataByCondition(condition,{_id: 0});
+            if(!gljData){
+                data.code = unitPriceData.code;
+                gljData  = await this.insertGLJWhenIsAdd(data,ration_glj,unitPriceFileId);
+            }
+        }
+        gljData.unit_price = unitPriceData;
+        return gljData
+    }
+
+    //修改属性后插入项目工料机
+    async insertGLJWhenIsAdd(glj,ration_glj,unitPriceFileId){
+        //新增项目工料机
+        let gljData = await this.add(glj);
+        //查看是否是有配合比的工料机类型
+        if (glj.type === GLJTypeConst.CONCRETE || glj.type === GLJTypeConst.MORTAR ||
+            glj.type === GLJTypeConst.MIX_RATIO || glj.type === GLJTypeConst.GENERAL_MACHINE){
+            // 配合比数据插入
+            let connect_key =this.getIndex(ration_glj,['code','name','specs','unit','type']);
+            let connect_key_n =this.getIndex(glj,['code','name','specs','unit','type']);
+            //先查找配合比数据是否已经存在
+            let mixRatioModel = new MixRatioModel();
+            let mixRatios_o = await mixRatioModel.findDataByCondition({connect_key: connect_key_n, unit_price_file_id: unitPriceFileId}, {_id: 0}, false);
+            if(mixRatios_o&&mixRatios_o.length>0){//已经存在,不用再插入配合比数据,直接返回
+                return gljData;
+            }
+            //不存在,则查找原始的配合比数据,并为新工料机增加配合比数据
+            let mixRatios = await mixRatioModel.findDataByCondition({connect_key: connect_key, unit_price_file_id: unitPriceFileId}, {_id: 0}, false);
+            let mixInsertResult ={};
+            if(mixRatios&&mixRatios.length>0){
+                let newMixRatioData = [];
+                for(let m of mixRatios){
+                    let tem ={
+                        consumption: m.consumption,
+                        glj_id: m.glj_id,
+                        unit_price_file_id: m.unit_price_file_id,
+                        connect_key: connect_key_n,
+                        type: m.type,
+                        code: m.code,
+                        specs:m.specs,
+                        name:m.name,
+                        unit:m.unit
+                    };
+                    newMixRatioData.push(tem);
+                }
+                mixInsertResult= mixRatioModel.add(newMixRatioData);
+            }
+        }
+        return gljData;
+
+    }
+
+
+    /**
      * 根据工料机id修改市场单价
      *
      * @param {Object} updateData
@@ -426,20 +531,25 @@ class GLJListModel extends BaseModel {
             throw '参数错误';
         }
         let fromTable = data.from === undefined ? 'std' : data.from;
-
         // 查找对应组成物的项目工料机数据
-        let [projectGljList, compositionGljList] = await this.getCompositionGLJList(gljId, projectId, 'name', fromTable);
+        let indexs=['code','name','specs','unit','type'];
+        let [projectGljList, compositionGljList] = await this.getCompositionGLJList(gljId, projectId, indexs, fromTable);
 
         // 整理配合比待插入数据
         let mixRatioInsertData = [];
         for (let tmp of compositionGljList) {
             // 配合比数据插入
+            var connect_key =this.getIndex(data,['code','name','specs','unit','type']);
             let mixRatioData = {
                 consumption: tmp.consumption,
                 glj_id: tmp.ID,
                 unit_price_file_id: unitPriceFileId,
-                connect_code: tmp.connectCode,
-                glj_type: tmp.gljType
+                connect_key: connect_key,
+                type: tmp.gljType,
+                code: tmp.code,
+                specs:tmp.specs,
+                name:tmp.name,
+                unit:tmp.unit
             };
             mixRatioInsertData.push(mixRatioData);
         }
@@ -452,15 +562,16 @@ class GLJListModel extends BaseModel {
             throw '组成物插入单价数据失败!';
         }
         // 如果已经存在则后续操作停止
-        if(projectGljList.length === compositionGljList.length) {
-            return;
+        if(Object.getOwnPropertyNames(projectGljList).length === compositionGljList.length) {
+            return
         }
 
         // 整理插入的数据
         let gljInsertData = [];
         let unitPriceInsertData = [];
         for(let tmp of compositionGljList) {
-            if (projectGljList[tmp.name] !== undefined) {
+            let key = this.getIndex(tmp,['code','name','specs','unit','gljType']);
+            if (projectGljList[key] !== undefined) {
                 continue;
             }
             // 项目工料机插入的数据
@@ -471,23 +582,29 @@ class GLJListModel extends BaseModel {
                 name: tmp.name,
                 specs: tmp.specs,
                 unit: tmp.unit === undefined ? '' : tmp.unit,
+                type: tmp.gljType,
+                original_code:tmp.code
             };
             gljInsertData.push(gljData);
 
+            let basePrice = scMathUtil.roundTo(tmp.basePrice,-6);
             // 单价文件插入的数据
             let unitPriceData = {
-                base_price: tmp.basePrice,
+                base_price: basePrice,
                 // 初始市场价=基价
-                market_price: tmp.basePrice,
+                market_price: basePrice,
                 code: tmp.code,
                 name: tmp.name,
                 unit_price_file_id: unitPriceFileId,
                 type: tmp.gljType,
                 short_name: tmp.shortName === undefined ? '' : tmp.shortName,
+                glj_id: tmp.ID,
+                specs: tmp.specs,
+                unit: tmp.unit === undefined ? '' : tmp.unit,
+                original_code:tmp.code
             };
             unitPriceInsertData.push(unitPriceData);
         }
-
         // 整理完后开始插入数据
         let addResult = await this.add(gljInsertData);
         if (!addResult) {
@@ -500,6 +617,7 @@ class GLJListModel extends BaseModel {
             throw '组成物插入单价数据失败!';
         }
 
+        return
     }
 
     /**
@@ -513,7 +631,7 @@ class GLJListModel extends BaseModel {
         let result = [];
         try {
             // 查找对应的项目工料机数据
-            let projectGLJData = await this.getDataById(projectGLJId);
+            let projectGLJData = await this.getDataById(projectGLJId,unitPriceFileId);
 
             let allowType = [GLJTypeConst.MIX_RATIO, GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR,
                 GLJTypeConst.GENERAL_MACHINE];
@@ -522,39 +640,13 @@ class GLJListModel extends BaseModel {
                 throw '找不到相关项目工料机';
             }
 
-            // 查找对应的项目工料机数据
-            let [gljData, compositionList] = await this.getCompositionGLJList(projectGLJData.glj_id, projectGLJData.project_id);
+            // 查找对应的项目工料机数据配合比,单价数据
+            let [gljData, mixRatioData,unitPriceData] = await this.getCompositionListByGLJ(projectGLJData, unitPriceFileId);
 
             if (gljData.length <= 0) {
                 throw '没有对应的组成物项目工料机';
             }
 
-            // 整理出code和name查找相关单价数据
-            let codeList = [];
-            let nameList = [];
-            let gljIdList = [];
-            for(let tmp of gljData) {
-                codeList.push(tmp.code);
-                nameList.push(tmp.name);
-                gljIdList.push(tmp.glj_id);
-            }
-
-            // 查找对应的单价数据
-            let unitPriceModel = new UnitPriceModel();
-            let condition = {code: {"$in": codeList}, name: {"$in": nameList}, unit_price_file_id: unitPriceFileId};
-            let unitPriceList = await unitPriceModel.findDataByCondition(condition, {_id: 0}, false);
-
-            // 查找对应的配合比数据
-            let mixRatioModel = new MixRatioModel();
-            condition = {glj_id: {"$in": gljIdList}, connect_code: projectGLJData.code, unit_price_file_id: unitPriceFileId};
-            let mixRatioData = await mixRatioModel.findDataByCondition(condition, {_id: 0}, false, 'glj_id');
-
-            // 整理数据
-            let unitPriceData = {};
-            for(let tmp of unitPriceList) {
-                unitPriceData[tmp.code + tmp.name] = tmp;
-            }
-
             gljData = this.combineData(gljData, unitPriceData, [], mixRatioData);
 
             // 排序
@@ -590,25 +682,91 @@ class GLJListModel extends BaseModel {
 
         let codeList = [];
         let nameList = [];
+        let specsList= [];
+        let typeList = [];
+        let unitList = [];
         for(let tmp of componentGljList) {
             codeList.push(tmp.code);
             nameList.push(tmp.name);
+            specsList.push(tmp.specs);
+            typeList.push(tmp.gljType);
+            unitList.push(tmp.unit);
         }
 
         // 查找对应的项目工料机数据
-        let condition = {code: {"$in": codeList}, name: {"$in": nameList}, project_id: projectId};
+        let condition = {project_id: projectId,code: {"$in": codeList}, name: {"$in": nameList},specs:{"$in": specsList},type:{"$in": typeList},unit:{"$in": unitList} };
         let gljData = await this.findDataByCondition(condition, {_id: 0}, false, indexBy);
 
         return [gljData, componentGljList];
     }
 
+    async getCompositionGLJByData(glj,unitPriceFileId){
+        let [gljData,mixRatioData,unitPriceData] = await this.getCompositionListByGLJ(glj,unitPriceFileId);
+        gljData = this.combineData(gljData, unitPriceData, [], mixRatioData);
+        return gljData;
+    }
+
+
+
+    /**
+     * 反回组成物工料机和配合比数据
+     * @param glj
+     * @param unitPriceFileId
+     * @returns {Promise.<void>}
+     */
+    async getCompositionListByGLJ(glj,unitPriceFileId){
+        let t_index = this.getIndex(glj,['code','name','specs','unit','type']);
+        // 查找对应的配合比数据
+        let mixRatioModel = new MixRatioModel();
+        let condition = {connect_key: t_index, unit_price_file_id: unitPriceFileId};
+        let mixRatios = await mixRatioModel.findDataByCondition(condition, {_id: 0}, false);
+        let codeList = [];
+        let nameList = [];
+        let specsList= [];
+        let typeList = [];
+        let unitList = [];
+        if(mixRatios.length<=0){
+            throw  '不存在对应的组成物';
+        }
+        let mixRatioData={};
+        for(let tmp of mixRatios) {
+            codeList.push(tmp.code);
+            nameList.push(tmp.name);
+            specsList.push(tmp.specs);
+            typeList.push(tmp.type);
+            unitList.push(tmp.unit);
+            let m_index = this.getIndex(tmp,['code','name','specs','unit','type']);
+            mixRatioData[m_index]=tmp;
+        }
+        // 查找对应的项目工料机数据
+        let gcondition = {project_id: glj.project_id,code: {"$in": codeList}, name: {"$in": nameList},specs:{"$in": specsList},type:{"$in": typeList},unit:{"$in": unitList} };
+        let gljData = await this.findDataByCondition(gcondition, {_id: 0}, false);
+
+        // 查找对应的单价数据
+        let unitPriceModel = new UnitPriceModel();
+        let ucondition = { unit_price_file_id: unitPriceFileId,code: {"$in": codeList}, name: {"$in": nameList},specs:{"$in": specsList},type:{"$in": typeList},unit:{"$in": unitList}};
+        let unitPriceList = await unitPriceModel.findDataByCondition(ucondition, {_id: 0}, false);
+
+
+        // 整理数据
+        let unitPriceData = {};
+        for(let tmp of unitPriceList) {
+            let u_index = this.getIndex(tmp,['code','name','specs','unit','type'])
+            unitPriceData[u_index] = tmp;
+        }
+
+
+        return [gljData,mixRatioData,unitPriceData];
+
+    }
+
     /**
      * 根据条件获取对应项目工料机数据
      *
      * @param {Number} id
      * @return {Promise}
      */
-    async getDataById(id) {
+    async getDataById(id,unitPriceFileId) {
 
         // 查找对应的项目工料机数据
         let projectGLJData = await this.findDataByCondition({id: id});
@@ -618,7 +776,15 @@ class GLJListModel extends BaseModel {
 
         // 查找对应的单价数据
         let unitPriceModel = new UnitPriceModel();
-        let unitPrice = await unitPriceModel.findDataByCondition({code: projectGLJData.code, name: projectGLJData.name});
+        let condition={
+            unit_price_file_id:unitPriceFileId,
+            code: projectGLJData.code,
+            name:projectGLJData.name,
+            specs:projectGLJData.specs,
+            type:projectGLJData.type,
+            unit:projectGLJData.unit
+        }
+        let unitPrice = await unitPriceModel.findDataByCondition(condition);
 
         projectGLJData.unit_price = unitPrice;
 

+ 1 - 1
modules/glj/models/mix_ratio_model.js

@@ -35,7 +35,7 @@ class MixRatioModel extends BaseModel {
                 this.model.schema.path('glj_id').required(true);
                 this.model.schema.path('consumption').required(true);
                 this.model.schema.path('unit_price_file_id').required(true);
-                this.model.schema.path('connect_code').required(true);
+                this.model.schema.path('connect_key').required(true);
                 break;
         }
     }

+ 11 - 3
modules/glj/models/schemas/glj.js

@@ -21,6 +21,11 @@ let modelSchema = {
         type: String,
         index: true
     },
+    //原始的编码
+    original_code: {
+        type: String,
+        index: true
+    },
     // 名称
     name: {
         type: String,
@@ -62,21 +67,24 @@ let modelSchema = {
         type: Number,
         default: 1
     },
+
     // 规格型号
     specs: {
         type: String,
         default: ''
     },
+    // 类型
+    type: Number,
     // 单位
     unit: String,
     // 显示调整基价
-    adjust_price: Number,
+    adjust_price: String,
     // 显示关联单价文件的字段
     unit_price: Schema.Types.Mixed,
     // 显示关联的消耗量
-    quantity: Number,
+    quantity: String,
     // 显示组成物的消耗量
-    consumption: Number,
+    consumption: String,
     // 显示关联配合比的id
     mix_ratio_id: Number,
     // 显示关联父级工料机code(组合物用)

+ 20 - 4
modules/glj/models/schemas/mix_ratio.js

@@ -14,7 +14,7 @@ let modelSchema = {
     id: Number,
     // 消耗量(有别于总消耗,此字段记录配合比的消耗量)
     consumption: {
-        type: Number,
+        type: String,
         default: 0
     },
     // 工料机总库对应id (关联用)
@@ -24,12 +24,28 @@ let modelSchema = {
     },
     // 单价文件表id (因为选择单价文件后配合比数据也需要同步,所以记录单价文件id)
     unit_price_file_id: Number,
-    // 关联项目工料机的code 不能关联id,因为单价文件导入别的项目后项目工料机id不同
-    connect_code: {
+    // 关联项目工料机的key 不能关联id,因为单价文件导入别的项目后项目工料机id不同
+    connect_key: {
         type: String,
         index: true
     },
-    glj_type: Number
+    // 规格型号
+    specs: {
+        type: String,
+        default: ''
+    },
+    // 单位
+    unit: String,
+    // 名称
+    name: {
+        type: String,
+        index: true,
+        default: ''
+    },
+    // 对应工料机code
+    code: String,
+    // 工料机类型
+    type: Number
 };
 let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
 export {model as default, collectionName as collectionName};

+ 21 - 2
modules/glj/models/schemas/unit_price.js

@@ -13,25 +13,44 @@ let modelSchema = {
     // 自增ID
     id: Number,
     // 基价单价
-    base_price: Number,
+    base_price: String,
     // 市场单价
-    market_price: Number,
+    market_price: String,
     // 编码
     code: {
         type: String,
         index: true
     },
+    //原始的编码
+    original_code: {
+        type: String,
+        index: true
+    },
     // 名称
     name: {
         type: String,
         index: true
     },
+    // 规格型号
+    specs: {
+        type: String,
+        default: ''
+    },
+    // 单位
+    unit: String,
     // 类型
     type: Number,
     // 类型简称
     short_name: String,
     // 单价文件表id
     unit_price_file_id: Number,
+    // 对应标准库工料机id
+    glj_id: Number,
+    //是否新增1为是,0为否
+    is_add:{
+        type: Number,
+        default: 0
+    }
 };
 let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
 export {model as default, collectionName as collectionName};

+ 2 - 1
modules/glj/models/schemas/unit_price_file.js

@@ -6,7 +6,7 @@
  * @version
  */
 import mongoose from "mongoose";
-
+let deleteSchema = require('../../../../public/models/delete_schema');
 let Schema = mongoose.Schema;
 let collectionName = 'unit_price_file';
 let modelSchema = {
@@ -23,6 +23,7 @@ let modelSchema = {
     user_id: Number,
     // 顶层projectId
     root_project_id: Number,
+    deleteInfo: deleteSchema
 };
 let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
 export {model as default, collectionName as collectionName};

+ 12 - 1
modules/glj/models/unit_price_file_model.js

@@ -127,13 +127,24 @@ class UnitPriceFileModel extends BaseModel {
     }
 
     /**
+     * 根据建设项目id获取单价文件数据
+     *
+     * @param {Number} root_project_id
+     * @return {Promise}
+     */
+    async getDataByRootProject(root_project_id){
+        let condition = {root_project_id: root_project_id, deleteInfo: null};
+        return  await this.findDataByCondition(condition, null, false);
+    }
+
+    /**
      * 根据用户获取对应被删除的单价文件
      *
      * @param {String} userID
      * @return {Promise}
      */
     async getGCUnitPriceFiles(userID){
-        let condition = {userID: userID, 'deleteInfo.deleted': true};
+        let condition = {user_id: userID, 'deleteInfo.deleted': true};
         let result = await this.findDataByCondition(condition, null, false);
         return result;
     }

+ 113 - 24
modules/glj/models/unit_price_model.js

@@ -8,7 +8,10 @@
 import BaseModel from "../../common/base/base_model"
 import GLJTypeConst from "../../common/const/glj_type_const"
 import CounterModel from "./counter_model"
+import MixRatioModel from "./mix_ratio_model";
 import {default as UnitPriceSchema, collectionName as collectionName} from "./schemas/unit_price";
+import _ from "lodash";
+const scMathUtil = require('../../../public/scMathUtil').getUtil();
 
 class UnitPriceModel extends BaseModel {
 
@@ -43,12 +46,12 @@ class UnitPriceModel extends BaseModel {
         // 整理数据
         let result = {};
         for(let tmp of unitPriceList) {
-            result[tmp.code + tmp.name] = tmp;
+            let index = this.getIndex(tmp,['code','name','specs','unit','type'])
+            result[index] = tmp;
         }
 
         return result;
     }
-
     /**
      * 设置场景
      *
@@ -78,19 +81,16 @@ class UnitPriceModel extends BaseModel {
      * @return {Promise} 返回数据以及是否新增
      */
     async addUnitPrice(data, unitPriceFileId, gljCount = 0) {
-        if (data.code === undefined || data.project_id === undefined || data.name === undefined
+        if (data.original_code===undefined||data.code === undefined || data.project_id === undefined || data.name === undefined
             || data.market_price === undefined) {
             return [null, false];
         }
-
-        // 先查找是否有同code的单价记录
-        let unitPriceData = await this.findDataByCondition({code: data.code, unit_price_file_id: unitPriceFileId}, null, false);
-
-        // 如果有记录,判断是否存在一样的市场单价,有则直接返回数据
-        data.market_price = parseFloat(data.market_price);
-        let unitPriceIndex = this.isPriceIncluded(unitPriceData, data.market_price);
-        if (unitPriceData && unitPriceIndex >= 0) {
-            return [unitPriceData[unitPriceIndex], false];
+        // 先查找是否有原始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);
+        if(unitPrice){
+            return [unitPrice, false];
         }
 
         // 如果不存在基价单价,则在数据源中获取
@@ -100,24 +100,25 @@ class UnitPriceModel extends BaseModel {
             data.type = firstUnitPrice.type !== undefined ? firstUnitPrice.type : 0;
         }
 
-        // 更改名称
-        if (gljCount > 0) {
-            let regular = /\(\d+\)/;
-            let changeString = '(' + gljCount + ')';
-            data.name = regular.test(data.name) ? data.name.replace(regular, changeString) :
-                data.name + changeString;
-        }
-
         let insertData = {
             code: data.code,
             base_price: data.base_price,
             market_price: data.market_price,
             unit_price_file_id: unitPriceFileId,
             name: data.name,
+            specs:data.specs,
+            original_code:data.original_code,
+            unit:data.unit,
             type: data.type,
-            short_name: data.shortName !== undefined ? data.shortName : ''
+            short_name: data.shortName !== undefined ? data.shortName : '',
+            glj_id: data.glj_id
         };
 
+        // 如果原始编码能找到,但不存在一样的编号,名称,单位.更改 等,更改code和添加新增标记
+        if (unitPriceData&&unitPriceData.length>0) {
+            insertData.code = data.original_code+"-"+unitPriceData.length;
+            insertData.is_add=1;
+        }
         let addPriceResult = await this.add(insertData);
         return [addPriceResult, true];
     }
@@ -161,10 +162,25 @@ class UnitPriceModel extends BaseModel {
                 break;
             }
         }
-
         return index;
     }
 
+    isPropertyInclude(data,pops,obj){
+        let condition={};
+        if (data.length <= 0) {
+            return null;
+        }
+        if(pops instanceof  Array){
+            for(let p of pops){
+                if(obj[p]){
+                    condition[p]=obj[p]
+                }
+            }
+        }else {
+            condition[pops]=obj[pops]
+        }
+        return _.find(data,condition);
+    }
     /**
      * 更新市场单价
      *
@@ -183,14 +199,14 @@ class UnitPriceModel extends BaseModel {
             throw '找不到对应的单价数据';
         }
 
-        // 基价单价的计算
+       /* // 基价单价的计算-----先不考虑同步
         switch (unitPriceData.type) {
             // 主材、设备自动赋值基价单价=市场单价
             case GLJTypeConst.MAIN_MATERIAL:
             case GLJTypeConst.EQUIPMENT:
                 updateData.base_price = updateData.market_price;
                 break;
-        }
+        }*/
 
         // 额外更新数据
         if (extend !== '') {
@@ -214,6 +230,79 @@ class UnitPriceModel extends BaseModel {
         return result.ok !== undefined && result.ok === 1;
     }
 
+    async updateUnitPrice(data){
+        //查找并更新单价
+        let doc={};
+        doc[data.field]=data.newval;
+        let unitPrice = await this.db.findAndModify({id:data.id},doc);
+        if(!unitPrice){
+            throw "没有找到对应的单价";
+        }
+        //查找是否是属于某个项目工料机的组成物
+        let mixRatioModel = new MixRatioModel();
+        let condition = {unit_price_file_id:unitPrice.unit_price_file_id, code:unitPrice.code,name: unitPrice.name, specs: unitPrice.specs,unit:unitPrice.unit,type:unitPrice.type};
+        let mixRatioList = await mixRatioModel.findDataByCondition(condition, null, false);
+        let connectKeyMap={};
+        //找到则计算项目工料机组成物的价格并更新
+        let rList= [];
+        if(mixRatioList&&mixRatioList.length>0){
+            for(let m of mixRatioList){
+                if(!connectKeyMap.hasOwnProperty(m.connect_key)){//为了去重复,组成物会与其它项目同步,所以有可能重复。
+                    rList.push(await this.updateParentUnitPrice(m,data.field));
+                    connectKeyMap[m.connect_key]=true;
+                }
+            }
+        }
+        return rList;
+    }
+
+    async updateParentUnitPrice(mixRatio,fieid){
+        //查找该工料机所有组成物
+        let indexList = ['code','name','specs','unit','type'];
+        let mixRatioModel = new MixRatioModel();
+        let mixRatioMap = await mixRatioModel.findDataByCondition({unit_price_file_id:mixRatio.unit_price_file_id,connect_key:mixRatio.connect_key}, null, false,indexList);
+        //查找对应的价格
+        let codeList = [];
+        let nameList = [];
+        let specsList= [];
+        let typeList = [];
+        let unitList = [];
+        for(let mk in mixRatioMap){
+            codeList.push(mixRatioMap[mk].code);
+            nameList.push(mixRatioMap[mk].name);
+            specsList.push(mixRatioMap[mk].specs);
+            typeList.push(mixRatioMap[mk].type);
+            unitList.push(mixRatioMap[mk].unit);
+        }
+        let condition = {unit_price_file_id: mixRatio.unit_price_file_id,code: {"$in": codeList}, name: {"$in": nameList},specs:{"$in": specsList},type:{"$in": typeList},unit:{"$in": unitList}};
+        let priceMap = await this.findDataByCondition(condition, {_id: 0}, false, indexList);
+        let sumPrice=0;
+        for(let pk in priceMap){
+            let price = parseFloat(priceMap[pk][fieid]);
+            let consumption = parseFloat(mixRatioMap[pk].consumption);
+            sumPrice +=scMathUtil.roundTo(price*consumption,-6);
+        }
+        sumPrice= scMathUtil.roundTo(sumPrice,-6);
+        if(sumPrice<=0){
+            return null;
+        }
+        //更新父价格
+        let keyList = mixRatio.connect_key.split("|-|");
+        let pcondition = {
+            unit_price_file_id:mixRatio.unit_price_file_id,
+            code:keyList[0]
+        };
+        for(let i = 1;i<keyList.length;i++){
+            if(keyList[i]!='null'){
+                pcondition[indexList[i]]=keyList[i];
+            }
+        }
+        let doc={};
+        doc[fieid]=sumPrice;
+        let uprice =  await this.db.findAndModify(pcondition,doc);
+        uprice[fieid]=sumPrice;
+        return uprice;
+    }
     /**
      * 复制单价文件数据
      *

+ 1 - 0
modules/glj/routes/glj_router.js

@@ -21,6 +21,7 @@ router.post('/get-project-info', gljController.init, gljController.getProjectInf
 router.post('/change-file', gljController.init, gljController.changeUnitPriceFile);
 router.post('/save-as', gljController.init, gljController.unitPriceSaveAs);
 router.post('/get-composition', gljController.init, gljController.getComposition);
+router.post('/updatePrice', gljController.init, gljController.updateUnitPrice);
 
 router.get('/test', gljController.init, gljController.test);
 router.get('/testModify', gljController.init, gljController.testModify);

+ 4 - 3
modules/main/controllers/bills_controller.js

@@ -42,9 +42,10 @@ module.exports = {
     },
     //zhong 2017-9-1
     updateCharacterContent: function (req, res) {
-        let findSet = JSON.parse(req.body.findSet),
-            updateObj = JSON.parse(req.body.updateObj),
-            txtObj = JSON.parse(req.body.txtObj);
+        let data = JSON.parse(req.body.data);
+        let findSet = data.findSet,
+            updateObj = data.updateObj,
+            txtObj = data.txtObj;
         billsData.updateCharacterContent(findSet, updateObj, txtObj, function (err, message) {
             callback(req, res, err, message, null);
         });

+ 14 - 1
modules/main/controllers/calc_program_controller.js

@@ -8,7 +8,8 @@ let calcProgramFacade = require('../facade/calc_program_facade');
 module.exports = {
     getProjectCalcProgram: getProjectCalcProgram,
     getStdCalcProgram: getStdCalcProgram,
-    saveCalcItem: saveCalcItem
+    saveCalcItem: saveCalcItem,
+    saveCalcItems: saveCalcItems
 };
 
 async function getProjectCalcProgram(req, res) {
@@ -50,3 +51,15 @@ async function saveCalcItem(req, res) {
     });
     res.json(result);
 };
+
+async function saveCalcItems(req, res) {
+    let result = {error: 0, message: ''};
+
+    calcProgramFacade.saveCalcItems(req.body.data, function (err, msg) {
+        if (err) {
+            result.error = 1;
+        };
+        result.message = msg;
+    });
+    res.json(result);
+};

+ 18 - 2
modules/main/controllers/labour_coe_controller.js

@@ -7,7 +7,8 @@ let labourCoeFacade = require('../facade/labour_coe_facade');
 
 module.exports = {
     getProjectLabourCoe: getProjectLabourCoe,
-    getStdLabourCoe: getStdLabourCoe
+    getStdLabourCoe: getStdLabourCoe,
+    save: save
 };
 
 async function getProjectLabourCoe(req, res) {
@@ -29,7 +30,7 @@ async function getStdLabourCoe(req, res) {
     let result = {error: 0, message: '', data: null};
 
     try {
-        let stdLC = await labourCoeFacade.getStdLabourCoe(req.body.ID);
+        let stdLC = await labourCoeFacade.getStdLabourCoe(JSON.parse(req.body.data).ID);
         result.data= stdLC;
     }catch (err){
         console.log(err);
@@ -39,3 +40,18 @@ async function getStdLabourCoe(req, res) {
 
     res.json(result);
 };
+
+function save(req, res) {
+    let result = {error: 0, message: '', data: null};
+
+    labourCoeFacade.save(req.body.data, function (err, message, data) {
+        if (err == 0){
+            result.data = data;
+        }else{
+            result.error = 1;
+            result.message = message;
+        }
+    });
+
+    res.json(result);
+};

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

@@ -22,6 +22,10 @@ module.exports = {
     },
     getData: function (req, res) {
         var data = JSON.parse(req.body.data);
+        // 注释代码用于测试getFilterData
+        // Project.getFilterData(data.project_id, ['bills', 'projectGLJ'], function (err, result) {
+        //     console.log(result);
+        // });
         Project.getData(data.project_id, function (err, message, result) {
             if (!err) {
                 callback(req, res, err, message, result);

+ 34 - 2
modules/main/facade/calc_program_facade.js

@@ -18,7 +18,8 @@ module.exports = {
     getStdCalcProgramFile: getStdCalcProgramFile,
     getData: getData,
     save: save,
-    saveCalcItem: saveCalcItem
+    saveCalcItem: saveCalcItem,
+    saveCalcItems: saveCalcItems
 };
 
 async function newProjectCalcProgramFile(data) {
@@ -114,11 +115,42 @@ function saveCalcItem(dataObj, callback) {
             callback( {err:1, msg:'没有找到计算程序文件'} );
         }
     });
+};
 
+// {  projectID: projectID, calcItems: [{templatesID, calcItem}]  }
+function saveCalcItems(datas, callback) {
+    let datasObj = JSON.parse(datas);
+    projectCalcProgramsModel.findOne({projectID: datasObj.projectID}, function (err, data) {
+        if(!err){
+            for (let cI of datasObj.calcItems){
+                for (let i = 0; i < data.templates.length; i++){
+                    if (data.templates[i].ID == cI.templatesID){
+                        for (let j=0; j < data.templates[i].calcItems.length; j++){
+                            if (data.templates[i].calcItems[j].ID == cI.calcItem.ID){
+                                data.templates[i].calcItems[j] = cI.calcItem;
+                                data.save(function (err) {
+                                    if (err) {
+                                        callback({err:1, msg:'本条计算规则保存失败'});
+                                    } else {
+                                        callback({err:0, msg:'本条计算规则保存成功'});
+                                    }
+                                });
+                                break;
+                            };
+                        };
+                        break;
+                    };
+                };
+            };
+        }
+        else {
+            callback( {err:1, msg:'没有找到计算程序文件'} );
+        }
+    });
 };
 
 // for test
 /*let udata = {ID:8, code: '8.8.8', name: '被改成了888', hehe: '增加的属性'};
-saveCalcItem({projectID: 597, templatesID: 4, calcItemID: 8, data: udata}, function (data) {
+saveCalcItem({projectID: 597, templatesID: 4, data: udata}, function (data) {
     console.log({msg:data.msg, data: data.data});
 })*/

+ 34 - 2
modules/main/facade/labour_coe_facade.js

@@ -83,6 +83,38 @@ function getData(projectID, callback) {
 };
 
 // 统一的 save() 方法供project调用
-function save (user_id, datas, callback) {
-    projectLabourCoesModel.update({"projectID": 553}, {"libName":"goo"}, callback(null, {data: 'ok'}));
+// data 格式要求:{projectID: projectID, libID: libID, libName: libName, newItemArr: needUpdateDatas}
+// needUpdateDatas: [{ID: 5, value: 2.87}, {ID: 13, value: 2.91}]
+function save (data, callback) {
+    let updateArr = [];
+    let datas = JSON.parse(data);
+
+    if (datas.libID){
+        let ItemObj = {
+            updateOne: {
+                filter: {projectID: datas.projectID},
+                update: { 'libID': datas.projectID, 'libName': datas.libName }
+            }
+        };
+        updateArr.push(ItemObj);
+    };
+
+    for (let Item of datas.newItemArr) {
+         let ItemObj = {
+             updateOne: {
+                 filter: {projectID: datas.projectID, 'coes.ID': Item.ID},
+                 update: { 'coes.$.coe': Item.coe }
+             }
+         };
+         updateArr.push(ItemObj);
+    };
+    // console.log(JSON.stringify(updateArr));
+    projectLabourCoesModel.bulkWrite(updateArr)
+        .then(function(){
+            logger.info(`Project LabourCoe saved successful : ${datas.projectID}`);
+            callback(0, '', null);
+        })
+        .catch(function (err) {
+            logger.err(err);
+            callback(1, err, null)});
 }

+ 1 - 1
modules/main/models/calc_program_model.js

@@ -14,7 +14,7 @@ let calcItemSchema = new Schema({
     compiledExpr: String,
     statement: String,
     feeRateID: Number,
-    feeRate: Number,
+    feeRate: String,
     labourCoeID: Number
 },{versionKey:false, _id: false});
 

+ 1 - 1
modules/main/models/labour_coe_model.js

@@ -8,7 +8,7 @@ let coeSchema = new Schema({
     ID: Number,
     ParentID: Number,
     name: String,
-    coe: Number,
+    coe: String,
     _id: false
 },{versionKey:false});
 

+ 28 - 5
modules/main/models/project.js

@@ -14,9 +14,13 @@ let projSetting = require('./proj_setting_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');
+
+const ProjectModel = require('../../pm/models/project_model').project;
+import GLJListModel from "../../glj/models/glj_list_model";
+
 var consts = require('./project_consts');
 var projectConsts = consts.projectConst;
-var async = require("async");
+var asyncTool = require("async");
 
 var moduleMap = {};
 
@@ -73,7 +77,7 @@ Project.prototype.save = function(datas, callback){
         functions.push(saveModule(item));
     }
 
-    async.parallel(functions, function(err, results) {
+    asyncTool.parallel(functions, function(err, results) {
         if (!err){
             callback(null, '', results)
         }
@@ -98,7 +102,7 @@ Project.prototype.getData = function(projectID, callback){
         })(itemName))
     }
 
-    async.parallel(functions, function(err, results) {
+    asyncTool.parallel(functions, function(err, results) {
         if (!err){
             callback(null, '', results)
         }
@@ -111,11 +115,29 @@ Project.prototype.getData = function(projectID, callback){
 Project.prototype.getFilterData = function (projectID, filter, callback) {
     let functions = [];
     let getModuleData = function (moduleName) {
-        return function (cb) {
+        return async function (cb) {
             if (moduleMap[moduleName]) {
                 moduleMap[moduleName].getData(projectID, function (err, name, data) {
                     cb(err, {'moduleName': name, 'data': data})
                 });
+            } else if (moduleName === projectConsts.PROJECTGLJ) {
+                try {
+                    if (isNaN(projectID) || projectID <= 0) {
+                        throw '标段id有误';
+                    }
+                    // 获取标段对应的单价文件id
+                    let unitPriceFileId = await ProjectModel.getUnitPriceFileId(projectID);
+                    if (unitPriceFileId <= 0) {
+                        throw '没有对应的单价文件';
+                    }
+                    // 先获取对应标段的项目工料机数据
+                    let gljListModel = new GLJListModel();
+                    let [gljList, mixRatioConnectData] = await gljListModel.getListByProjectId(projectID, unitPriceFileId);
+
+                    cb(null, {'moduleName': moduleName, 'data': gljList});
+                } catch (error) {
+                    cb(error, null);
+                }
             } else {
                 throw '要查询的项目模块不存在';
             }
@@ -124,7 +146,8 @@ Project.prototype.getFilterData = function (projectID, filter, callback) {
     for (let itemName of filter) {
         functions.push(getModuleData(itemName));
     }
-    async.parallel(functions, function (err, results) {
+    asyncTool.parallel(functions, function (err, results) {
+        //console.log(results);
         if (err) {
             throw '获取项目数据出错';
         } else {

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

@@ -33,7 +33,8 @@ let projectConstList = [
     'properties',
     'volume_price',
     'feeRate',
-    'labour_coe'
+    'labour_coe',
+    'calc_program'
 ];
 
 let commonConst = {

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

@@ -12,6 +12,7 @@ module.exports = function (app) {
     cpRouter.post('/getProjectCalcProgram', cpController.getProjectCalcProgram);
     cpRouter.post('/getStdCalcProgram', cpController.getStdCalcProgram);
     cpRouter.post('/saveCalcItem', cpController.saveCalcItem);
+    cpRouter.post('/saveCalcItems', cpController.saveCalcItems);
 
     app.use('/calcProgram',cpRouter);
 }

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

@@ -11,6 +11,7 @@ module.exports = function (app) {
 
     lcRouter.post('/getProjectLabourCoe', lcController.getProjectLabourCoe);
     lcRouter.post('/getStdLabourCoe', lcController.getStdLabourCoe);
+    lcRouter.post('/save', lcController.save);
 
     app.use('/labourCoe',lcRouter);
 }

+ 5 - 4
modules/options/controllers/optionsController.js

@@ -10,7 +10,7 @@ let optionsDao = new OptionsDao();
 class OptionController extends BaseController {
     //获得所有选项类型的选项
     async getOptions(req, res){
-        let resJson = {error: null, message: '', data: []};
+        let resJson = {error: 0, message: '', data: []};
         let user_id = req.session.sessionUser.id,
             compilation_id = req.session.sessionCompilation._id;
         let defaultOpts = {
@@ -58,11 +58,12 @@ class OptionController extends BaseController {
     }
 
     async saveOptions(req, res){
-        let resJson = {error: null, message: '', data: null};
+        let resJson = {error: 0, message: '', data: null};
+        let data = JSON.parse(req.body.data);
         let user_id = req.session.sessionUser.id,
             compilation_id = req.session.sessionCompilation._id,
-            optsType = req.body.optsType,
-            opts = JSON.parse(req.body.opts);
+            optsType = data.optsType,
+            opts = data.opts;
         try{
             let hasThisOpts = false;
             for(let i in optionsTypes){

+ 90 - 43
modules/pm/controllers/pm_controller.js

@@ -4,6 +4,7 @@
 import UnitPriceFileModel from "../../glj/models/unit_price_file_model";
 let ProjectsData = require('../models/project_model').project;
 let projType = require('../models/project_model').projType;
+let fileType = require('../models/project_model').fileType;
 const engineering = require("../../common/const/engineering");
 let EngineeringLibModel = require("../../users/models/engineering_lib_model");
 let fee_rate_facade = require("../../fee_rates/facade/fee_rates_facade");
@@ -57,6 +58,13 @@ module.exports = {
             }
         });
     },
+    updateFiles: async function(req, res){
+        let data = JSON.parse(req.body.data);
+        let updateDatas = data.updateDatas;
+        await ProjectsData.udpateUserFiles(req.session.sessionUser.ssoId, updateDatas, function (err, message, data) {
+            callback(req, res, err, message, data);
+        });
+    },
     copyProjects: function (req, res) {
         let data = JSON.parse(req.body.data);
         ProjectsData.copyUserProjects(req.session.sessionUser.ssoId, data.updateData, function (err, message, data) {
@@ -144,14 +152,14 @@ module.exports = {
             if (isNaN(projectId) && projectId <= 0) {
                 throw {msg: 'id数据有误!', err: 1};
             }
-            // 获取对应建设项目下所有的单位工程id
+            /*// 获取对应建设项目下所有的单位工程id
             let idList = await ProjectsData.getTenderByProjectId(projectId);
             if (idList.length <= 0) {
                 throw {msg: '不存在对应单位工程', err: 0};
-            }
+            }*/
             // 获取对应的单价文件
             let unitPriceFileModel = new UnitPriceFileModel();
-            let unitPriceFileData = await unitPriceFileModel.getDataByTenderId(idList);
+            let unitPriceFileData = await unitPriceFileModel.getDataByRootProject(projectId);
 
             if (unitPriceFileData === null) {
                 throw {msg: '不存在对应单价文件', err: 0};
@@ -191,61 +199,100 @@ module.exports = {
             callback(request, response, error.err, error.msg, responseData);
         }
     },
-    //拼接获取回收站数据
+
     getGCDatas: async function(request, response) {
-        let userID = req.session.sessionUser.ssoId;
-        let engIdsSet = new Set(), rootIdsSet = new Set(), rst = [];
+        let userID = request.session.sessionUser.ssoId;
+        let rst = [];
+        let _projs = Object.create(null), _engs = Object.create(null), prefix = 'ID_';
         try{
-            let gc_unitPriceFiles = await ProjectsData.getGCFiles('UnitPriceFile', userID);
-            let gc_feeRateFiles = await ProjectsData.getGCFiles('FeeRateFile', userID);
+            let gc_unitPriceFiles = await ProjectsData.getGCFiles(fileType.unitPriceFile, userID);
+            let gc_feeRateFiles = await ProjectsData.getGCFiles(fileType.feeRateFile, userID);
             let gc_tenderFiles = await ProjectsData.getGCFiles(projType.tender, userID);
-            if(gc_unitPriceFiles.length > 0){
-                gc_unitPriceFiles.forEach(function (obj) {
-                    rootIdsSet.add(obj.rootProjectID);
-                });
+            for(let i = 0, len = gc_unitPriceFiles.length; i < len; i++){
+                let gc_uf = gc_unitPriceFiles[i];
+                let theProj = _projs[prefix + gc_uf.root_project_id] || null;
+                if(!theProj){
+                    let tempProj = await ProjectsData.getProjectsByIds([gc_uf.root_project_id]);
+                    if(tempProj.length > 0 && tempProj[0].projType !== projType.folder){
+                        theProj = _projs[prefix + gc_uf.root_project_id] = tempProj[0]._doc;
+                        buildProj(theProj);
+                    }
+                }
+                if(theProj){
+                    theProj.unitPriceFiles.push(gc_uf);
+                }
             }
-            if(gc_feeRateFiles.length > 0){
-                gc_feeRateFiles.forEach(function (obj) {
-                    rootIdsSet.add(obj.rootProjectID);
-                });
+            for(let i = 0, len = gc_feeRateFiles.length; i < len; i++){
+                let gc_ff = gc_feeRateFiles[i];
+                let theProj = _projs[prefix + gc_ff.rootProjectID] || null;
+                if(!theProj){
+                    let tempProj = await ProjectsData.getProjectsByIds([gc_ff.rootProjectID]);
+                    if(tempProj.length > 0 && tempProj[0].projType !== projType.folder){
+                        theProj = _projs[prefix + gc_ff.rootProjectID] = tempProj[0]._doc;
+                        buildProj(theProj);
+                    }
+                }
+                if(theProj) {
+                    theProj.feeRateFiles.push(gc_ff);
+                }
             }
             if(gc_tenderFiles.length > 0){
-                rst = rst.concat(gc_tenderFiles);
-                gc_tenderFiles.forEach(function (obj) {
-                    engIdsSet.add(obj.ParentID);
-                });
-            }
-            if(engIdsSet.size > 0){
-                let engineerings = await ProjectsData.getProjectsByIds(Array.from(engIdsSet));
-                engineerings.forEach(function (obj) {
-                    rst.push(obj);
-                    rootIdsSet.add(obj.ParentID);
-                });
-            }
-            if(rootIdsSet.size > 0){
-                let projects = await ProjectsData.getProjectsByIds(Array.from(rootIdsSet));
-                for(let i = 0, len = projects.length; i < len; i++){
-                    projects[i].unitPriceFiles = [];
-                    projects[i].feeRateFiles = [];
-                    for(let j = 0, jLen = gc_unitPriceFiles.length; j < jLen; j++){
-                        if(gc_unitPriceFiles[j].rootProjectID === projects[i].ID){
-                            projects[i].unitPriceFiles.push(gc_unitPriceFiles);
-                            break;
+                for(let i = 0, len = gc_tenderFiles.length; i < len; i++){
+                    let gc_t = gc_tenderFiles[i];
+                    let theEng = _engs[prefix + gc_t.ParentID] || null;
+                    if(!theEng){
+                        let tempEngs = await ProjectsData.getProjectsByIds([gc_t.ParentID]);
+                        if(tempEngs.length > 0 && tempEngs[0].projType === projType.engineering){
+                            theEng = _engs[prefix + gc_t.ParentID] = tempEngs[0]._doc;
+                            theEng.children = [];
                         }
                     }
-                    for(let j = 0, jLen = gc_feeRateFiles.length; j < jLen; j++){
-                        if(gc_feeRateFiles[j].rootProjectID === projects[i].ID){
-                            projects[i].feeRateFiles.push(gc_feeRateFiles[j]);
-                            break;
+                    if(theEng) {
+                        theEng.children.push(gc_t);
+                        let theProj = _projs[prefix + theEng.ParentID] || null;
+                        if(!theProj){
+                            let tempProj = await ProjectsData.getProjectsByIds([theEng.ParentID]);
+                            if(tempProj.length > 0 && tempProj[0].projType === projType.project){
+                                theProj = _projs[prefix + theEng.ParentID] = tempProj[0]._doc;
+                                buildProj(theProj);
+                            }
+                        }
+                        if(theProj) {
+                            let isExist = false;
+                            for(let j = 0, jLen = theProj.children.length; j < jLen; j++){
+                                if(theProj.children[j].ID === theEng.ID){
+                                    isExist = true;
+                                    break;
+                                }
+                            }
+                            if(!isExist){
+                                theProj.children.push(theEng);
+                            }
                         }
                     }
                 }
-                rst = rst.concat(projects);
+            }
+            for(let i in _projs){
+                rst.push(_projs[i]);
+            }
+            function buildProj(proj){
+                proj.children = [];
+                proj.unitPriceFiles = [];
+                proj.feeRateFiles = [];
             }
             callback(request, response, null, 'success', rst);
         }
         catch (error){
-            callback(request, response, true, 'fail', null);
+            callback(request, response, true, error, null);
         }
+    },
+
+    recGC: function(request, response){
+        let userID = request.session.sessionUser.ssoId;
+        let data = JSON.parse(request.body.data);
+        let nodes = data.nodes;
+        ProjectsData.recGC(userID, nodes, function (err, msg, data) {
+            callback(request, response, err, msg, data);
+        });
     }
 };

+ 222 - 6
modules/pm/models/project_model.js

@@ -1,7 +1,12 @@
 /**
  * Created by Mai on 2017/1/18.
  */
+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';
+let FeeRateFiles = mongoose.model('fee_rate_file');
 let counter = require("../../../public/counter/counter.js");
 
 let newProjController = require('../controllers/new_proj_controller');
@@ -18,6 +23,17 @@ let projectType = {
     project: 'Project',
     engineering: 'Engineering',
 };
+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,14 +93,18 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, datas, callbac
                         name: data.updateData.name,
                         project_id: data.updateData.ID,
                         user_id: userId,
-                        root_project_id: data.property.rootProjectID
+                        root_project_id: data.updateData.property.rootProjectID
                     };
                     let addResult = await unitPriceFileModel.add(insertData);
                     if (!addResult) {
                         callback(1, '新增单价文件失败.', null);
                         return;
                     }
-                    data.updateData.property.unitPriceFile.id = addResult.id + '';
+                    data.updateData.property.unitPriceFile.id = addResult.id;
+                }
+                if(data.updateData.projType === projectType.tender){
+                    data.updateData.property.decimal = defaultDecimal;
+                    data.updateData.property.billsQuantityDecimal = billsQuantityDecimal;
                 }
                 newProject = new Projects(data.updateData);
                 // 查找同级是否存在同名数据
@@ -94,7 +114,7 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, datas, callbac
                     return;
                 }
                 if(data.updateData.projType==='Tender'){
-                    let  feeRateFileID = await feeRateFacade.newFeeRateFile(data.updateData);
+                    let  feeRateFileID = await feeRateFacade.newFeeRateFile(userId, data.updateData);
                     newProject.property.feeFile = feeRateFileID?feeRateFileID:-1;
 
                     // 新建人工系数文件 CSL, 2017.10.13
@@ -117,7 +137,54 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, datas, callbac
                 deleteInfo['deleteDateTime'] = new Date();
                 deleteInfo['deleteBy'] = userId;
                 data.updateData['deleteInfo'] = deleteInfo;
-                Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
+                //Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
+                //update
+                try{
+                    if(data.updateData.projType === projectType.project){
+                        let engineerings = await Projects.find({userID: userId, ParentID: data.updateData.ID});
+                        let isExist = false;
+                        if(engineerings.length > 0){
+                            for(let j = 0, jLen = engineerings.length; j < jLen; j++){
+                                let e_tenders = await Projects.find({userID: userId, ParentID: engineerings[j].ID});
+                                if(e_tenders.length > 0){
+                                    isExist = true;
+                                    break;
+                                }
+                            }
+                        }
+                        if(isExist){//fake
+                            await UnitPriceFiles.update({user_id: userId, root_project_id: data.updateData.ID}, {$set: {deleteInfo: deleteInfo}}, {multi: true});
+                            await FeeRateFiles.update({userID: userId, rootProjectID: data.updateData.ID}, {$set: {deleteInfo: deleteInfo}}, {multi: true});
+                            await Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
+                        }
+                        else {//true
+                            await UnitPriceFiles.remove({user_id: userId, root_project_id: data.updateData.ID});
+                            await FeeRateFiles.remove({userID: userId, rootProjectID: data.updateData.ID});
+                            //await Projects.update({userID: userId, NextSiblingID: data.updateData.ID, deleteInfo: null}, {$set: {NextSiblingID: data.NextSiblingID}});
+                            await Projects.remove({userID: userId, ID: data.updateData.ID}, updateAll);
+                        }
+                    }
+                    else if(data.updateData.projType === projectType.engineering){
+                        let tenders = await Projects.find({userID: userId, ParentID: data.updateData.ID});
+                        if(tenders.length > 0){//fake
+                            await Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
+                        }
+                        else {//true
+                            //await Projects.update({userID: userId, NextSiblingID: data.updateData.ID, deleteInfo: null}, {$set: {NextSiblingID: data.NextSiblingID}});
+                            await Projects.remove({userID: userId, ID: data.updateData.ID}, updateAll);
+                        }
+                    }
+                    else if(data.updateData.projType === projectType.tender){//fake
+                        await Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
+                    }
+                    else if(data.updateData.projType === projectType.folder){//true
+                        await Projects.remove({userID: userId, ID: data.updateData.ID}, updateAll);
+                    }
+                    else throw '未知文件类型,删除失败!';
+                }
+                catch (error){
+                    callback(1, error, null);
+                }
             } else {
                 hasError = true;
                 callback(1, '提交数据错误.', null);
@@ -125,6 +192,42 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, datas, callbac
         }
     }
 };
+
+ProjectsDAO.prototype.udpateUserFiles = async function (userId, datas, callback){
+    let updateType = {update: 'update', delete: 'delete'};
+    let deleteInfo = Object.create(null);
+    deleteInfo.deleted = true;
+    deleteInfo.deleteBy = userId;
+    deleteInfo.deleteDateTime = new Date();
+    try{
+        for(let i = 0, len = datas.length; i < len; i++){
+            let data = datas[i];
+            if(data.updateType === updateType.update && data.fileType === fileType.unitPriceFile){
+                await UnitPriceFiles.update({user_id: userId, id: parseInt(data.updateData.id)}, data.updateData);
+                await Projects.update({userID: userId, 'property.unitPriceFile.id': data.updateData.id}, {$set: {'property.unitPriceFile.name': data.updateData.name}});
+            }
+            else if(data.updateType === updateType.update && data.fileType === fileType.feeRateFile){
+                await FeeRateFiles.update({userID: userId, ID: data.updateData.ID}, data.updateData);
+                await Projects.update({userID: userId, 'property.feeFile.id': data.updateData.ID}, {$set: {'property.feeFile.name': data.updateData.name}});
+            }
+            else if(data.updateType === updateType.delete && data.fileType === fileType.unitPriceFile){
+                data.updateData.deleteInfo = deleteInfo;
+                await UnitPriceFiles.update({user_id: userId, id: parseInt(data.updateData.id)}, data.updateData);
+                await Projects.update({userID: userId, 'property.feeFile.id': data.updateData.id}, {$set: {'property.feeFile.name': data.updateData.name}});
+            }
+            else if(data.updateType === updateType.delete && data.fileType === fileType.feeRateFile){
+                data.updateData.deleteInfo = deleteInfo;
+                await FeeRateFiles.update({userID: userId, ID: data.updateData.ID}, data.updateData);
+            }
+            else throw '未知文件类型,删除失败'
+        }
+        callback(false, '删除成功', null);
+    }
+    catch(error){
+        callback(true, '删除失败', null);
+    }
+};
+
 ProjectsDAO.prototype.copyUserProjects = function (userId, datas, callback) {
     let data, project, updateLength = 0, hasError = false, deleteInfo = null, tempType = 1, i;
     let updateAll = function (err) {
@@ -240,11 +343,123 @@ ProjectsDAO.prototype.getGCFiles = async function (fileType, userID){
         rst = await feeRateFacade.getGCFeeRateFiles(userID);
     }
     else {
-        rst = await Projects.find({projType: fileType, 'deleteInfo.deleted': true});
+        let isExist = false;
+        for(let type in projectType){
+            if(projectType[type] === fileType) {
+                isExist = true;
+                break;
+            }
+        }
+        if(!isExist) throw '不存在此项目类型!';
+        rst = await Projects.find({userID: userID, projType: fileType, 'deleteInfo.deleted': true});
     }
     return rst;
 };
 
+ProjectsDAO.prototype.getFirstNodeID = async function (userID, pid) {
+    let nodes = await Projects.find({userID: userID, ParentID: pid, deleteInfo: null});
+    if(nodes.length === 0){
+        return -1;
+    }
+    else {
+        let prefix = 'ID_';
+        let chain = Object.create(null);
+        for(let i = 0, len = nodes.length; i < len; i++){
+            let nodeDoc = nodes[i]._doc;
+            let node = Object.create(null);
+            node.ID = nodeDoc.ID;
+            node.NextSiblingID = nodeDoc.NextSiblingID;
+            chain[prefix + node.ID] = node;
+        }
+        for(let i =0, len = nodes.length; i < len; i++){
+            let nodeDoc = nodes[i]._doc;
+            let next = nodeDoc.NextSiblingID > 0 ? chain[prefix + nodeDoc.NextSiblingID] : null;
+            chain[prefix + nodeDoc.ID].next = next;
+            if(next){
+                next.pre = chain[prefix + nodeDoc.ID]
+            }
+        }
+        for(let node in chain){
+            let pre = chain[node].pre || null;
+            if(!pre){
+                return chain[node].ID;
+            }
+        }
+    }
+};
+
+ProjectsDAO.prototype.recGC = async function(userID, datas, callback){
+    let functions = [];
+    let updateDatas = [];
+    for(let i = 0, len = datas.length; i < len; i++){
+        if(datas[i].findData.ParentID !== undefined && datas[i].findData.NextSiblingID === -1 && !datas[i].findData.deleteInfo){//维护项目管理树
+            let findNode = await Projects.find(datas[i].findData);
+            if(findNode.length > 0){
+                datas[i].findData = Object.create(null);
+                datas[i].findData.ID = findNode[0].ID;
+                updateDatas.push(datas[i]);
+            }
+        }
+        else {
+            if(datas[i].updateType === projectType.project && datas[i].updateData.NextSiblingID === undefined){//则为待查询NextSiblingID,前端无法查询
+                let projData = await Projects.find({userID: userID, ID: datas[i].findData.ID});//建设项目原本可能属于某文件夹、文件夹的存在需要判断
+                let projPid = projData[0].ParentID;
+                if(projPid !== -1){
+                    let projFolder = await Projects.find({userID: userID, ID: projPid});
+                    if(projFolder.length === 0){//文件夹已不存在
+                        projPid = -1;
+                        datas[i].updateData.ParentID = -1;
+                    }
+                }
+                let firstNodeID = await this.getFirstNodeID(userID, projPid);
+                datas[i].updateData.NextSiblingID = firstNodeID;
+            }
+            updateDatas.push(datas[i])
+        }
+    }
+    for(let i = 0, len = updateDatas.length; i < len; i ++){
+        functions.push((function(data){
+                return function (cb) {
+                    if(data.updateType === fileType.unitPriceFile){
+                        UnitPriceFiles.update({id: parseInt(data.findData.id)}, data.updateData, function (err) {
+                            if(err) cb(err);
+                            else {
+                                Projects.update({userID: userID, 'property.unitPriceFile.id': data.findData.id}, {$set: {'property.unitPriceFile.name': data.updateData.name}}, function (err) {
+                                    if(err) cb(err);
+                                    else cb(false);
+                                });
+                            }
+                        })
+                    }
+                    else if(data.updateType === fileType.feeRateFile){
+                        FeeRateFiles.update(data.findData, data.updateData, function (err) {
+                            if(err) cb(err);
+                            else {
+                                Projects.update({userID: userID, 'property.feeFile.id': data.findData.ID}, {$set: {'property.feeFile.name': data.updateData.name}}, function (err) {
+                                    if(err) cb(err);
+                                    else cb(false);
+                                });
+                            }
+                        });
+                    }
+                    else{
+                        if(data){
+                            Projects.update(data.findData, data.updateData, function (err) {
+                                if(err)cb(err);
+                                else cb(false);
+                            });
+                        }
+                    }
+                }
+            }
+        )(updateDatas[i]));
+    }
+    async_c.parallel(functions, function (err, results) {
+        if(err) callback(err, 'fail', null);
+        else callback(false, 'success', null);
+    });
+};
+
 /**
  * 整理工程专业对应标准库数据
  *
@@ -367,5 +582,6 @@ ProjectsDAO.prototype.changeUnitPriceFileInfo = async function(projectId, change
 
 module.exports ={
     project: new ProjectsDAO(),
-    projType: projectType
+    projType: projectType,
+    fileType: fileType
 };

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

@@ -0,0 +1,51 @@
+/**
+ * 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;

+ 4 - 0
modules/pm/routes/pm_route.js

@@ -43,6 +43,10 @@ module.exports = function (app) {
     pmRouter.post('/getNewProjectID', pmController.getNewProjectID);
     pmRouter.post('/getUnitFile', pmController.getUnitFileList);
     pmRouter.post('/getFeeRateFile', pmController.getFeeRateFileList);
+    pmRouter.post('/updateFiles', pmController.updateFiles);
+    //GC
+    pmRouter.post('/getGCDatas', pmController.getGCDatas);
+    pmRouter.post('/recGC', pmController.recGC);
 
     app.use('/pm/api', pmRouter);
 };

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

@@ -12,7 +12,8 @@ module.exports={
     getGLJData:getGLJData,
     addGLJ:addGLJ,
     replaceGLJ:replaceGLJ,
-    mReplaceGLJ:mReplaceGLJ
+    mReplaceGLJ:mReplaceGLJ,
+    updateRationGLJByEdit:updateRationGLJByEdit
 }
 function createRationGLJ() {
     let gls = mongoose.model('ration_glj');
@@ -42,7 +43,6 @@ async function getGLJData(req, res) {
     }
     try {
         let info = await ration_glj_facade.getLibInfo(req);
-        console.log(info);
         ration_glj_facade.getGLJData(info,function (err,datas) {
             if(err){
                 result.error=1;
@@ -111,4 +111,22 @@ async function mReplaceGLJ(req, res){
         result.message = err.message;
     }
     res.json(result);
+}
+
+async  function updateRationGLJByEdit(req, res) {
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let uresult= await ration_glj_facade.updateRationGLJByEdit(data);
+        result.data=uresult;
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+    res.json(result);
+    
 }

+ 5 - 5
modules/ration_glj/facade/glj_calculate_facade.js

@@ -10,7 +10,7 @@ let ration = mongoose.model('ration');
 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();
 
 module.exports={
     calculateQuantity:calculateQuantity,
@@ -131,7 +131,7 @@ function calculateAss(quantity,assList,glj) {
             }
         }
     }
-    return quantity;
+    return scMathUtil.roundTo(quantity,-6);
 }
 
 function generateAdjustState(glj,coeList,adjustState,index) {
@@ -187,7 +187,7 @@ function calculateTimes(ass){
     if(r){
         times=times*-1;
     }
-    return times;
+    return scMathUtil.roundTo(times,-6);
 }
 
 function calculateQuantityByCoes(quantity,coeList,glj){
@@ -197,7 +197,7 @@ function calculateQuantityByCoes(quantity,coeList,glj){
             coeQuantity = everyCoe(coeQuantity,coeList[i],glj);
         }
     }
-    return coeQuantity;
+    return scMathUtil.roundTo(coeQuantity,-6);
 }
 
 function everyCoe(quantity,coe,glj) {
@@ -213,7 +213,7 @@ function everyCoe(quantity,coe,glj) {
             }
         }
     }
-    return coeQuantity;
+    return scMathUtil.roundTo(coeQuantity,-6);
 }
 
 

+ 4 - 6
modules/ration_glj/facade/quantity_detail_facade.js

@@ -14,7 +14,7 @@ let quantity_detail_model = mongoose.model('quantity_detail');
 const uuidV1 = require('uuid/v1');
 let ration_model = mongoose.model('ration');
 let bill_model=mongoose.model("bills");
-
+const scMathUtil = require('../../../public/scMathUtil').getUtil();
 
 module.exports={
     save:save,
@@ -238,7 +238,6 @@ function normalUpdate(user_id,datas) {
 }
 function updateQuantityRegex(user_id,datas) {
     return function(callback){
-        console.log(datas);
         doRegexUpdate(datas).then(function (result) {
             if(result.err){
                 callback(null,{
@@ -422,8 +421,9 @@ function  getEvalResult(referenceIndexs,detailList,regex) {
         console.log('replace all C reference -----'+regex);
         regex =replaceSqr(regex);
         console.log('replace all sqar reference -----'+regex);
-        return _.round(eval(regex), 4);
+        return scMathUtil.roundTo(eval(regex), -4);
     }catch (error){
+        console.log(error);
         throw new Error('输入的表达式有误,请重新输入!');
     }
 
@@ -523,7 +523,6 @@ function update_quantity_detail(user_id,datas) {
 function delete_quantity_detail(user_id,datas) {
     return function (callback) {
         doQuantityDelete(datas.doc).then(function (result) {
-            console.log(result);
             if(result.err){
                 callback(result.err,'')
             }else {
@@ -533,8 +532,7 @@ function delete_quantity_detail(user_id,datas) {
     }
 }
 
-async function doQuantityDelete(doc) {
-   console.log(doc) ;
+async function doQuantityDelete(doc) {;
     let result={
         err:null
     }

+ 1 - 0
modules/ration_glj/facade/ration_coe_facade.js

@@ -11,6 +11,7 @@ let async_n = require("async");
 let coeMolde = mongoose.model('std_ration_lib_coe_list');
 let ration_coe = mongoose.model('ration_coe');
 let glj_calculate_facade = require('./glj_calculate_facade');
+const scMathUtil = require('../../../public/scMathUtil').getUtil();
 
 
 module.exports={

+ 89 - 115
modules/ration_glj/facade/ration_glj_facade.js

@@ -32,7 +32,8 @@ module.exports={
     getGLJData:getGLJData,
     addGLJ:addGLJ,
     replaceGLJ:replaceGLJ,
-    mReplaceGLJ:mReplaceGLJ
+    mReplaceGLJ:mReplaceGLJ,
+    updateRationGLJByEdit:updateRationGLJByEdit
 }
 
 let operationMap={
@@ -41,9 +42,7 @@ let operationMap={
     'ut_delete':delete_ration_glj
 };
 let updateFunctionMap = {
-    'normalUpdate':normalUpdate,
-    'marketPriceAdjustUpdate':marketPriceAdjustUpdate,
-    'customQuantityUpdate':customQuantityUpdate
+    'normalUpdate':normalUpdate
 };
 
 
@@ -82,7 +81,7 @@ function combineQuantity(results,rations) {
     _.forEach(results,function (data) {
         let tmp = {
             projectGLJID:data.projectGLJID,
-            quantity: data.quantity
+            quantity: Number(data.quantity)
         }
         let ration=_.find(rations,{ID:data.rationID});
         if(ration){
@@ -117,6 +116,7 @@ function get_lib_glj_info(ration_glj) {
             }else if(glj){
                 ration_glj.name = glj.name;
                 ration_glj.code = glj.code;
+                ration_glj.original_code=glj.code;
                 ration_glj.unit = glj.unit;
                 ration_glj.specs = glj.specs;
                 ration_glj.basePrice = glj.basePrice;
@@ -152,6 +152,7 @@ function createNewRecord(ration_glj) {
     newRecoed.quantity=ration_glj.quantity;
     newRecoed.name = ration_glj.name;
     newRecoed.code = ration_glj.code;
+    newRecoed.original_code=ration_glj.original_code;
     newRecoed.unit = ration_glj.unit;
     newRecoed.specs = ration_glj.specs;
     newRecoed.from=ration_glj.from?ration_glj.from:undefined;
@@ -174,6 +175,9 @@ async function getInfoFromProjectGLJ(ration_glj) {
          ration_glj.basePrice=result.unit_price.base_price;
          ration_glj.projectGLJID=result.id;
          ration_glj.isEstimate=result.is_evaluate;
+         if(result.hasOwnProperty('subList')&&result.subList.length>0){
+             ration_glj.subList=getMixRatioShowDatas(result.subList);
+         }
          return ration_glj;
      } catch (err) {
          logger.err(err);
@@ -182,6 +186,29 @@ async function getInfoFromProjectGLJ(ration_glj) {
 
  }
 
+ function getMixRatioShowDatas(subList) {
+     var temRationGLJs = [];
+     for(let pg of subList){
+         var tem = {
+             projectGLJID:pg.id,
+             code:pg.code,
+             name:pg.name,
+             specs:pg.specs,
+             unit:pg.unit,
+             shortName:pg.unit_price.short_name,
+             rationItemQuantity:pg.ratio_data.consumption,
+             basePrice:pg.unit_price.base_price,
+             marketPrice:pg.unit_price.market_price,
+             adjustPrice:pg.adjust_price,
+             isEstimate:pg.is_evaluate,
+             isMixRatio:true
+         }
+         temRationGLJs.push(tem);
+     }
+     temRationGLJs=_.sortBy(temRationGLJs,'code');
+     return temRationGLJs;
+ }
+
 function create_ration_glj(user_id,datas) {
     return function (callback) {
         let ration_glj_list=datas.ration_glj_list;
@@ -223,7 +250,6 @@ function create_ration_glj(user_id,datas) {
                     logger.info("can't find gljs")
                     callback(null,null)
                 }
-
             }
         })
     }
@@ -258,122 +284,15 @@ function normalUpdate(user_id,datas){
     }
 }
 
-function customQuantityUpdate(user_id,datas){
-    return function(callback) {
-        doCustomQuantityUpdate(datas).then((result)=>{
-            if(result.err){
-                callback(result.err,'');
-            }else {
-                let ration_glj_data ={
-                    moduleName:'ration_glj',
-                    data:{
-                        updateTpye:commonConsts.UT_UPDATE,
-                        quantityRefresh:true,
-                        glj_result:result.cal_result.glj_result
-                    }
-                };
-                let ration_data ={
-                    moduleName:'ration',
-                    data:{
-                        updateTpye:commonConsts.UT_UPDATE,
-                        stateRefresh:true,
-                        rationID:result.cal_result.rationID,
-                        adjustState:result.cal_result.adjustState
-                    }
-                };
-                callback(null,[ration_glj_data,ration_data]);
-            }
-        })
-    }
-}
-
 async function doCustomQuantityUpdate(datas){
-    let result = {
-        err:null
-    }
-    try{
-        await ration_glj.update(datas.query,datas.doc);
+        let result =  await ration_glj.findOneAndUpdate(datas.query,datas.doc);
         let cal_result = await glj_calculate_facade.calculateQuantity({projectID:datas.query.projectID,rationID:datas.query.rationID});
-
         cal_result.glj_result.forEach(function (item) {
            if(!item.doc.hasOwnProperty('customQuantity')){
                item.doc.customQuantity=null;
            }
         });
-        result.cal_result =cal_result;
-        console.log(result);
-        return result;
-    }catch (err){
-        result.err = err;
-        console.log(err);
-        return result;
-    }
-}
-
-
-function marketPriceAdjustUpdate(user_id,datas) {
-    return function (callback) {
-        updateprojectGljAndRationGLJ(datas.query,datas.doc).then((result)=>{
-            if(result.err){
-                callback(result.err,'');
-            }else {
-                let returndata ={
-                    moduleName:'ration_glj',
-                    data:{
-                        updateTpye:commonConsts.UT_UPDATE,
-                        query:datas.query,
-                        doc:result.doc
-                    }
-                };
-                let ration_data ={
-                    moduleName:'ration',
-                    data:{
-                        updateTpye:commonConsts.UT_UPDATE,
-                        stateRefresh:true,
-                        rationID:datas.query.rationID,
-                        adjustState:result.adjustState
-                    }
-                }
-                callback(null,[returndata,ration_data]);
-            }
-        })
-
-    }
-}
-
-async function updateprojectGljAndRationGLJ(query,doc) {
-    let returnresult={};
-    try {
-        let gljListModel = new GLJListModel();
-        let temp = doc.market_price;
-        if(doc.market_price==null){
-            doc.market_price = doc.base_price;
-        }
-        delete doc.base_price;
-        let result = await gljListModel.modifyMarketPrice(doc);
-        let updateDoc ={
-            marketPrice:result.unit_price.market_price,
-            marketPriceAdjust:temp,
-            projectGLJID:result.id,
-            isEstimate:result.is_evaluate,
-            name : result.name,
-            code:result.code
-        };
-        let updateresult = await ration_glj.findOneAndUpdate(query, updateDoc);
-        let stateResult =  await glj_calculate_facade.calculateQuantity({projectID:query.projectID,rationID:query.rationID},true);
-
-         returnresult ={
-            err:null,
-            doc :updateDoc,
-            adjustState:stateResult.adjustState
-        }
-        return returnresult;
-
-        } catch (error) {
-           returnresult.err = error;
-            console.log(error);
-            return returnresult
-         }
+        return cal_result;
 }
 
 
@@ -622,6 +541,7 @@ function getGLJSearchInfo(ration_glj) {
         glj_id: ration_glj.GLJID,
         project_id: ration_glj.projectID,
         code: ration_glj.code,
+        original_code:ration_glj.original_code,
         name: ration_glj.name,
         shortName:ration_glj.shortName,
         specs: ration_glj.specs,
@@ -633,6 +553,9 @@ function getGLJSearchInfo(ration_glj) {
         repositoryId:ration_glj.repositoryId,
         from:ration_glj.from?ration_glj.from:'std'//std:标准工料机库, cpt:补充工料机库
     };
+     if(data.from=='cpt'){//从补充工料机来的数据即为新增数据
+         data.is_add = 1;
+     }
     return data;
 }
 
@@ -648,6 +571,9 @@ async function addGLJ(rgList) {
        g.projectGLJID=result.id;
        g.isEstimate=result.is_evaluate;
        g.ID=uuidV1();
+       if(result.hasOwnProperty('subList')&&result.subList.length>0){
+           g.subList=getMixRatioShowDatas(result.subList);
+       }
        newRecodes.push(createNewRecord(g));
    }
     await ration_glj.insertMany(newRecodes);
@@ -693,6 +619,54 @@ async function mReplaceGLJ(data) {
     return mresult
 }
 
+async function updateRationGLJByEdit(data) {
+    var doc = data.doc;
+    var result;
+    if(doc.hasOwnProperty('customQuantity')){
+      result = await doCustomQuantityUpdate(data)
+    }else {
+       result = await doRationGLJUpdate(data);
+    }
+    return result;
+}
+
+async function doRationGLJUpdate(data){
+    let resutl = {};
+    let doc = data.doc;
+    let priceInfo = data.priceInfo;
+    let rg =  await ration_glj.findOne(data.query);
+    let gljListModel = new GLJListModel();
+    let projectGLJ= getGLJSearchInfo(rg);
+    for(let key in doc){
+        projectGLJ[key]=doc[key]
+    }
+    projectGLJ.base_price = priceInfo.base_price;
+    projectGLJ.market_price = priceInfo.market_price;
+    let projcetGLJ_n = await gljListModel.modifyGLJ(projectGLJ,rg);
+    doc.code = projcetGLJ_n.code;
+    doc.projectGLJID=projcetGLJ_n.id;
+    if(projcetGLJ_n.unit_price.is_add==1){
+        doc.createType='replace';
+        doc.rcode=projcetGLJ_n.original_code;
+    }else {
+        doc.createType='normal';
+        doc.rcode='';
+    }
+    await ration_glj.findOneAndUpdate(data.query,doc);
+    //取价格
+    gljListModel.getGLJPrice(projcetGLJ_n);
+    doc.basePrice=projcetGLJ_n.unit_price.base_price;
+    doc.marketPrice=projcetGLJ_n.unit_price.market_price;
+    doc.adjustPrice=projcetGLJ_n.adjust_price;
+    doc.isAdd = projcetGLJ_n.unit_price.is_add;
+    resutl.doc = doc;
+    let stateResult = await glj_calculate_facade.calculateQuantity({projectID:data.query.projectID,rationID:data.query.rationID});
+    resutl.adjustState= stateResult.adjustState;
+    return resutl;
+}
+
+
+
 async function changAdjustState(data,rationList) {
     let stateList=[];
     for(let r of rationList){

+ 8 - 4
modules/ration_glj/models/ration_glj.js

@@ -13,16 +13,20 @@ var ration_glj = new Schema({
     projectGLJID:Number,
     name:String,
     code:String,
+    //原始的编码
+    original_code: {
+        type: String,
+        index: true
+    },
     rcode:String,
     specs:String,
     unit:String,
     shortName:String,
     billsItemID: Number,
     type:Number,
-    quantity:Number,
-    customQuantity:Number,
-    rationItemQuantity:Number,
-    marketPriceAdjust:Number,
+    quantity:String,
+    customQuantity:String,
+    rationItemQuantity:String,
     createType: {type: String,default:'normal'},//normal、add、replace  正常、添加工料机、替换工料机
     from:{type: String,default:'std'}//std, cpt  来自标准工料机库、补充工料机库
 },{versionKey:false});

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

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

+ 1 - 1
modules/ration_repository/models/ration_item.js

@@ -47,7 +47,7 @@ var counter = require('../../../public/counter/counter');
 var rationItemDAO = function(){};
 
 rationItemDAO.prototype.getRationItemsBySection = function(sectionId,callback){
-    rationItemModel.find({"sectionId": sectionId, "$or": [{"isDeleted": null}, {"isDeleted": false} ]},function(err,data){
+    rationItemModel.find({"sectionId": sectionId, "$or": [{"isDeleted": null}, {"isDeleted": false} ]}, null, {sort: {code: 1}}, function(err,data){
         if(err) callback(true, "Fail to get items", "")
         else callback(false,"Get items successfully", data);
     })

+ 44 - 35
modules/reports/controllers/rpt_controller.js

@@ -8,6 +8,9 @@ import async from "async";
 import JV from "../rpt_component/jpc_value_define";
 
 let Template = mongoose.model('rpt_templates');
+let rptTplDataFacade = require("../facade/rpt_tpl_data_facade");
+//let fsUtil = require("../../../public/fsUtil");
+
 import rptTplFacade from "../facade/rpt_template_facade";
 import demoTemplateFacade from "../facade/rpt_tpl_data_demo_facade";
 
@@ -17,6 +20,7 @@ import rpt_xl_util from "../util/rpt_excel_util";
 import rpt_pdf_util from "../util/rpt_pdf_util";
 import fs from "fs";
 import strUtil from "../../../public/stringUtil";
+import rptDataExtractor from "../util/rpt_construct_data_util";
 
 //统一回调函数
 let callback = function(req, res, err, data){
@@ -68,42 +72,36 @@ function getAllPagesCommonOrg(rpt_id, pageSize, option, cb) {
     );
 }
 
-function getAllPagesCommon(rpt_id, pageSize, cb) {
+function getAllPagesCommon(user_id, prj_id, rpt_id, pageSize, option, cb) {
     let rptTpl = null;
     rptTplFacade.getRptTemplate(rpt_id).then(function(rst) {
         rptTpl = rst;
         if (rptTpl) {
-            let tplData = {};
-            if (rptTpl[JV.NODE_FIELD_MAP]) {
-                //1. 离散数据
-                if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DISCRETE_FIELDS] && rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DISCRETE_FIELDS].length > 0) {
-                    tplData[JV.DATA_DISCRETE_DATA] = [];
-                }
-                //2. 主数据
-                if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS] && rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS].length > 0) {
-                    tplData[JV.DATA_MASTER_DATA] = [];
-                }
-                //3. 从数据
-                if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS] && rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS].length > 0) {
-                    tplData[JV.DATA_DETAIL_DATA] = [];
-                }
-                //2. Ex主数据
-                if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS_EX] && rptTpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS_EX].length > 0) {
-                    tplData[JV.DATA_MASTER_DATA_EX] = [];
-                }
-                //3. Ex从数据
-                if (rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS_EX] && rptTpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS_EX].length > 0) {
-                    tplData[JV.DATA_DETAIL_DATA_EX] = [];
-                }
-                //4. 重点: 开始组装$PROJECT对象
-                let $PROJECT = {};
-                //let $PROJECT.COMMON = {};
-                //return demoTemplateData.getPromise(rptTpl.ID_KEY);
-                //return demoTemplateData.findOne({"Data_Key": rptTpl.ID_KEY}).exec();
+            let rptDataUtil = new rptDataExtractor();
+            rptDataUtil.initialize((rptTpl._doc)?rptTpl._doc:rptTpl);
+            let filter = rptDataUtil.getDataRequestFilter();
+            rptTplDataFacade.prepareProjectData(user_id, prj_id, filter, function (err, msg, rawDataObj) {
+                if (!err) {
+                    let tplData = rptDataUtil.assembleData(rawDataObj);
+                    let printCom = JpcEx.createNew();
+                    rptTpl[JV.NODE_MAIN_INFO][JV.NODE_PAGE_INFO][JV.PROP_PAGE_SIZE] = pageSize;
+                    let defProperties = rptUtil.getReportDefaultCache();
+                    let dftOption = option||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.wirteObjToFile(pageRst, "D:/GitHome/ConstructionCost/tmp/testBuiltPageResult.js");
+                        cb(null, pageRst);
+                    } else {
+                        cb('Have errors while on going...', null);
+                    }
 
-            } else {
-                cb('No report template data were found!', null);
-            }
+                } else {
+                    cb('No report data were found!', null);
+                }
+            });
         } else {
             cb('No report template was found!', null);
         }
@@ -111,7 +109,18 @@ function getAllPagesCommon(rpt_id, pageSize, cb) {
 }
 
 module.exports = {
-    getReportAllPages: function(req, res){
+    getReportAllPages: function (req, res) {
+        let params = JSON.parse(req.body.params),
+            rpt_id = params.rpt_tpl_id,
+            prj_id = params.prj_id,
+            user_id = params.user_id,
+            pageSize = params.pageSize;
+        getAllPagesCommon(user_id, prj_id, rpt_id, pageSize, null, function (err, pageRst) {
+            callback(req, res, err, pageRst);
+        });
+    },
+
+    getTestReportAllPages: function(req, res){
         let rpt_id = req.body.ID;
         let pageSize = req.body.pageSize;
         getAllPagesCommonOrg(rpt_id, pageSize, JV.PAGING_OPTION_NORMAL, function(err, pageRst){
@@ -119,7 +128,7 @@ module.exports = {
             callback(req, res, err, pageRst);
         })
     },
-    getExcel: function(req, res) {
+    getTestExcel: function(req, res) {
         let rpt_id = req.params.id,
             pageSize = req.params.size,
             rptName = req.params.rptName,
@@ -145,7 +154,7 @@ module.exports = {
             }
         })
     },
-    getExcelInOneBook: function(req, res) {
+    getTestExcelInOneBook: function(req, res) {
         let rpt_ids = req.params.ids.split(','),
             pageSize = req.params.size,
             rptName = req.params.rptName,
@@ -185,7 +194,7 @@ module.exports = {
             }
         })
     },
-    getPDF:function (req, res) {
+    getTestPDF:function (req, res) {
         let rpt_id = req.params.id,
             pageSize = req.params.size,
             rptName = req.params.rptName;

+ 3 - 0
modules/reports/controllers/rpt_tpl_controller.js

@@ -52,6 +52,9 @@ let mExport = {
             compilationId = params.compilationId,
             userId = params.userId,
             engineerId = params.engineerId;
+        if (!compilationId) {
+            compilationId = req.session.sessionCompilation._id;
+        }
         rttFacade.findTplTree(compilationId, engineerId, userId).then(function(result) {
             if (result) {
                 callback(req,res,false,"", result);

+ 10 - 4
modules/reports/routes/report_router.js

@@ -17,11 +17,17 @@ module.exports =function (app) {
                     userID: req.session.managerData.userID});
         }
     });
-    rptRouter.post('/getReport', reportController.getReportAllPages);
 
-    rptRouter.get('/getExcel/:id/:size/:rptName/:isOneSheet/:option', reportController.getExcel);
-    rptRouter.get('/getExcelInOneBook/:ids/:size/:rptName/:option', reportController.getExcelInOneBook);
+    //test
+    rptRouter.post('/getTestReport', reportController.getTestReportAllPages);
+    rptRouter.get('/getTestExcel/:id/:size/:rptName/:isOneSheet/:option', reportController.getTestExcel);
+    rptRouter.get('/getTestExcelInOneBook/:ids/:size/:rptName/:option', reportController.getTestExcelInOneBook);
+    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('/getExcelInOneBook/:ids/:size/:rptName/:option', reportController.getExcelInOneBook);
+    // rptRouter.get('/getPDF/:id/:size/:rptName', reportController.getPDF);
 
-    rptRouter.get('/getPDF/:id/:size/:rptName', reportController.getPDF);//2/A4/07-1表
     app.use("/report_api", rptRouter);
 };

+ 17 - 15
modules/reports/rpt_component/helper/jpc_helper_flow_tab.js

@@ -2,33 +2,35 @@ let JV = require('../jpc_value_define');
 let JpcCommonHelper = require('./jpc_helper_common');
 
 let JpcFlowTabHelper = {
-    getMaxRowsPerPage: function(bands, rptTpl) {
+    getMaxRowsPerPage: function(bands, rptTpl, isEx) {
         let rst = 1;
-        let tab = rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_CONTENT];
+        let FLOW_INFO_STR = (!isEx)?JV.NODE_FLOW_INFO:JV.NODE_FLOW_INFO_EX;
+        let tab = rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT];
         let band = bands[tab[JV.PROP_BAND_NAME]];
         if (band) {
             let maxFieldMeasure = 1.0;
             if (JV.CAL_TYPE_ABSTRACT === JpcCommonHelper.getPosCalculationType(tab[JV.PROP_CALCULATION])) {
                 let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
-                maxFieldMeasure = 1.0 * rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_CONTENT][JV.PROP_CMN_HEIGHT] * unitFactor;
+                maxFieldMeasure = 1.0 * rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT][JV.PROP_CMN_HEIGHT] * unitFactor;
             } else {
-                maxFieldMeasure = (band.Bottom - band.Top) * rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_CONTENT][JV.PROP_CMN_HEIGHT] / JV.HUNDRED_PERCENT;
+                maxFieldMeasure = (band.Bottom - band.Top) * rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT][JV.PROP_CMN_HEIGHT] / JV.HUNDRED_PERCENT;
             }
             rst = Math.floor((band.Bottom - band.Top) / maxFieldMeasure);
         }
         return rst;
     },
-    getActualContentAreaHeight: function(bands, rptTpl, segments, page) {
+    getActualContentAreaHeight: function(bands, rptTpl, segments, page, isEx) {
         let rst = 1;
-        let tab = rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_CONTENT];
+        let FLOW_INFO_STR = (!isEx)?JV.NODE_FLOW_INFO:JV.NODE_FLOW_INFO_EX;
+        let tab = rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT];
         let band = bands[tab[JV.PROP_BAND_NAME]];
         if (band) {
             let maxFieldMeasure = 1.0;
             if (JV.CAL_TYPE_ABSTRACT === JpcCommonHelper.getPosCalculationType(tab[JV.PROP_CALCULATION])) {
                 let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
-                maxFieldMeasure = 1.0 * rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_CONTENT][JV.PROP_CMN_HEIGHT] * unitFactor;
+                maxFieldMeasure = 1.0 * rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT][JV.PROP_CMN_HEIGHT] * unitFactor;
             } else {
-                maxFieldMeasure = (band.Bottom - band.Top) * rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_CONTENT][JV.PROP_CMN_HEIGHT] / JV.HUNDRED_PERCENT;
+                maxFieldMeasure = (band.Bottom - band.Top) * rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT][JV.PROP_CMN_HEIGHT] / JV.HUNDRED_PERCENT;
             }
             if (segments.length >= page) {
                 rst = segments[page - 1].length * maxFieldMeasure;
@@ -36,17 +38,17 @@ let JpcFlowTabHelper = {
         }
         return rst;
     },
-    chkSegEnd: function (bands, rptTpl, sortedSequence, segIdx, preRec, nextRec) {
-        let me = this, rst = true;
-        let remainAmt = preRec + nextRec - sortedSequence[segIdx].length;
-        rst = me.hasEnoughSpace(rptTpl, bands, remainAmt);
-        return rst;
+    chkSegEnd: function (bands, rptTpl, segmentLength, preRec, nextRec, isEx) {
+        let me = this;
+        let remainAmt = preRec + nextRec - segmentLength;
+        return me.hasEnoughSpace(rptTpl, bands, remainAmt, isEx);
     },
-    hasEnoughSpace: function (rptTpl, bands, remainAmt) {
+    hasEnoughSpace: function (rptTpl, bands, remainAmt, isEx) {
         if (remainAmt < 0) return false;
         let rst = true, measurement = 1.0, douDiffForCompare = 0.00001;
         let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
-        let tab = rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_CONTENT];
+        let FLOW_INFO_STR = (!isEx)?JV.NODE_FLOW_INFO:JV.NODE_FLOW_INFO_EX;
+        let tab = rptTpl[FLOW_INFO_STR][JV.NODE_FLOW_CONTENT];
         let band = bands[tab[JV.PROP_BAND_NAME]];
         if (band !== null && band !== undefined) {
             measurement = 1.0 * tab[JV.PROP_CMN_HEIGHT] * unitFactor;

+ 6 - 0
modules/reports/rpt_component/helper/jpc_helper_text.js

@@ -8,6 +8,12 @@ let JpcTextHelper = {
         //position
         rst[JV.PROP_AREA] = JpcAreaHelper.outputArea(textNode[JV.PROP_AREA], band, unitFactor, rows, rowIdx, cols, colIdx, multiCols, multiColIdx, false, false);
         return rst;
+    },
+    outputDirectValue: function (textNode, value, band, unitFactor, rows, rowIdx, cols, colIdx, multiCols, multiColIdx) {
+        let rst = JpcCommonOutputHelper.createCommonOutput(textNode, value, null);
+        //position
+        rst[JV.PROP_AREA] = JpcAreaHelper.outputArea(textNode[JV.PROP_AREA], band, unitFactor, rows, rowIdx, cols, colIdx, multiCols, multiColIdx, false, false);
+        return rst;
     }
 };
 

+ 4 - 3
modules/reports/rpt_component/jpc_data.js

@@ -1,4 +1,5 @@
 let JV = require('./jpc_value_define');
+let jpc_common_helper = require("./helper/jpc_helper_common");
 let JpcData = {
     createNew: function() {
         let JpcDataRst = {};
@@ -11,14 +12,14 @@ let JpcData = {
                 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 ((mstFieldObj[JV.PROP_IS_ID]) && (mstFieldObj[JV.PROP_IS_ID] === 'T')) {
+                    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 ((dtlFieldObj[JV.PROP_IS_ID]) && (dtlFieldObj[JV.PROP_IS_ID] === 'T')) {
+                    if (jpc_common_helper.getBoolean(dtlFieldObj[JV.PROP_IS_ID])) {
                         detailIDs.push({"idx": i, "seq": dtlFieldObj[JV.PROP_ID_SEQ]});
                     }
                 }
@@ -100,6 +101,6 @@ let JpcData = {
         };
         return JpcDataRst;
     }
-}
+};
 
 module.exports = JpcData;

+ 18 - 7
modules/reports/rpt_component/jpc_ex.js

@@ -85,10 +85,14 @@ JpcExSrv.prototype.createNew = function(){
         if (rptTpl[JV.NODE_FLOW_INFO]) {
             me.flowTab = JpcFlowTab.createNew();
             me.flowTab.initialize(false);
+            me.isFollowMode = false;
         }
         if (rptTpl[JV.NODE_FLOW_INFO_EX]) {
             me.flowTabEx = JpcFlowTab.createNew();
             me.flowTabEx.initialize(true);
+            if (rptTpl[JV.NODE_FLOW_INFO_EX][JV.PROP_FLOW_EX_DISPLAY_MODE] === JV.DISPLAY_MODE_FOLLOW) {
+                me.isFollowMode = true;
+            }
         }
         if (rptTpl[JV.NODE_BILL_INFO]) {
             me.billTab = JpcBillTab.createNew();
@@ -137,12 +141,16 @@ JpcExSrv.prototype.createNew = function(){
     JpcResult.paging = function(rptTpl, dataObj, defProperties, option) {
         let me = this, dftPagingOption = option||JV.PAGING_OPTION_NORMAL;
         if (me.flowTab) {
-            me.totalPages = me.flowTab.preSetupPages(rptTpl, dataObj, defProperties, dftPagingOption);
-            if (me.flowTabEx) {
-                me.exTotalPages = me.flowTabEx.preSetupPages(rptTpl, dataObj, defProperties, dftPagingOption);
-                //console.log('ad-hoc flow pages: ' + me.exTotalPages);
+            if (me.isFollowMode) {
+                me.totalPages = me.flowTab.preSetupPages(rptTpl, dataObj, defProperties, dftPagingOption, me, me.flowTabEx);
+            } else {
+                me.totalPages = me.flowTab.preSetupPages(rptTpl, dataObj, defProperties, dftPagingOption, me, null);
+                if (me.flowTabEx) {
+                    me.exTotalPages = me.flowTabEx.preSetupPages(rptTpl, dataObj, defProperties, dftPagingOption, me, null);
+                    //console.log('ad-hoc flow pages: ' + me.exTotalPages);
+                }
+                me.totalPages += me.exTotalPages;
             }
-            me.totalPages += me.exTotalPages;
         } else if (me.crossTab) {
             //me.totalPages = me.crossTab.preSetupPages(rptTpl, defProperties, dftPagingOption);
             me.totalPages = me.crossTab.preSetupPages(rptTpl, defProperties, JV.PAGING_OPTION_NORMAL); //infinity对交叉表来说无意义
@@ -157,7 +165,7 @@ JpcExSrv.prototype.createNew = function(){
                 let expression = me.formulas[i][JV.PROP_EXPRESSION];
                 if (expression) {
                     let $ME = me.formulas[i];
-                    console.log(expression);
+                    // console.log(expression);
                     eval(expression);
                 }
             }
@@ -215,8 +223,11 @@ JpcExSrv.prototype.createNew = function(){
                         adHocMergePos[JV.NODE_PAGE_SIZE] = JpcCommonHelper.getPageSize(rptTpl);
                         rst[JV.PAGE_SPECIAL_MERGE_POS] = adHocMergePos;
                     }
+
                 } else {
-                    rst.cells = me.flowTabEx.outputAsSimpleJSONPage(rptTpl, dataObj, page - (me.totalPages - me.exTotalPages), bands, controls, adHocMergePos, me);
+                    if (!me.isFollowMode) {
+                        rst.cells = me.flowTabEx.outputAsSimpleJSONPage(rptTpl, dataObj, page - (me.totalPages - me.exTotalPages), bands, controls, adHocMergePos, me);
+                    }
                 }
             } else if (me.crossTab) {
                 rst.cells = me.crossTab.outputAsSimpleJSONPage(rptTpl, dataObj, page, bands, controls, me);

+ 444 - 33
modules/reports/rpt_component/jpc_flow_tab.js

@@ -11,14 +11,63 @@ let JpcCommonOutputHelper = require('./helper/jpc_helper_common_output');
 let JpcAreaHelper = require('./helper/jpc_helper_area');
 
 let JpcFlowTabSrv = function(){};
+//let grpPageInfo = {"segGrpRecStartIdx": 0, "insertedGrpRecAmt": 0, "preAddPageGrpInfo": null};
 JpcFlowTabSrv.prototype.createNew = function(){
-    function private_addPageValue(ValuedIdxLst, sortedSequence, preRec, nextRec,page_seg_map, segIdx, pageIdx) {
-        let vIdx = [];
-        for (let vi = 0; vi < nextRec; vi++) {
-            if (sortedSequence.length > preRec + vi) {
-                vIdx.push(sortedSequence[preRec + vi]);
+    function private_addPageValue(ValuedIdxLst, sortedSequence, grpSequenceInfo, startRecIdx, maxRecPerPage,page_seg_map, segIdx, pageIdx, grpPageInfo, isFollow) {
+        let vIdx = [], preAmt = 0, insertedGrpAmt = 0, grp_lines = 0;
+        if (grpSequenceInfo && grpPageInfo) {
+            //grpPageInfo[JV.PROP_INSERTED_GRP_REC] = 0;
+            if (grpPageInfo[JV.PROP_PRE_ADD_GRP_REC_INFO].length > 0) {
+                for (let grpLineIdx of grpPageInfo[JV.PROP_PRE_ADD_GRP_REC_INFO]) {
+                    vIdx.push([(isFollow)?JV.TYPE_FOLLOW_MODE:-1, JV.DISPLAY_VAL_TYPE_GROUP, grpPageInfo[JV.PROP_SEG_GRP_IDX], grpLineIdx]);
+                }
+                grpPageInfo[JV.PROP_SEG_GRP_IDX]++;
+            }
+            preAmt = grpPageInfo[JV.PROP_PRE_ADD_GRP_REC_INFO].length;
+            grpPageInfo[JV.PROP_PRE_ADD_GRP_REC_INFO] = [];
+            grp_lines = grpPageInfo[JV.PROP_GRP_LINES];
+        }
+        for (let vi = 0; (vi + insertedGrpAmt * grp_lines) < maxRecPerPage - preAmt; vi++) {
+            if (grpSequenceInfo && grpPageInfo) {
+                if ((startRecIdx + vi) === grpSequenceInfo[grpPageInfo[JV.PROP_SEG_GRP_IDX]]) {
+                    //表示这里要插入grouping信息啦!
+                    //1. 首先push正常的记录
+                    vIdx.push([(isFollow)?JV.TYPE_FOLLOW_MODE:-1, JV.DISPLAY_VAL_TYPE_NORMAL, sortedSequence[startRecIdx + vi]]);
+                    //2. 然后就要push grouping记录了
+                    let hasFullGrp = true;
+                    for (let i = 0; i < grp_lines; i++) {
+                        if ( ((vi + insertedGrpAmt * grp_lines) + i + 1) >= (maxRecPerPage - preAmt)) {
+                            for (let j = i; j < grp_lines; j++) {
+                                grpPageInfo[JV.PROP_PRE_ADD_GRP_REC_INFO].push(j);
+                            }
+                            //准备要跳出去了
+                            hasFullGrp = false;
+                            break;
+                        } else {
+                            vIdx.push([(isFollow)?JV.TYPE_FOLLOW_MODE:-1, JV.DISPLAY_VAL_TYPE_GROUP, grpPageInfo[JV.PROP_SEG_GRP_IDX], i]);
+                        }
+                    }
+                    //3. 进位下一个group信息所在位置
+                    if (hasFullGrp) {
+                        grpPageInfo[JV.PROP_INSERTED_GRP_REC]++;
+                        insertedGrpAmt++;
+                        grpPageInfo[JV.PROP_SEG_GRP_IDX]++;
+                    } else {
+                        break;
+                    }
+                } else {
+                    if (sortedSequence.length > startRecIdx + vi) {
+                        vIdx.push([(isFollow)?JV.TYPE_FOLLOW_MODE:-1, JV.DISPLAY_VAL_TYPE_NORMAL, sortedSequence[startRecIdx + vi]]);
+                    } else {
+                        vIdx.push([(isFollow)?JV.TYPE_FOLLOW_MODE:-1, JV.DISPLAY_VAL_TYPE_NORMAL, JV.BLANK_VALUE_INDEX]);
+                    }
+                }
             } else {
-                vIdx.push(JV.BLANK_VALUE_INDEX);
+                if (sortedSequence.length > startRecIdx + vi) {
+                    vIdx.push([(isFollow)?JV.TYPE_FOLLOW_MODE:-1, JV.DISPLAY_VAL_TYPE_NORMAL, sortedSequence[startRecIdx + vi]]);
+                } else {
+                    vIdx.push([(isFollow)?JV.TYPE_FOLLOW_MODE:-1, JV.DISPLAY_VAL_TYPE_NORMAL, JV.BLANK_VALUE_INDEX]);
+                }
             }
         }
         page_seg_map.push([pageIdx, segIdx]);
@@ -36,7 +85,13 @@ JpcFlowTabSrv.prototype.createNew = function(){
         me.seg_sum_fields_idx = [];
         me.seg_sum_tab_fields = [];
         me.page_sum_fields_idx = [];
-        me.group_fields_idx = [];
+
+        me.group_fields = [];
+        me.group_sum_fields = [];
+        me.group_sum_values = null;
+        me.group_node_info = null; //记录在哪个seg及到哪条记录后有group sum信息
+        me.group_lines_amt = 0;    //每group一次占用多少行,计算page信息会用到
+
         me.pageStatusLst = [];
         me.groupSumValLst = [];
         me.segSumValLst = [];
@@ -48,7 +103,8 @@ JpcFlowTabSrv.prototype.createNew = function(){
         let FLOW_NODE_STR = me.isEx?JV.NODE_FLOW_INFO_EX:JV.NODE_FLOW_INFO;
         JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_SEG_SUM][JV.PROP_SUM_FIELDS], me.seg_sum_tab_fields, me.seg_sum_fields_idx, me.isEx);
         JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_PAGE_SUM][JV.PROP_SUM_FIELDS], null, me.page_sum_fields_idx, me.isEx);
-        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_FIELDS], null, me.group_fields_idx, me.isEx);
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_FIELDS], me.group_fields, null, me.isEx);
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_SUM_FIELDS], me.group_sum_fields, null, me.isEx);
         for (let si = 0; si < dataSeq.length; si++) {
             me.segments.push(dataSeq[si].slice(0));
         }
@@ -74,8 +130,271 @@ JpcFlowTabSrv.prototype.createNew = function(){
 
         }
     };
-    JpcFlowTabResult.preSetupPages = function (rptTpl, dataOjb, defProperties, option) {
+    JpcFlowTabResult.sumUpGrp = function ($CURRENT_RPT, dataObj, segIdx, preGrpIdx, nexGrpIdx) {
+        let me = this, segDataIdx = me.segments[segIdx];
+        for (let j = 0; j < me.group_sum_fields.length; j++) {
+            let sum_field = JE.F(me.group_sum_fields[j][JV.PROP_FIELD_ID], $CURRENT_RPT);
+            if (sum_field) {
+                let data_field = null;
+                if (sum_field[JV.PROP_AD_HOC_DATA]) {
+                    data_field = sum_field[JV.PROP_AD_HOC_DATA]
+                } else {
+                    data_field = dataObj[sum_field.DataNodeName][sum_field.DataSeq];
+                }
+                let sumV = 0;
+                for (let si = preGrpIdx; si <= nexGrpIdx; si++) {
+                    sumV += JpcFieldHelper.getValue(data_field, segDataIdx[si]);
+                }
+                // me.group_sum_values[segIdx][j].push(sumV);
+                me.group_sum_values[segIdx][me.group_sum_fields[j][JV.PROP_SUM_KEY]].push(sumV);
+            }
+        }
+        me.group_node_info[segIdx].push(nexGrpIdx);
+    };
+    JpcFlowTabResult.setupGroupingData = function (rptTpl, dataObj, $CURRENT_RPT) {
+        let me = this;
+        if (me.group_fields.length > 0 && me.group_sum_fields.length > 0) {
+            me.group_sum_values = [];
+            me.group_node_info = [];
+            let CURRENT_FLOW_INFO = (me.isEx)?JV.NODE_FLOW_INFO_EX:JV.NODE_FLOW_INFO;
+            me.group_lines_amt = rptTpl[CURRENT_FLOW_INFO][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_LINES].length;
+            //
+            let preGrpIdx = 0, nexGrpIdx = 0;
+            for (let segIdx = 0; segIdx < me.segments.length; segIdx++) {
+                let segDataIdx = me.segments[segIdx];
+                // me.group_sum_values.push([]);
+                me.group_sum_values.push({});
+                me.group_node_info.push([]);
+                for (let j = 0; j < me.group_sum_fields.length; j++) {
+                    // me.group_sum_values[segIdx].push([]);
+                    me.group_sum_values[segIdx][me.group_sum_fields[j][JV.PROP_SUM_KEY]] = [];
+                    // me.group_node_info[segIdx].push([]);
+                }
+                for (let di = 0; di < segDataIdx.length; di++) {
+                    let hasDiff = false;
+                    if (di > 1) {
+                        for (let i = 0; i < me.group_fields.length; i++) {
+                            let grp_field = JE.F(me.group_fields[i][JV.PROP_FIELD_ID], $CURRENT_RPT);
+                            if (grp_field) {
+                                let data_field = null;
+                                if (grp_field[JV.PROP_AD_HOC_DATA]) {
+                                    data_field = grp_field[JV.PROP_AD_HOC_DATA]
+                                } else {
+                                    data_field = dataObj[grp_field.DataNodeName][grp_field.DataSeq];
+                                }
+                                let v1 = JpcFieldHelper.getValue(data_field, segDataIdx[di]), v2 = JpcFieldHelper.getValue(data_field, segDataIdx[di - 1]);
+                                if (v1 !== v2) {
+                                    hasDiff = true;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                    if (hasDiff) {
+                        //then sum up the fields
+                        me.sumUpGrp($CURRENT_RPT, dataObj, segIdx, preGrpIdx, nexGrpIdx);
+                        nexGrpIdx = di;
+                        preGrpIdx = di;
+                        if (di === segDataIdx.length - 1) {
+                            me.sumUpGrp($CURRENT_RPT, dataObj, segIdx, preGrpIdx, nexGrpIdx);
+                        }
+                    } else if (di === segDataIdx.length - 1) {
+                        me.sumUpGrp($CURRENT_RPT, dataObj, segIdx, preGrpIdx, di);
+                    } else {
+                        nexGrpIdx = di;
+                    }
+                }
+            }
+        }
+    };
+
+    JpcFlowTabResult.preSetupPages = function (rptTpl, dataObj, defProperties, option, $CURRENT_RPT, followTabEx) {
+        //换一种思路来整理流水式数据
+        let me = this, rst = 1, counterRowRec = 0, counterRowRecEx = 0, maxRowRec = 1, pageIdx = 0, currentRecAmt = 0;
+        me.setupGroupingData(rptTpl, dataObj, $CURRENT_RPT);
+        if (followTabEx) {
+            followTabEx.setupGroupingData(rptTpl, dataObj, $CURRENT_RPT);
+        }
+        me.paging_option = option||JV.PAGING_OPTION_NORMAL;
+        let CURRENT_FLOW_INFO = (me.isEx)?JV.NODE_FLOW_INFO_EX:JV.NODE_FLOW_INFO;
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[CURRENT_FLOW_INFO][JV.NODE_FLOW_CONTENT][JV.PROP_FLOW_FIELDS], null, me.disp_fields_idx, me.isEx);
+        if (me.paging_option === JV.PAGING_OPTION_INFINITY) {
+            rst = me.segments.length;
+            let pageStatus = [true, true, false, true, true, true, false, false];
+            for (let segIdx = 0; segIdx < me.segments.length; segIdx++) {
+                if (segIdx === me.segments.length - 1) {
+                    pageStatus[JV.STATUS_REPORT_END] = true;
+                }
+                if (segIdx > 0) {
+                    pageStatus[JV.STATUS_REPORT_START] = false;
+                }
+                me.pageStatusLst.push(pageStatus.slice(0));
+                pageIdx++;
+                let grpSeqInfo = (me.group_node_info)?me.group_node_info[segIdx]:null;
+                private_addPageValue(me.dispValueIdxLst, me.segments[segIdx], grpSeqInfo, 0, me.segments[segIdx].length, me.page_seg_map, segIdx, pageIdx, null, false);
+            }
+            //目前不支持flowTabEx
+        } else {
+            let bands = JpcBand.createNew(rptTpl, defProperties);
+            let pageStatus = [true, true, false, true, false, false, false, false];
+            if (me.isEx) {
+                pageStatus[JV.STATUS_REPORT_START] = false;
+            }
+            if (rptTpl[CURRENT_FLOW_INFO][JV.PROP_MULTI_COLUMN]) {
+                me.multiCols = 1 * rptTpl[CURRENT_FLOW_INFO][JV.PROP_MULTI_COLUMN];
+            }
+            let grpPageInfo = {};
+            function private_resetBandArea() {
+                JpcBandHelper.setBandArea(bands, rptTpl, pageStatus, !me.isEx, me.isEx);
+                maxRowRec = JpcFlowTabHelper.getMaxRowsPerPage(bands, rptTpl, me.isEx);
+            }
+            function private_addPage(segIdx, grpSeqInfo, isFollow, isMix, mixSplitPoint) {
+                private_resetBandArea();
+                me.pageStatusLst.push(pageStatus.slice(0));
+                currentRecAmt += maxRowRec;
+                pageIdx++;
+                if (isMix) {
+                    //先处理上半部分
+                    private_addPageValue(me.dispValueIdxLst, me.segments[segIdx], grpSeqInfo, counterRowRec, mixSplitPoint,me.page_seg_map, segIdx, pageIdx, grpPageInfo, false);
+                    for (let dv of me.dispValueIdxLst[me.dispValueIdxLst.length - 1]) {
+                        if (dv[1] === JV.DISPLAY_VAL_TYPE_NORMAL) counterRowRec++;
+                    }
+                    //再处理下半部分
+                    private_addPageValue(me.dispValueIdxLst, followTabEx.segments[segIdx], null, counterRowRecEx, maxRowRec - mixSplitPoint, me.page_seg_map, segIdx, pageIdx, null, true);
+                    for (let dv of me.dispValueIdxLst[me.dispValueIdxLst.length - 1]) {
+                        if (dv[1] === JV.DISPLAY_VAL_TYPE_NORMAL) counterRowRecEx++;
+                    }
+                    //合并到一页中
+                    me.page_seg_map.splice(me.page_seg_map.length - 1, 1);
+                    let vl = me.dispValueIdxLst[me.dispValueIdxLst.length - 2];
+                    for (let item of me.dispValueIdxLst[me.dispValueIdxLst.length - 1]) {
+                        vl.push(item);
+                    }
+                    me.dispValueIdxLst.splice(me.dispValueIdxLst.length - 1, 1);
+                } else if (isFollow) {
+                    private_addPageValue(me.dispValueIdxLst, followTabEx.segments[segIdx], null, counterRowRecEx, maxRowRec, me.page_seg_map, segIdx, pageIdx, null, true);
+                    for (let dv of me.dispValueIdxLst[me.dispValueIdxLst.length - 1]) {
+                        if (dv[1] === JV.DISPLAY_VAL_TYPE_NORMAL) counterRowRecEx++;
+                    }
+                } else {
+                    private_addPageValue(me.dispValueIdxLst, me.segments[segIdx], grpSeqInfo, counterRowRec, maxRowRec,me.page_seg_map, segIdx, pageIdx, grpPageInfo, false);
+                    for (let dv of me.dispValueIdxLst[me.dispValueIdxLst.length - 1]) {
+                        if (dv[1] === JV.DISPLAY_VAL_TYPE_NORMAL) counterRowRec++;
+                    }
+                }
+            }
+            for (let segIdx = 0; segIdx < me.segments.length; segIdx++) {
+                let grpSeqInfo = (me.group_node_info)?me.group_node_info[segIdx]:null;
+                let grpRecAmt = (grpSeqInfo)?(grpSeqInfo.length*me.group_lines_amt):0;
+                let grpRecAmtEx = 0;
+                if (followTabEx && followTabEx.group_node_info) {
+                    grpRecAmtEx = followTabEx.group_node_info.length * followTabEx.group_lines_amt;
+                }
+                grpPageInfo[JV.PROP_SEG_GRP_IDX] = 0;
+                grpPageInfo[JV.PROP_INSERTED_GRP_REC] = 0;
+                grpPageInfo[JV.PROP_PRE_ADD_GRP_REC_INFO] = [];
+                grpPageInfo[JV.PROP_GRP_LINES] = me.group_lines_amt;
+                pageStatus[JV.STATUS_SEGMENT_START] = true;
+                private_resetBandArea();
+                let threshold = 0;
+                currentRecAmt = 0;
+                counterRowRec = 0;
+                counterRowRecEx = 0;
+                let ttlSegRecAmtNormal = me.segments[segIdx].length + grpRecAmt;
+                let ttlSegRecAmt = (followTabEx)?(me.segments[segIdx].length + grpRecAmt + followTabEx.segments[segIdx].length + grpRecAmtEx):(me.segments[segIdx].length + grpRecAmt);
+                while (true) {
+                    if (currentRecAmt > 0) pageStatus[JV.STATUS_SEGMENT_START] = false;
+                    if (pageIdx > 0) pageStatus[JV.STATUS_REPORT_START] = false;
+                    //开始判断各种scenarios
+                    if (ttlSegRecAmtNormal < ttlSegRecAmt) {
+                        if (currentRecAmt + maxRowRec > ttlSegRecAmtNormal) {
+                            if (currentRecAmt >= ttlSegRecAmtNormal) {
+                                //纯 followTabEx 数据
+                                if (currentRecAmt + maxRowRec >= ttlSegRecAmt) {
+                                    pageStatus[JV.STATUS_SEGMENT_END] = true;
+                                    private_resetBandArea();
+                                    let hasAdHocRow = !JpcFlowTabHelper.chkSegEnd(bands, rptTpl, ttlSegRecAmt, currentRecAmt, maxRowRec, me.isEx);
+                                    if (hasAdHocRow) {
+                                        //add page info(pre segment end)
+                                        pageStatus[JV.STATUS_SEGMENT_END] = false;
+                                        private_addPage(segIdx, null, true, false, -1);
+                                    }
+                                    //add page info
+                                    pageStatus[JV.STATUS_SEGMENT_END] = true;
+                                    private_addPage(segIdx, null, true, false, -1);
+                                } else {
+                                    private_addPage(segIdx, null, true, false, -1);
+                                }
+                            } else {
+                                //混合数据
+                                if (currentRecAmt + maxRowRec >= ttlSegRecAmt) {
+                                    pageStatus[JV.STATUS_SEGMENT_END] = true;
+                                    private_resetBandArea();
+                                    let hasAdHocRow = !JpcFlowTabHelper.chkSegEnd(bands, rptTpl, ttlSegRecAmt, currentRecAmt, maxRowRec, me.isEx);
+                                    if (hasAdHocRow) {
+                                        //add page info(pre segment end)
+                                        pageStatus[JV.STATUS_SEGMENT_END] = false;
+                                        private_addPage(segIdx, grpSeqInfo, false, true, ttlSegRecAmtNormal);
+                                    }
+                                    //add page info
+                                    pageStatus[JV.STATUS_SEGMENT_END] = true;
+                                    if (currentRecAmt >= ttlSegRecAmtNormal) {
+                                        //纯 followTabEx 数据啦
+                                        private_addPage(segIdx, null, true, false, -1);
+                                    } else {
+                                        private_addPage(segIdx, grpSeqInfo, false, true, ttlSegRecAmtNormal);
+                                    }
+                                } else {
+                                    private_addPage(segIdx, grpSeqInfo, false, true, ttlSegRecAmtNormal);
+                                }
+                            }
+                        } else {
+                            //纯followTab数据
+                            private_addPage(segIdx, grpSeqInfo, false, false, -1);
+                        }
+                    } else {
+                        if (currentRecAmt + maxRowRec >= ttlSegRecAmt) {
+                            pageStatus[JV.STATUS_SEGMENT_END] = true;
+                            private_resetBandArea();
+                            let hasAdHocRow = !JpcFlowTabHelper.chkSegEnd(bands, rptTpl, ttlSegRecAmt, currentRecAmt, maxRowRec, me.isEx);
+                            if (hasAdHocRow) {
+                                //add page info(pre segment end)
+                                pageStatus[JV.STATUS_SEGMENT_END] = false;
+                                private_addPage(segIdx, grpSeqInfo, false, false, -1);
+                            }
+                            //add page info
+                            pageStatus[JV.STATUS_SEGMENT_END] = true;
+                            private_addPage(segIdx, grpSeqInfo, false, false, -1);
+                        } else {
+                            private_addPage(segIdx, grpSeqInfo, false, false, -1);
+                        }
+                    }
+                    //检测是否可退出
+                    if (currentRecAmt >= ttlSegRecAmt) {
+                        break;
+                    }
+                    //控制阀值,超过阀值则强制退出,防止死循环
+                    threshold++;
+                    if (threshold > 500) {
+                        console.log("Hey! There may be a dead loop here!!!");
+                        break;
+                    }
+                }
+            }
+            // console.log(me.dispValueIdxLst);
+            rst = Math.ceil(pageIdx / me.multiCols);
+        }
+        me.pagesAmt = rst;
+        return rst;
+    };
+
+    JpcFlowTabResult.preSetupPages_Org = function (rptTpl, dataObj, defProperties, option, $CURRENT_RPT, followTabEx) {
         let me = this, rst = 1, counterRowRec = 0, maxRowRec = 1, pageIdx = 0;
+        me.setupGroupingData(rptTpl, dataObj, $CURRENT_RPT);
+        if (followTabEx) {
+            followTabEx.setupGroupingData(rptTpl, dataObj, $CURRENT_RPT);
+        }
+        //console.log(me.group_sum_values);
         me.paging_option = option||JV.PAGING_OPTION_NORMAL;
         let CURRENT_FLOW_INFO = (me.isEx)?JV.NODE_FLOW_INFO_EX:JV.NODE_FLOW_INFO;
         JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[CURRENT_FLOW_INFO][JV.NODE_FLOW_CONTENT][JV.PROP_FLOW_FIELDS], null, me.disp_fields_idx, me.isEx);
@@ -91,7 +410,11 @@ JpcFlowTabSrv.prototype.createNew = function(){
                 }
                 me.pageStatusLst.push(pageStatus.slice(0));
                 pageIdx++;
-                private_addPageValue(me.dispValueIdxLst, me.segments[segIdx], 0, me.segments[segIdx].length, me.page_seg_map, segIdx, pageIdx);
+                let grpSeqInfo = (me.group_node_info)?me.group_node_info[segIdx]:null;
+                private_addPageValue(me.dispValueIdxLst, me.segments[segIdx], grpSeqInfo, 0, me.segments[segIdx].length, me.page_seg_map, segIdx, pageIdx, null, false);
+                if (followTabEx) {
+                    //
+                }
             }
         } else {
             let bands = JpcBand.createNew(rptTpl, defProperties);
@@ -104,31 +427,55 @@ JpcFlowTabSrv.prototype.createNew = function(){
             }
             function private_resetBandArea() {
                 JpcBandHelper.setBandArea(bands, rptTpl, pageStatus, !me.isEx, me.isEx);
-                maxRowRec = JpcFlowTabHelper.getMaxRowsPerPage(bands, rptTpl);
+                maxRowRec = JpcFlowTabHelper.getMaxRowsPerPage(bands, rptTpl, me.isEx);
             }
+            let grpPageInfo = {};
             for (let segIdx = 0; segIdx < me.segments.length; segIdx++) {
+                let grpSeqInfo = (me.group_node_info)?me.group_node_info[segIdx]:null;
+                grpPageInfo[JV.PROP_SEG_GRP_IDX] = 0;
+                grpPageInfo[JV.PROP_INSERTED_GRP_REC] = 0;
+                grpPageInfo[JV.PROP_PRE_ADD_GRP_REC_INFO] = [];
+                grpPageInfo[JV.PROP_GRP_LINES] = me.group_lines_amt;
                 private_resetBandArea();
                 let orgMaxRowRec = maxRowRec;
-                let rowSplitCnt = Math.ceil(1.0 * me.segments[segIdx].length / orgMaxRowRec);
+                let grpRecAmt = (grpSeqInfo)?(grpSeqInfo.length*me.group_lines_amt):0;
+                let grpRecAmtEx = 0;
+                let rowSplitCnt = Math.ceil(1.0 * (me.segments[segIdx].length + grpRecAmt) / orgMaxRowRec);
+                let rowSplitCntEx = rowSplitCnt;
+                if (followTabEx) {
+                    rowSplitCntEx = Math.ceil(1.0 * (me.segments[segIdx].length + grpRecAmt + followTabEx.segments[segIdx].length + grpRecAmtEx) / orgMaxRowRec);
+                }
                 pageStatus[JV.STATUS_SEGMENT_END] = true;
                 private_resetBandArea();
-                let hasAdHocRow = !JpcFlowTabHelper.chkSegEnd(bands, rptTpl, me.segments, segIdx, (rowSplitCnt - 1) * orgMaxRowRec, maxRowRec);
-                if (hasAdHocRow) rowSplitCnt++;
-                if (rowSplitCnt % me.multiCols > 0) {
-                    rowSplitCnt++
+                let len = (followTabEx)?(me.segments[segIdx].length + grpRecAmt + followTabEx.segments[segIdx].length + grpRecAmtEx):(me.segments[segIdx].length + grpRecAmt);
+                //let hasAdHocRow = !JpcFlowTabHelper.chkSegEnd(bands, rptTpl, me.segments[segIdx].length, (rowSplitCntEx - 1) * orgMaxRowRec, maxRowRec, me.isEx);
+                let hasAdHocRow = !JpcFlowTabHelper.chkSegEnd(bands, rptTpl, len, (rowSplitCntEx - 1) * orgMaxRowRec, maxRowRec, me.isEx);
+                if (hasAdHocRow) rowSplitCntEx++;
+                if (rowSplitCntEx % me.multiCols > 0) {
+                    rowSplitCntEx++
                 }
-                for (let rowIdx = 0; rowIdx < rowSplitCnt; rowIdx++) {
-                    pageStatus[JV.STATUS_SEGMENT_END] = (rowIdx === (rowSplitCnt - 1));
+                counterRowRec = 0;
+                for (let segPageIdx = 0; segPageIdx < rowSplitCntEx; segPageIdx++) {
+                    pageStatus[JV.STATUS_SEGMENT_END] = (segPageIdx === (rowSplitCntEx - 1));
                     if (pageIdx > 0) pageStatus[JV.STATUS_REPORT_START] = false;
                     private_resetBandArea();
                     me.pageStatusLst.push(pageStatus.slice(0));
                     pageIdx++;
-                    counterRowRec = orgMaxRowRec * rowIdx;
-                    private_addPageValue(me.dispValueIdxLst, me.segments[segIdx], counterRowRec, maxRowRec,me.page_seg_map, segIdx, pageIdx);
+                    if (followTabEx) {
+                        //
+                    } else {
+                        //
+                    }
+                    private_addPageValue(me.dispValueIdxLst, me.segments[segIdx], grpSeqInfo, counterRowRec, maxRowRec,me.page_seg_map, segIdx, pageIdx, grpPageInfo, false);
+                    //备注: 考虑到分组数据是临时融入的,所以需要知道这一页的数据融入了多少条group数据,并且得考虑边界条件情况(group数据可能跨页!)
+                    for (let dv of me.dispValueIdxLst[me.dispValueIdxLst.length - 1]) {
+                        if (dv[1] === JV.DISPLAY_VAL_TYPE_NORMAL) counterRowRec++;
+                    }
                 }
                 pageStatus[JV.STATUS_SEGMENT_END] = false;
                 pageStatus[JV.STATUS_REPORT_START] = false;
             }
+            // console.log(me.dispValueIdxLst);
             rst = Math.ceil(pageIdx / me.multiCols);
         }
         me.pagesAmt = rst;
@@ -145,7 +492,7 @@ JpcFlowTabSrv.prototype.createNew = function(){
             //2. then reset the band height
             let tab = rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_CONTENT];
             let flowContentBand = bands[tab[JV.PROP_BAND_NAME]];
-            let actH = JpcFlowTabHelper.getActualContentAreaHeight(bands, rptTpl, me.segments, page);
+            let actH = JpcFlowTabHelper.getActualContentAreaHeight(bands, rptTpl, me.segments, page, me.isEx);
             let offsetY = actH - (flowContentBand.Bottom - flowContentBand.Top);
             JpcBandHelper.resetBandPos(rptTpl[JV.NODE_BAND_COLLECTION], bands, flowContentBand, 0, offsetY);
             // 2.1 Content-Tab
@@ -205,27 +552,66 @@ JpcFlowTabSrv.prototype.createNew = function(){
         let me = this, rst = [];
         let FLOW_NODE_STR = me.isEx?JV.NODE_FLOW_INFO_EX:JV.NODE_FLOW_INFO;
         let tab = rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_CONTENT];
+        let tabEx = (rptTpl[JV.NODE_FLOW_INFO_EX])?rptTpl[JV.NODE_FLOW_INFO_EX][JV.NODE_FLOW_CONTENT]:null;
         let band = bands[tab[JV.PROP_BAND_NAME]];
         if (band) {
             let pageStatus = me.pageStatusLst[page - 1];
             if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]]) {
                 let tab_fields = tab[JV.PROP_FLOW_FIELDS];
+                let tab_fields_ex = tabEx?tabEx[JV.PROP_FLOW_FIELDS]:null;
                 let data_details = me.isEx?dataObj[JV.DATA_DETAIL_DATA_EX]:dataObj[JV.DATA_DETAIL_DATA];
+                let data_details_ex = $CURRENT_RPT.isFollowMode?dataObj[JV.DATA_DETAIL_DATA_EX]:null;
                 let contentValuesIdx = me.dispValueIdxLst[page - 1];
-                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) {
-                        data_field = data_details[me.disp_fields_idx[i]];
-                    } else {
-                        data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
-                        if (data_field) {
-                            data_field = data_field[JV.PROP_AD_HOC_DATA];
+                //normal content
+                for (let rowIdx = 0; rowIdx < contentValuesIdx.length; rowIdx++) {
+                    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) {
+                            data_field = data_details[me.disp_fields_idx[i]];
+                        } else {
+                            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])) {
+                            // rst.push(me.outputTabField(band, tab_field, data_field, contentValuesIdx[rowIdx], -1, contentValuesIdx.length, rowIdx, 1, 0, unitFactor, true, controls, multiColIdx));
+                            //测试中
+                            if (contentValuesIdx[rowIdx][0] !== JV.TYPE_FOLLOW_MODE && contentValuesIdx[rowIdx][1] === JV.DISPLAY_VAL_TYPE_NORMAL) {
+                                rst.push(me.outputTabField(band, tab_field, data_field, contentValuesIdx[rowIdx][2], -1, contentValuesIdx.length, rowIdx, 1, 0, unitFactor, true, controls, multiColIdx));
+                            }
                         }
                     }
-                    if (!(tab_field[JV.PROP_HIDDEN])) {
-                        for (let rowIdx = 0; rowIdx < contentValuesIdx.length; rowIdx++) {
-                            rst.push(me.outputTabField(band, tab_field, data_field, contentValuesIdx[rowIdx], -1, contentValuesIdx.length, rowIdx, 1, 0, unitFactor, true, controls, multiColIdx));
+                    if (contentValuesIdx[rowIdx][0] === JV.TYPE_FOLLOW_MODE) {
+                        for (let i = 0; i < tab_fields_ex.length; i++) {
+                            let tab_field = tab_fields_ex[i];
+                            let data_field = null;
+                            if (me.disp_fields_idx[i] !== JV.BLANK_FIELD_INDEX) {
+                                data_field = data_details_ex[me.disp_fields_idx[i]];
+                            } else {
+                                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])) {
+                                if (contentValuesIdx[rowIdx][0] === JV.TYPE_FOLLOW_MODE && contentValuesIdx[rowIdx][1] === JV.DISPLAY_VAL_TYPE_NORMAL) {
+                                    rst.push(me.outputTabField(band, tab_field, data_field, contentValuesIdx[rowIdx][2], -1, contentValuesIdx.length, rowIdx, 1, 0, unitFactor, true, controls, multiColIdx));
+                                }
+                            }
+                        }
+                    }
+                }
+                //grouping content
+                for (let rowIdx = 0; rowIdx < contentValuesIdx.length; rowIdx++) {
+                    if (contentValuesIdx[rowIdx][1] === JV.DISPLAY_VAL_TYPE_GROUP) {
+                        for (let grpIdx = 0; grpIdx < rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_LINES].length; grpIdx++) {
+                            if (contentValuesIdx[rowIdx][3] === grpIdx) {
+                                let grp_line = rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_GROUP][JV.PROP_GROUP_LINES][grpIdx];
+                                let lineRst = me.outputTabGrpLine(band, grp_line, page, contentValuesIdx[rowIdx], contentValuesIdx.length, rowIdx, 1, 0, unitFactor, true, controls, multiColIdx)
+                                rst = rst.concat(lineRst);
+                            }
                         }
                     }
                 }
@@ -291,6 +677,31 @@ JpcFlowTabSrv.prototype.createNew = function(){
         rst[JV.PROP_AREA] = JpcAreaHelper.outputArea(tab_field[JV.PROP_AREA], band, unitFactor, rows, rowIdx, cols, colIdx, me.multiCols, multiColIdx, true, false);
         return rst;
     };
+    JpcFlowTabResult.outputTabGrpLine = function (band, grp_line, page, grpValueIdx, rows, rowIdx, cols, colIdx, unitFactor, isRow, controls, multiColIdx) {
+        let me = this, rst = [];
+        if (grp_line[JV.PROP_GROUP_SUM_KEYS]) {
+            let segIdx = JpcCommonHelper.getSegIdxByPageIdx(page, me.page_seg_map);
+            let curSegGrpSum = me.group_sum_values[segIdx];
+            for (let sumFieldNode of grp_line[JV.PROP_GROUP_SUM_KEYS]) {
+                let value = curSegGrpSum[sumFieldNode[JV.PROP_SUM_KEY]][grpValueIdx[2]];
+                let sumFldRst = JpcTextHelper.outputDirectValue(sumFieldNode, value,  band, unitFactor, rows, rowIdx, cols, colIdx, me.multiCols, multiColIdx)
+                rst.push(sumFldRst);
+            }
+        }
+        if (grp_line[JV.PROP_TEXTS]) {
+            for (let txt of grp_line[JV.PROP_TEXTS]) {
+                rst.push(JpcTextHelper.outputText(txt, band, unitFactor, rows, rowIdx, cols, colIdx, me.multiCols, multiColIdx));
+            }
+        }
+        if (grp_line[JV.PROP_DISCRETE_FIELDS]) {
+            //
+        }
+        if (grp_line[JV.PROP_PARAMS]) {
+            //
+        }
+        // console.log(rst);
+        return rst;
+    };
 
     return JpcFlowTabResult;
 };

+ 42 - 4
modules/reports/rpt_component/jpc_rte.js

@@ -46,15 +46,53 @@ let JE = {
         }
         return rst;
     },
-    setFieldValue: function (field, newValue, dataObj, valIdx) {
-        if (!(field.DataNodeName)) {
+    setFieldValue: function (field, dataObj, valIdx, newValue) {
+        if (field.DataNodeName === "NA") {
+            if (!field[JV.PROP_AD_HOC_DATA]) {
+                field[JV.PROP_AD_HOC_DATA] = [];
+            }
+            field[JV.PROP_AD_HOC_DATA][valIdx] = newValue;
+        } else if (!field.DataNodeName) {
             //that means this is a self-defined discrete field!
             field.DataNodeName = JV.DATA_DISCRETE_DATA;
-            let len = dataObj[JV.DATA_DISCRETE_DATA];
+            let len = dataObj[JV.DATA_DISCRETE_DATA].length;
             field.DataSeq = len;
             dataObj[JV.DATA_DISCRETE_DATA].push([]);
+            dataObj[field.DataNodeName][field.DataSeq][valIdx] = newValue;
+        } else {
+            dataObj[field.DataNodeName][field.DataSeq][valIdx] = newValue;
         }
-        dataObj[field.DataNodeName][field.DataSeq][valIdx] = newValue;
+    },
+    getFieldValue: function (field, dataObj, valIdx, dftVal) {
+        let rst = dftVal;
+        if (field.DataNodeName === "NA") {
+            if (!field[JV.PROP_AD_HOC_DATA]) {
+                field[JV.PROP_AD_HOC_DATA] = [];
+            }
+            if (field[JV.PROP_AD_HOC_DATA].length > valIdx) {
+                rst = field[JV.PROP_AD_HOC_DATA][valIdx];
+            } else {
+                if (dftVal === null && field[JV.PROP_AD_HOC_DATA].length > 0) {
+                    rst = field[JV.PROP_AD_HOC_DATA][field[JV.PROP_AD_HOC_DATA].length - 1];
+                }
+            }
+        } else {
+            if (!field.DataNodeName) {
+                //that means this is a self-defined discrete field!
+                field.DataNodeName = JV.DATA_DISCRETE_DATA;
+                let len = dataObj[JV.DATA_DISCRETE_DATA];
+                field.DataSeq = len;
+                dataObj[JV.DATA_DISCRETE_DATA].push([]);
+            }
+            if (dataObj[field.DataNodeName][field.DataSeq].length > valIdx) {
+                rst = dataObj[field.DataNodeName][field.DataSeq][valIdx];
+            } else {
+                if (dftVal === null && dataObj[field.DataNodeName][field.DataSeq].length > 0) {
+                    rst = dataObj[field.DataNodeName][field.DataSeq][dataObj[field.DataNodeName][field.DataSeq].length - 1];
+                }
+            }
+        }
+        return rst;
     }
 }
 

+ 275 - 73
modules/reports/util/rpt_construct_data_util.js

@@ -13,19 +13,83 @@ let projectConst = consts.projectConst;
 let projectConstList = consts.projectConstList;
 
 class Rpt_Common{
-    initialize(Projects) {
-        this.Projects = Projects;
+    initialize(rpt_tpl, currentDataObj) {
+        this.template = rpt_tpl;
+        this.currentDataObj = currentDataObj;
     };
-
-    getSerialNo(fieldId, $CURRENT_RPT, $CURRENT_DATA){
-        let itemSerialNoRec = $JE.F(fieldId, $CURRENT_RPT);
-        if (itemSerialNoRec) {
-            itemSerialNoRec[JV.PROP_AD_HOC_DATA] = [];
-            for (var innerFmlIdx = 0; innerFmlIdx < $CURRENT_DATA[JV.DATA_DETAIL_DATA][0].length; innerFmlIdx++) {
-                itemSerialNoRec[JV.PROP_AD_HOC_DATA][innerFmlIdx] = (innerFmlIdx + 1);
+    Multiply(val1, val2, fixFormat) {
+        let rst = [], maxLen = val1.length, minLen = val2.length;
+        if (minLen > maxLen) {
+            maxLen = maxLen + minLen; minLen = maxLen - minLen; maxLen = maxLen - minLen;
+        }
+        for (let i = 0; i < maxLen; i++) {
+            let value = ((i < val1.length)?val1[i]:val1[minLen - 1]) * ((i < val2.length)?val2[i]:val2[minLen - 1]);
+            if (fixFormat) value = value.toFixed(fixFormat);
+            rst.push(value);
+        }
+        return rst;
+    };
+    Divide(val1, val2, fixFormat) {
+        let rst = [], maxLen = val1.length, minLen = val2.length;
+        if (minLen > maxLen) {
+            maxLen = maxLen + minLen; minLen = maxLen - minLen; maxLen = maxLen - minLen;
+        }
+        for (let i = 0; i < maxLen; i++) {
+            let value = ((i < val1.length)?val1[i]:val1[minLen - 1]) / ((i < val2.length)?val2[i]:val2[minLen - 1]);
+            if (fixFormat) value = value.toFixed(fixFormat);
+            rst.push(value);
+        }
+        return rst;
+    };
+    Plus(val1, val2, fixFormat) {
+        let rst = [], maxLen = val1.length, minLen = val2.length;
+        if (minLen > maxLen) {
+            maxLen = maxLen + minLen; minLen = maxLen - minLen; maxLen = maxLen - minLen;
+        }
+        for (let i = 0; i < maxLen; i++) {
+            let value = ((i < val1.length)?val1[i]:val1[minLen - 1]) + ((i < val2.length)?val2[i]:val2[minLen - 1]);
+            if (fixFormat) value = value.toFixed(fixFormat);
+            rst.push(value);
+        }
+        return rst;
+    };
+    MultiPlus(arrVal, fixFormat) {
+        let rst = [];
+        for (let i = 0; i < arrVal.length; i++) {
+            let valItem = arrVal[i];
+            if (i === 0) {
+                for (let dtl of valItem) {
+                    let value = parseFloat(dtl);
+                    if (fixFormat) value = value.toFixed(fixFormat);
+                    rst.push(value);
+                }
+            } else {
+                for (let j = 0; j < valItem.length; j++) {
+                    if (j < rst.length) {
+                        let value = rst[j] + valItem[j];
+                        if (fixFormat) value = value.toFixed(fixFormat);
+                        rst[j] = value;
+                    } else {
+                        let value = parseFloat(valItem[j]);
+                        if (fixFormat) value = value.toFixed(fixFormat);
+                        rst.push(value);
+                    }
+                }
             }
-            itemSerialNoRec = null;
         }
+        return rst;
+    };
+    Minus(val1, val2, fixFormat) {
+        let rst = [], maxLen = val1.length, minLen = val2.length;
+        if (minLen > maxLen) {
+            maxLen = maxLen + minLen; minLen = maxLen - minLen; maxLen = maxLen - minLen;
+        }
+        for (let i = 0; i < maxLen; i++) {
+            let value = ((i < val1.length)?val1[i]:val1[minLen - 1]) - ((i < val2.length)?val2[i]:val2[minLen - 1]);
+            if (fixFormat) value = value.toFixed(fixFormat);
+            rst.push(value);
+        }
+        return rst;
     };
 }
 
@@ -38,6 +102,7 @@ class Rpt_Data_Extractor {
         this.rptTpl = tpl;
     };
 
+    //-- 根据报表模板映射指标(非离散指标)的定义,罗列出所有需要用到的data对象key,作为数据请求的过滤依据
     getDataRequestFilter() {
         let rst = [];
         let tpl = this.rptTpl;
@@ -47,47 +112,41 @@ class Rpt_Data_Extractor {
                     if (field[JV.PROP_FIELD_EXP_MAP]) {
                         if (field[JV.PROP_FIELD_EXP_MAP].indexOf('.' + key + '.') >= 0) {
                             rst.push(key);
+                            if (key === projectConst.RATION_GLJ && (rst.indexOf(projectConst.PROJECTGLJ) < 0)) {
+                                rst.push(projectConst.PROJECTGLJ);
+                            }
+                            if (key === projectConst.PROJECTGLJ && (rst.indexOf(projectConst.RATION_GLJ) < 0)) {
+                                rst.push(projectConst.RATION_GLJ);
+                            }
                         }
                     }
                 }
             }
         };
-        if (tpl[JV.NODE_FIELD_MAP][JV.NODE_DISCRETE_FIELDS]) {
-            for (let field of tpl[JV.NODE_FIELD_MAP][JV.NODE_DISCRETE_FIELDS]) {
-                pri_func_chk_filter(field);
-            }
-        }
-        if (tpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS]) {
-            for (let field of tpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS]) {
-                pri_func_chk_filter(field);
-            }
-        }
-        if (tpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS]) {
-            for (let field of tpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS]) {
-                pri_func_chk_filter(field);
-            }
-        }
-        if (tpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS_EX]) {
-            for (let field of tpl[JV.NODE_FIELD_MAP][JV.NODE_MASTER_FIELDS_EX]) {
-                pri_func_chk_filter(field);
-            }
-        }
-        if (tpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS_EX]) {
-            for (let field of tpl[JV.NODE_FIELD_MAP][JV.NODE_DETAIL_FIELDS_EX]) {
-                pri_func_chk_filter(field);
+        let pri_setup_filter = function (FIELD_LIST_KEY) {
+            if (tpl[JV.NODE_FIELD_MAP][FIELD_LIST_KEY]) {
+                for (let field of tpl[JV.NODE_FIELD_MAP][FIELD_LIST_KEY]) {
+                    pri_func_chk_filter(field);
+                }
             }
-        }
+        };
+        pri_setup_filter(JV.NODE_DISCRETE_FIELDS);
+        pri_setup_filter(JV.NODE_MASTER_FIELDS);
+        pri_setup_filter(JV.NODE_DETAIL_FIELDS);
+        pri_setup_filter(JV.NODE_MASTER_FIELDS_EX);
+        pri_setup_filter(JV.NODE_DETAIL_FIELDS_EX);
         return rst;
     };
 
     //--- 装配数据(把收集到的数据,依据报表模板的指示,预处理(如:排序、过滤、合计)及装配到相关指标) ---//
     assembleData(rawDataObj) {
-        let $PROJECT = {"COMMON": {}, "MAIN": {}, "DETAIL": {}};
+        let $PROJECT = {"COMMON": null, "MAIN": {}, "DETAIL": {}};
         let tpl = this.rptTpl;
+        this.COMMON.initialize(tpl, rawDataObj);
+        $PROJECT.COMMON = this.COMMON;
         $PROJECT.MAIN["myOwnRawDataObj"] = rawDataObj.prj._doc;
         $PROJECT.MAIN.getProperty = ext_mainGetPropety;
         $PROJECT.MAIN.getFee = ext_mainGetFee;
-        $PROJECT.DETAIL.getRationPropertyByID = ext_getRationPropertyByID;
         if (tpl[JV.NODE_MAP_DATA_HANDLE_INFO]) {
             for (let preHandle of tpl[JV.NODE_MAP_DATA_HANDLE_INFO]) {
                 let srcData = getModuleDataByKey(rawDataObj.prjData, preHandle[JV.PROP_DATA_KEY]);
@@ -307,9 +366,18 @@ function sortData(sourceData, sortCfg) {
 
 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;
+    obj[prop].getPropertyByForeignId = ext_getPropertyByForeignId;
+    obj[prop].getArrayItemByKey = ext_getArrayItemByKey;
+    if (prop === projectConst.CALC_PROGRAM) obj[prop].getCalcProperty = ext_getCalcProperty;
+    if (prop === projectConst.FEERATE) obj[prop].getFeeRate = ext_getFeeRate;
 }
 
 function assembleFields(fieldList, rstDataArr, $PROJECT) {
@@ -327,11 +395,9 @@ function shielded_exec_env($PROJECT, $ME, rptDataItemObj) {
 }
 
 function ext_mainGetPropety(propKey) {
-    let rst = [];
-    let parentObj = this;
-    //console.log(this);
+    let rst = [], parentObj = this;
     let dtObj = parentObj["myOwnRawDataObj"];
-    if ((dtObj) && (propKey)) {
+    if (propKey && dtObj) {
         if (dtObj.hasOwnProperty("property")) {
             rst.push(dtObj["property"][propKey]);
         } else  {
@@ -341,7 +407,25 @@ function ext_mainGetPropety(propKey) {
     return rst;
 }
 
-function ext_mainGetFee(feeKey) {
+function ext_getPropety(propKey) {
+    let rst = [], parentObj = this;
+    let dtObj = parentObj["myOwnRawDataObj"];
+    if (propKey && dtObj) {
+        for (let dItem of dtObj.data) {
+            let doc = (dItem._doc === null || dItem._doc === undefined)?dItem:dItem._doc;
+            if (doc.hasOwnProperty("property")) {
+                rst.push(doc["property"][propKey]);
+            } else if (doc.hasOwnProperty(propKey)) {
+                rst.push(doc[propKey]);
+            } else {
+                rst.push('');
+            }
+        }
+    }
+    return rst;
+}
+
+function ext_mainGetFee(feeKey, dtlFeeKey) {
     let rst = [];
     let parentObj = this;
     let dtObj = parentObj["myOwnRawDataObj"];
@@ -349,7 +433,11 @@ function ext_mainGetFee(feeKey) {
         if (dtObj.hasOwnProperty("fees")) {
             for (let fee of dtObj["fees"]) {
                 if (fee["fieldName"] === feeKey) {
-                    rst.push(dtObj["fees"][feeKey]);
+                    if (dtlFeeKey) {
+                        rst.push(fee[dtlFeeKey]);
+                    } else {
+                        rst.push(fee["unitFee"]);
+                    }
                     break;
                 }
             }
@@ -362,56 +450,170 @@ function ext_mainGetFee(feeKey) {
     return rst;
 }
 
-function ext_getPropety(propKey) {
-    let rst = [], parentObj = this;
-    let dtObj = parentObj["myOwnRawDataObj"];
-    if (propKey && dtObj) {
-        for (let dItem of dtObj.data) {
-            let doc = (dItem._doc === null || dItem._doc === undefined)?dItem:dItem._doc;
-            if (doc.hasOwnProperty("property")) {
-                rst.push(doc["property"][propKey]);
-            } else if (doc.hasOwnProperty(propKey)) {
-                rst.push(doc[propKey]);
-            } else {
-                rst.push('');
-            }
-        }
-    }
-    return rst;
-}
-
-function ext_getFee(feeKey) {
+function ext_getFee(feeKey, dtlFeeKey) {
     let rst = [], parentObj = this;
     let dtObj = parentObj["myOwnRawDataObj"];
     if (feeKey && dtObj) {
         for (let dItem of dtObj.data) {
+            let hasValue = false;
             if (dItem.hasOwnProperty("fees")) {
                 for (let fee of dItem["fees"]) {
                     if (fee["fieldName"] === feeKey) {
-                        rst.push(dItem["fees"][feeKey]);
+                        if (dtlFeeKey) {
+                            rst.push(fee[dtlFeeKey]);
+                        } else {
+                            rst.push(fee["unitFee"]);
+                        }
+                        hasValue = true;
                         break;
                     }
                 }
             } else if (dItem.hasOwnProperty(feeKey)) {
+                hasValue = true;
                 rst.push(dItem[feeKey]);
             } else {
-                rst.push[0];
+                hasValue = true;
+                rst.push(0);
             }
+            if (!hasValue) {
+                rst.push(0);
+            }
+        }
+    }
+    for (let i = 0; i < rst.length; i++) {
+        rst[i] = parseFloat(rst[i]);
+    }
+    return rst;
+}
+
+function ext_getCalcProperty(templateIDs, calcItemKey, calcItemKeyVal, calcItemRstKey){
+    let rst = [], parentObj = this; //this should be "calc_program" object
+    let dtObj = parentObj["myOwnRawDataObj"];
+    let optimizeObj = {};
+    let private_getProperty = function (cId) {
+        let calcTplObj = optimizeObj["calc_program_" + cId];
+        if (!calcTplObj) {
+            let templates = (dtObj.data._doc)?dtObj.data._doc.templates:dtObj.data.templates;
+            for (let tpl of templates) {
+                if (cId === tpl.ID) {
+                    optimizeObj["calc_program_" + cId] = tpl;
+                    calcTplObj = tpl;
+                    break;
+                }
+            }
+        }
+        if (calcTplObj) {
+            for (let calcItem of calcTplObj.calcItems) {
+                if (calcItem[calcItemKey] === calcItemKeyVal) {
+                    rst.push(calcItem[calcItemRstKey]);
+                    break;
+                }
+            }
+        }
+    };
+    if (templateIDs instanceof Array) {
+        for (let tplId of templateIDs) {
+            private_getProperty(tplId);
         }
+    } else {
+        private_getProperty(templateIDs);
     }
+    optimizeObj = null;
     return rst;
 }
 
-function ext_getRationPropertyByID(IdVal, propKey) {
-    let rst = [], me = this;
-    if (IdVal !== null && IdVal !== undefined && me[projectConst.RATION]) {
+function ext_getFeeRate(fee_Ids){
+    let rst = [], parentObj = this; //this should be "feeRate" object
+    let dtObj = parentObj["myOwnRawDataObj"];
+    let optimizeObj = {};
+    let private_getFeeRate = function (fId) {
+        let feeRateItemObj = optimizeObj["fee_rates_" + fId];
+        if (!feeRateItemObj) {
+            let rates = (dtObj.data._doc)?dtObj.data._doc.rates:dtObj.data.rates;
+            for (let feeItem of rates) {
+                if (fId === feeItem.ID) {
+                    optimizeObj["fee_rates_" + fId] = feeItem;
+                    feeRateItemObj = feeItem;
+                    break;
+                }
+            }
+        }
+        if (feeRateItemObj) {
+            rst.push(feeRateItemObj.rate);
+        } else {
+            rst.push(0);
+        }
+    };
+    if (fee_Ids instanceof Array) {
+        for (let fId of fee_Ids) {
+            private_getFeeRate(fId);
+        }
+    } else {
+        private_getFeeRate(fee_Ids);
+    }
+    optimizeObj = null;
+    return rst;
+}
+
+function ext_getArrayItemByKey(arrayKey, itemKey, itemKeyValue, itemRstKey){
+    let rst = [], parentObj = this;
+    let dtObj = parentObj["myOwnRawDataObj"];
+    let private_getItemValue = function (arr, dtlItKV) {
+        for (let item of arr) {
+            if (item[itemKey] === dtlItKV) {
+                if (itemRstKey) {
+                    rst.push(item[itemRstKey]);
+                } else {
+                    rst.push(item);
+                }
+                break;
+            }
+        }
+    };
+    let arr = dtObj[arrayKey];
+    if (arr && arr instanceof Array) {
+        if (itemKeyValue instanceof Array) {
+            for (let dtlItemKeyVal of itemKeyValue) {
+                private_getItemValue(arr, dtlItemKeyVal);
+            }
+        } else {
+            private_getItemValue(arr, itemKeyValue);
+        }
+    }
+
+}
+
+function ext_getPropertyByForeignId(foreignIdVal, adHocIdKey, propKey) {
+    let rst = [], parentObj = this;
+    let IdKey = (adHocIdKey)?adHocIdKey:"ID";
+    let dtObj = parentObj["myOwnRawDataObj"];
+    if (foreignIdVal !== null && foreignIdVal !== undefined) {
         let isFound = false;
-        if (IdVal instanceof Array) {
-            for (let id of IdVal) {
+        if (foreignIdVal instanceof Array) {
+            for (let idVal of foreignIdVal) {
                 isFound = false;
-                for (let item of me[projectConst.RATION]["myOwnRawDataObj"].data) {
-                    if (item.ID === id) {
-                        rst.push(item[propKey]);
+                for (let i = 0; i < dtObj.data.length; i++) {
+                    let item = (dtObj.data[i]._doc)?dtObj.data[i]._doc:dtObj.data[i];
+                    if (item[IdKey] === idVal) {
+                        let splitPKey = propKey.split(".");
+                        if (splitPKey.length > 1) {
+                            let rstP = null;
+                            for (let i = 0; i < splitPKey.length; i++) {
+                                if (i === 0) {
+                                    rstP = item[splitPKey[i]];
+                                } else {
+                                    if (splitPKey[i].indexOf("[") === 0 && splitPKey[i].indexOf("]") === (splitPKey[i].length - 1)) {
+                                        //考虑数组情况^_^!!!
+                                    } else {
+                                        //
+                                    }
+                                    rstP = rstP[splitPKey[i]];
+                                }
+                            }
+                            rst.push(rstP);
+                        } else {
+                            rst.push(item[propKey]);
+                        }
                         isFound = true;
                         break;
                     }
@@ -419,8 +621,8 @@ function ext_getRationPropertyByID(IdVal, propKey) {
                 // if (!isFound) rst.push[null];
             }
         } else {
-            for (let item of me[projectConst.RATION]["myOwnRawDataObj"].data) {
-                if (item.ID === IdVal) {
+            for (let item of dtObj.data) {
+                if (item[IdKey] === foreignIdVal) {
                     rst.push(item[propKey]);
                     isFound = true;
                     break;

+ 196 - 69
public/calc_util.js

@@ -22,26 +22,64 @@ let executeObj = {
             base = me.calcBase[calcBaseName];
 
         if (base != null) {
-            let price = 0, aprice = 0, mprice = 0, tmpSum = 0;
-            for (let glj of me.treeNode.data.gljList) {
-                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;
+            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])) {
+                for (let glj of me.treeNode.data.gljList) {
+                       if (glj.type == gljType.GENERAL_MACHINE) {
+                            // 获取机械组成物
+                           let mds = projectObj.project.composition.getCompositionByCode(glj.code);
+                           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);
+                               }
+                           };
+                           tmpSum = tmpSum + (glj["quantity"] * mdSum).toDecimal(me.digitDefault);
+                           tmpSum = (tmpSum).toDecimal(me.digitDefault);
+                       }
+                };
+            }else{
+                for (let glj of me.treeNode.data.gljList) {
+                    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 (!price) price = 0;
+                        tmpSum = tmpSum + (glj["quantity"] * price).toDecimal(me.digitDefault);
+                        tmpSum = (tmpSum).toDecimal(me.digitDefault);
                     };
-                    if (!price) price = 0;
-                    tmpSum = tmpSum + glj["quantity"] * price;
                 };
             };
-            rst = tmpSum;
+
+            rst = (tmpSum).toDecimal(me.digitDefault);
         };
+
         return rst;
+    },
+    HJ: function () {
+        let me = this;
+        return me.treeNode.data.baseTotalPrice;
     }
 };
 
@@ -190,9 +228,18 @@ class Calculation {
     // 先编译公用的基础数据
     compilePublics(feeRates, labourCoes, feeTypes, calcBases){
         let me = this;
+        me.compiledFeeRates = {};
+        me.compiledLabourCoes = {};
+        me.compiledTemplates = {};
+        me.compiledFeeTypes = {};
+        me.compiledFeeTypeNames = [];
+        me.compiledCalcBases = {};
+        me.saveForReports = [];
+        me.digit = 2;
+        me.digitDefault = 6;
+
         let private_compile_feeRateFile = function() {
             if (feeRates) {
-                me.compiledFeeRates = {};
                 for (let rate of feeRates) {
                     me.compiledFeeRates["feeRate_" + rate.ID] = rate;
                 }
@@ -200,7 +247,6 @@ class Calculation {
         };
         let private_compile_labourCoeFile = function() {
             if (labourCoes) {
-                me.compiledLabourCoes = {};
                 for (let coe of labourCoes) {
                     me.compiledLabourCoes["LabourCoe_" + coe.ID] = coe;
                 }
@@ -208,8 +254,6 @@ class Calculation {
         };
         let private_compile_feeType = function() {
             if (feeTypes) {
-                me.compiledFeeTypes = {};
-                me.compiledFeeTypeNames = [];
                 for (let ft of feeTypes) {
                     me.compiledFeeTypes[ft.type] = ft.name;
                     me.compiledFeeTypes[ft.name] = ft.type;    // 中文预编译,可靠性有待验证
@@ -219,7 +263,6 @@ class Calculation {
         };
         let private_compile_calcBase = function() {
             if (calcBases) {
-                me.compiledCalcBases = {};
                 for (let cb of calcBases) {
                     me.compiledCalcBases[cb.dispName] = cb;         // 中文预编译,可靠性有待验证
                 }
@@ -230,7 +273,6 @@ class Calculation {
         private_compile_labourCoeFile();
         private_compile_feeType();
         private_compile_calcBase();
-        me.compiledTemplates = {};
     };
 
     compileTemplate(template){
@@ -294,18 +336,28 @@ class Calculation {
         let private_compile_items = function() {
             for (let idx of template.compiledSeq) {
                 let item = template.calcItems[idx];
-                item.compiledExpr = item.expression.split('@(').join('$CE.at(');
-                item.compiledExpr = item.compiledExpr.split('base(').join('$CE.base(');
+                item.dispExprUser = item.dispExpr;    // 用于界面显示。disExpr是公式模板,不允许修改:人工系数占位符被修改后变成数值,第二次无法正确替换。
+                if (item.expression == 'HJ')
+                    item.compiledExpr = '$CE.HJ()'
+                else{
+                    item.compiledExpr = item.expression.split('@(').join('$CE.at(');
+                    item.compiledExpr = item.compiledExpr.split('base(').join('$CE.base(');
+                };
 
                 if (item.labourCoeID){
                     let lc = me.compiledLabourCoes["LabourCoe_" + item.labourCoeID].coe;
-                    item.dispExpr = item.dispExpr.replace(/L/gi, lc.toString());
+                    item.dispExprUser = item.dispExpr.replace(/L/gi, lc.toString());
                     item.compiledExpr = item.compiledExpr.replace(/L/gi, lc.toString());
                 };
 
                 if (item.feeRateID) {
-                    var cmf = me.compiledFeeRates["feeRate_" + item.feeRateID];
+                    let orgFeeRate = item.feeRate;
+                    let cmf = me.compiledFeeRates["feeRate_" + item.feeRateID];
                     item.feeRate = cmf?cmf.rate:100;
+
+                    if (!orgFeeRate || (orgFeeRate && orgFeeRate != item.feeRate)){
+                        me.saveForReports.push({templatesID: template.ID, calcItem: item});
+                    }
                 };
 
                 // 字段名映射
@@ -335,55 +387,130 @@ class Calculation {
         };
     };
 
-    calculate($treeNode){
-        let me = this;
-        let templateID = $treeNode.data.calcTemplateID;
-        if (!templateID) templateID = 1;
-        let template = me.compiledTemplates[templateID];
-        $treeNode.data.calcTemplate = template;
-
-        if ($treeNode && template.hasCompiled) {
-            let $CE = executeObj;
-            $CE.treeNode = $treeNode;
-            $CE.template = template;
-            $CE.calcBase = me.compiledCalcBases;
-
-            if (!$treeNode.data.fees) {
-                $treeNode.data.fees = [];
-                $treeNode.data.feesIndex = {};
+    initFees(treeNode){
+        if (!treeNode.data.fees) {
+            treeNode.data.fees = [];
+            treeNode.data.feesIndex = {};
+            treeNode.changed = true;
+        };
+    };
+
+    checkFee(treeNode, feeObj){
+        if (feeObj.fieldName == '') return;
+
+        if (!treeNode.data.feesIndex[feeObj.fieldName]){
+            let fee = {
+                'fieldName': feeObj.fieldName,
+                'unitFee': feeObj.unitFee,
+                'totalFee': feeObj.totalFee,
+                'tenderUnitFee': 0,
+                'tenderTotalFee': 0
+            };
+            treeNode.data.fees.push(fee);
+            treeNode.data.feesIndex[feeObj.fieldName] = fee;
+            treeNode.changed = true;
+        }
+        else{
+            if (treeNode.data.feesIndex[feeObj.fieldName].unitFee != feeObj.unitFee){
+                treeNode.data.feesIndex[feeObj.fieldName].unitFee = feeObj.unitFee;
+                treeNode.changed = true;
             };
 
-            for (let idx of template.compiledSeq) {
-                let calcItem = template.calcItems[idx];
-
-                let feeRate = calcItem.feeRate;
-                if (!feeRate) feeRate = 100;    // 100%
-                calcItem.unitFee = eval(calcItem.compiledExpr) * feeRate * 0.01;   // 如果eval()对清单树有影响,就换成小麦的Expression对象再试
-
-                let quantity = $treeNode.data.quantity;
-                if (!quantity) quantity = 0;
-                calcItem.totalFee = calcItem.unitFee * quantity;
-
-                // 费用同步到定额
-                // 引入小麦的字段检测后,快速切换定额出现计算卡顿现象,过多的循环造成。这里把她的代码拆出来,减少微循环。
-                if (!$treeNode.data.feesIndex[calcItem.fieldName]){
-                    let fee = {
-                        'fieldName': calcItem.fieldName,
-                        'unitFee': calcItem.unitFee,
-                        'totalFee': calcItem.totalFee,
-                        'tenderUnitFee': 0,
-                        'tenderTotalFee': 0
+            if (treeNode.data.feesIndex[feeObj.fieldName].totalFee != feeObj.totalFee){
+                treeNode.data.feesIndex[feeObj.fieldName].totalFee = feeObj.totalFee;
+                treeNode.changed = true;
+            };
+        };
+    };
+
+    calculate(treeNode){
+        let me = this;
+        let project = projectObj.project;
+
+        // 汇总定额或子清单的费用类别
+        if (treeNode.data.gatherType != undefined){
+            if (treeNode.sourceType != project.Bills.getSourceType()) return;
+
+            me.initFees(treeNode);
+
+            let objsArr = (treeNode.data.gatherType === CP_GatherType.rations) ? 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.data.gatherType === CP_GatherType.rations) ? 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);
                     };
-                    $treeNode.data.fees.push(fee);
-                    $treeNode.data.feesIndex[calcItem.fieldName] = fee;
-                }
-                else{
-                    $treeNode.data.feesIndex[calcItem.fieldName].unitFee = calcItem.unitFee;
-                    $treeNode.data.feesIndex[calcItem.fieldName].totalFee = calcItem.totalFee;
-                }
-            }
+                };
+                ftObj.unitFee = uf.toDecimal(me.digit);
+                ftObj.totalFee = tf.toDecimal(me.digit);
+                ftObj.tenderUnitFee = tuf.toDecimal(me.digit);
+                ftObj.tenderTotalFee = ttf.toDecimal(me.digit);
+
+                me.checkFee(treeNode, ftObj);
 
+                rst.push(ftObj);
+            };
+            treeNode.data.calcTemplate = {"calcItems": rst};
         }
+        else{
+            // 叶子清单的缺省计算程序需要提供总金额作为计算基数(不需要工料机),然后每条按比例(费率)计算,不需要工料机明细。
+            if (treeNode.data.baseTotalPrice != undefined){
+                if (treeNode.sourceType != project.Bills.getSourceType()) return;
+
+                delete treeNode.data.gljList;
+
+                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);
+                };
+
+                if (treeNode.data.programID == undefined){
+                    treeNode.data.programID = 1;
+                };
+            };
+
+            let template = me.compiledTemplates[treeNode.data.programID];
+            treeNode.data.calcTemplate = template;
+
+            if (treeNode && template.hasCompiled) {
+                let $CE = executeObj;
+                $CE.treeNode = treeNode;
+                $CE.template = template;
+                $CE.calcBase = me.compiledCalcBases;
+
+                me.initFees(treeNode);
+
+                for (let idx of template.compiledSeq) {
+                    let calcItem = template.calcItems[idx];
+
+                    let feeRate = calcItem.feeRate;
+                    if (!feeRate) feeRate = 100;    // 100%
+                    calcItem.unitFee = (eval(calcItem.compiledExpr) * feeRate * 0.01).toDecimal(me.digit);   // 如果eval()对清单树有影响,就换成小麦的Expression对象再试
+
+                    let quantity = treeNode.data.quantity;
+                    if (!quantity) quantity = 0;
+                    calcItem.totalFee = (calcItem.unitFee * quantity).toDecimal(me.digit);
+
+                    me.checkFee(treeNode, calcItem);
+                };
+            }
+        };
     }
 };
 

+ 8 - 8
public/fsUtil.js

@@ -2,26 +2,26 @@
  * Created by Tony on 2017/4/10.
  */
 
-var fs = require('fs');
+let fs = require('fs');
 
 module.exports = {
     writeArrayToFile: function(arr, filePath) {
         if (arr && filePath && Array.isArray(arr)) {
-            var chunks = [], len = 0;
-            for (var i = 0; i < arr.length; i++) {
-                var buffer = new Buffer(arr[i]);
+            let chunks = [], len = 0;
+            for (let i = 0; i < arr.length; i++) {
+                let buffer = new Buffer(arr[i]);
                 chunks.push(buffer);
                 len += buffer.length;
                 //
             }
-            var resultBuffer = new Buffer(len);
-            for(var i=0,size=chunks.length,pos=0;i<size;i++){
+            let resultBuffer = new Buffer(len);
+            for(let i=0,size=chunks.length,pos=0;i<size;i++){
                 chunks[i].copy(resultBuffer,pos);
                 pos += chunks[i].length;
             }
             fs.writeFile(filePath, resultBuffer, function(err){
                 if(err) throw err;
-                console.log('Write file: ' + filePath + ' ok!');
+                //console.log('Write file: ' + filePath + ' ok!');
             });
         }
     },
@@ -32,4 +32,4 @@ module.exports = {
             this.writeArrayToFile(arr, filePath);
         }
     }
-}
+};

+ 1 - 1
public/scMathUtil.js

@@ -4,7 +4,7 @@
 const fs = require('fs');
 let scMath = null;
 
-getScMathUtil = function() {
+let getScMathUtil = function() {
     if (!(scMath)) {
         let data = fs.readFileSync(__dirname + '/web/scMathUtil.js', 'utf8', 'r');
         //console.log(data);

+ 42 - 42
public/stringUtil.js

@@ -1,5 +1,5 @@
-var pinyin = (function (){
-    var Pinyin = function (ops){
+let pinyin = (function (){
+    let Pinyin = function (ops){
             this.initialize(ops);
         },
 
@@ -23,10 +23,10 @@ var pinyin = (function (){
 
         // 提取拼音, 返回首字母大写形式
         getFullChars: function(str){
-            var result = '', name;
-            var reg = new RegExp('[a-zA-Z0-9\- ]');
-            for (var i=0, len = str.length; i < len; i++){
-                var ch = str.substr(i,1), unicode = ch.charCodeAt(0);
+            let result = '', name;
+            let reg = new RegExp('[a-zA-Z0-9\- ]');
+            for (let i=0, len = str.length; i < len; i++){
+                let ch = str.substr(i,1), unicode = ch.charCodeAt(0);
                 if(unicode > 40869 || unicode < 19968){
                     result += ch;
                 }else{
@@ -43,10 +43,10 @@ var pinyin = (function (){
         getCamelChars: function(str){
             if(typeof(str) !== 'string')
                 throw new Error(-1, "函数getFisrt需要字符串类型参数!");
-            var chars = []; //保存中间结果的数组
-            for(var i=0,len=str.length; i < len; i++){
+            let chars = []; //保存中间结果的数组
+            for(let i=0,len=str.length; i < len; i++){
                 //获得unicode码
-                var ch = str.charAt(i);
+                let ch = str.charAt(i);
                 //检查该unicode码是否在处理范围之内,在则返回该码对映汉字的拼音首字母,不在则调用其它函数处理
                 chars.push(this._getChar(ch));
             }
@@ -57,9 +57,9 @@ var pinyin = (function (){
 
         // 提取拼音
         _getFullChar: function(str){
-            for (var key in this.full_dict){
+            for (let key in this.full_dict){
                 if(-1 !== this.full_dict[key].indexOf(str)){
-                    return this._capitalize(key); break;
+                    return this._capitalize(key);
                 }
             }
             return false;
@@ -68,14 +68,14 @@ var pinyin = (function (){
         // 首字母大写
         _capitalize: function(str){
             if(str.length>0){
-                var first = str.substr(0,1).toUpperCase();
-                var spare = str.substr(1,str.length);
+                let first = str.substr(0,1).toUpperCase();
+                let spare = str.substr(1,str.length);
                 return first + spare;
             }
         },
 
         _getChar: function(ch){
-            var unicode = ch.charCodeAt(0);
+            let unicode = ch.charCodeAt(0);
             //如果不在汉字处理范围之内,返回原字符,也可以调用自己的处理函数
             if(unicode > 40869 || unicode < 19968)
                 return ch; //dealWithOthers(ch);
@@ -88,21 +88,21 @@ var pinyin = (function (){
         _getResult: function(chars){
             if(!this.options.checkPolyphone)
                 return chars.join('');
-            var result = [''];
-            for(var i=0,len=chars.length;i<len;i++){
-                var str = chars[i], strlen = str.length;
+            let result = [''];
+            for(let i=0,len=chars.length;i<len;i++){
+                let str = chars[i], strlen = str.length;
                 if(strlen == 1){
-                    for(var j=0; j < result.length; j++){
+                    for(let j=0; j < result.length; j++){
                         result[k] += str;
                     }
                 }else{
-                    var swap1 = result.slice(0);
+                    let swap1 = result.slice(0);
                     result = [];
-                    for(var j=0; j < strlen; j++){
+                    for(let j=0; j < strlen; j++){
                         //复制一个相同的arrRslt
-                        var swap2 = swap1.slice(0);
+                        let swap2 = swap1.slice(0);
                         //把当前字符str[k]添加到每个元素末尾
-                        for(var k=0; k < swap2.length; k++){
+                        for(let k=0; k < swap2.length; k++){
                             swap2[k] += str.charAt(j);
                         }
                         //把复制并修改后的数组连接到arrRslt上
@@ -115,8 +115,8 @@ var pinyin = (function (){
 
     };
 
-    var extend = function(dst, src){
-        for(var property in src){
+    let extend = function(dst, src){
+        for(let property in src){
             dst[property] = src[property];
         }
         return dst;
@@ -127,51 +127,51 @@ var pinyin = (function (){
 
 module.exports = {
     isEmptyString: function(str) {
-        var rst = false;
-        if (str == null || str == undefined) {
+        let rst = false;
+        if (str === null || str === undefined) {
             rst = true;
         } else if (typeof str) {
-            var reg = /^\s*$/;
+            let reg = /^\s*$/;
             rst = reg.test(str);
         }
         return rst;
     },
     convertNumToChinese : function(num, isCurrency) {
         if (!/^\d*(\.\d*)?$/.test(num)) { return "Number is wrong!"; }
-        var AA, BB;
+        let AA, BB;
         if (isCurrency) {
-            AA = new Array("零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖");
-            BB = new Array("", "拾", "佰", "仟", "萬", "億", "点", "");
+            AA = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
+            BB = ["", "拾", "佰", "仟", "萬", "億", "点", ""];
         } else {
             AA = ['零','一','二','三','四','五','六','七','八','九'];
-            BB = new Array("", "十", "百", "千", "万", "亿", "点", "");
+            BB = ["", "十", "百", "千", "万", "亿", "点", ""];
         }
-        //var AA = new Array("零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖");
-        //var BB = new Array("", "拾", "佰", "仟", "萬", "億", "点", "");
-        var a = ("" + num).replace(/(^0*)/g, "").split("."), k = 0, re = "";
-        for (var i = a[0].length - 1; i >= 0; i--) {
+        //let AA = new Array("零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖");
+        //let BB = new Array("", "拾", "佰", "仟", "萬", "億", "点", "");
+        let a = ("" + num).replace(/(^0*)/g, "").split("."), k = 0, re = "";
+        for (let i = a[0].length - 1; i >= 0; i--) {
             switch (k) {
                 case 0: re = BB[7] + re; break;
                 case 4: if (!new RegExp("0{4}\\d{" + (a[0].length - i - 1) + "}$").test(a[0]))
                     re = BB[4] + re; break;
                 case 8: re = BB[5] + re; BB[7] = BB[5]; k = 0; break;
             }
-            if (k % 4 == 2 && a[0].charAt(i + 2) != 0 && a[0].charAt(i + 1) == 0) re = AA[0] + re;
-            if (a[0].charAt(i) != 0) re = AA[a[0].charAt(i)] + BB[k % 4] + re; k++;
+            if (k % 4 === 2 && a[0].charAt(i + 2) !== 0 && a[0].charAt(i + 1) === 0) re = AA[0] + re;
+            if (a[0].charAt(i) !== 0) re = AA[a[0].charAt(i)] + BB[k % 4] + re; k++;
         }
 
         if (a.length > 1) //加上小数部分(如果有小数部分)
         {
             re += BB[6];
-            for (var i = 0; i < a[1].length; i++) re += AA[a[1].charAt(i)];
+            for (let i = 0; i < a[1].length; i++) re += AA[a[1].charAt(i)];
         }
         return re;
     },
     convertStrToBoolean: function(str) {
-        var rst = false, me = this;
+        let rst = false, me = this;
         if (!me.isEmptyString(str)) {
-            var upperStr = str.toUpperCase();
-            if (upperStr == 'T' || upperStr == 'Y' || upperStr == 'YES' || upperStr == 'TRUE') {
+            let upperStr = str.toUpperCase();
+            if (upperStr === 'T' || upperStr === 'Y' || upperStr === 'YES' || upperStr === 'TRUE') {
                 rst = true;
             }
         }
@@ -183,4 +183,4 @@ module.exports = {
     getPinYinCamelChars: function(value) {
         return pinyin.getCamelChars(value);
     }
-}
+};

Diferenças do arquivo suprimidas por serem muito extensas
+ 82 - 0
public/web/PerfectLoad.js


+ 0 - 23
public/web/calculation/calc_util.js

@@ -1,23 +0,0 @@
-/**
- * Created by Tony on 2017/6/19.
- */
-
-class calculation {
-    init(calcTpl, calFee){
-        let me = this;
-        me.calcTpl = calcTpl;
-        me.calFee = calFee;
-        me.hasCompiled = false;
-    };
-
-    compile(){
-        let me = this;
-        me.hasCompiled = false;
-        if (me.calcTpl && me.calcTpl.calcItems && me.calcTpl.calcItems.length > 0) {
-            me.calcTpl.compiledSeq = [];
-        }
-    };
-    calculate(){
-        //
-    }
-}

+ 12 - 3
public/web/number_util.js

@@ -2,9 +2,9 @@
  * Created by chen on 2017/7/5.
  */
 
-Number.prototype.toDecimal = function (ADigit) {
-    return parseFloat(this.toFixed(ADigit));
-};
+// Number.prototype.toDecimal = function (ADigit) {
+//     return parseFloat(this.toFixed(ADigit));
+// };
 
 var  number_util = {
     isNumber : function (obj) {
@@ -24,5 +24,14 @@ var  number_util = {
             value = editingText;
         }
         return value;
+    },
+    roundToString:function(obj,decimal){
+        let value;
+        if(this.isNumber(obj)){
+            value = scMathUtil.roundTo(obj,-decimal)
+        }else {
+            value = scMathUtil.roundTo(Number(obj),-decimal);
+        }
+        return value.toFixed(decimal);
     }
 }

+ 14 - 1
public/web/rpt_value_define.js

@@ -100,7 +100,10 @@ const JV = {
     PROP_DISCRETE_FIELDS: "discrete_field_s",
     PROP_FLOW_FIELDS: "flow_field_s",
     PROP_BILL_FIELDS: "bill_field_s",
-    PROP_GROUP_FIELDS: "group_field_s",
+    PROP_GROUP_FIELDS: "group_field_s", //用来分组的指标(如按清单、定额etc...)
+    PROP_GROUP_LINES: "group_lines",    //显示分组行,因分组的特殊性,分组的数据当成流水数据一样(行高相同),group_lines里的每一条数据占用流水的一整行,里面再细分(指标/text)
+    PROP_GROUP_SUM_KEYS: "SumKey_S",
+    PROP_SUM_KEY: "SumKey",
     PROP_SUM_FIELDS: "sum_field_s",
     PROP_TEXTS: "text_s",
     PROP_TEXT: "text",
@@ -159,6 +162,11 @@ const JV = {
     BLANK_VALUE_INDEX: -100,
     BLANK_PAGE_VALUE_INDEX: -200,
 
+    PROP_SEG_GRP_IDX: "segGrpRecStartIdx",
+    PROP_PRE_ADD_GRP_REC_INFO: "preAddPageGrpInfo",
+    PROP_INSERTED_GRP_REC: "insertedGrpRecAmt",
+    PROP_GRP_LINES: "me.group_lines_amt",
+
     RUN_TYPE_BEFORE_PAGING: "before_paging",
     RUN_TYPE_BEFORE_OUTPUT: "before_output",
 
@@ -224,6 +232,11 @@ const JV = {
     PAGING_OPTION_NORMAL: 'normal',
     PAGING_OPTION_INFINITY: 'infinity',
 
+    DISPLAY_VAL_TYPE_NORMAL: 0,
+    DISPLAY_VAL_TYPE_GROUP: 1,
+
+    TYPE_FOLLOW_MODE: 1,
+
     PAGE_SELF_DEFINE: "自定义",
     PAGE_SPECIAL_MERGE_POS: "page_merge_pos",
 

+ 10 - 1
public/web/scMathUtil.js

@@ -73,7 +73,7 @@ let scMathUtil = {
             if (num === 0){
                 num = 1;
                 let bin1 = bin.substring(0, i);
-                let bin2 = zeroString(iLength - (i + 1));//bin.substring(i + 1, iLength);
+                let bin2 = this.zeroString(iLength - (i + 1));//bin.substring(i + 1, iLength);
                 result = bin1 + num.toString() + bin2;
                 break;
             }
@@ -85,4 +85,13 @@ let scMathUtil = {
         let me = this;
         return me.innerRoundTo(me.binToFloat(me.incMantissa(me.floatToBin(num))), digit);
     }
+};
+
+Number.prototype.toDecimal = function (ADigit) {
+    //return parseFloat(this.toFixed(ADigit));
+    digit = (ADigit && typeof(ADigit) === 'number' && Number.isInteger(ADigit) && ADigit >= 0) ? -ADigit : -2;
+    // var s = scMathUtil.roundTo(this, digit);
+    // console.log('Number: ' + this + '   Digit: ' + digit + '    Result: ' + s);
+    // return parseFloat(s);
+    return scMathUtil.roundTo(this, digit);
 };

+ 21 - 21
public/web/sheet/sheet_common.js

@@ -4,12 +4,12 @@
 var sheetCommonObj = {
     // CSL.2017.06.05
     // createSpread、initSheet 在一个Spread多个Sheet分别调用时的情况下使用。
-    // buildSheet 在一个Spread、一个Sheet的情况下使用。
     createSpread: function(container, SheetCount){
         var me = this;
         var spreadBook = new GC.Spread.Sheets.Workbook(container, { sheetCount: SheetCount });
         spreadBook.options.tabStripVisible = false;
-         spreadBook.options.showHorizontalScrollbar = true;
+        spreadBook.options.showHorizontalScrollbar = true;
+        spreadBook.options.showVerticalScrollbar = true;
         spreadBook.options.allowCopyPasteExcelStyle = false;
         spreadBook.options.allowUserDragDrop = true;
         return spreadBook;
@@ -20,10 +20,24 @@ var sheetCommonObj = {
         var spreadNS = GC.Spread.Sheets;
         sheet.suspendPaint();
         sheet.suspendEvent();
+
         sheet.setRowCount(1, spreadNS.SheetArea.colHeader);
         sheet.setColumnCount(setting.header.length, spreadNS.SheetArea.viewport);
+
+        if (setting && setting.view && setting.view.colHeaderHeight) {
+            sheet.setRowHeight(0, setting.view.colHeaderHeight, spreadNS.SheetArea.colHeader);
+        };
+        if (setting && setting.view && setting.view.rowHeaderWidth) {
+            sheet.setColumnWidth(0, setting.view.rowHeaderWidth, spreadNS.SheetArea.rowHeader);
+        };
+
         sheet.options.colHeaderAutoTextIndex = 1;
         sheet.options.colHeaderAutoText = spreadNS.HeaderAutoText.numbers;
+
+        sheet.options.protectionOptions = {
+            allowResizeColumns: true
+        };
+
         sheet.showRowOutline(false);
         me.buildHeader(sheet, setting);
         if (rowCount > 0) sheet.setRowCount(rowCount);
@@ -31,29 +45,15 @@ var sheetCommonObj = {
         sheet.resumePaint();
     },
 
+    // buildSheet 在一个Spread、一个Sheet的情况下使用。
     buildSheet: function(container, setting, rowCount) {
         var me = this;
-        var spreadBook = new GC.Spread.Sheets.Workbook(container, { sheetCount: 1 });
-        spreadBook.options.tabStripVisible = false;
-        spreadBook.options.showHorizontalScrollbar = true;
-        var spreadNS = GC.Spread.Sheets;
+        var spreadBook = me.createSpread(container, { sheetCount: 1 });
         var sheet = spreadBook.getSheet(0);
-        sheet.suspendPaint();
-        sheet.suspendEvent();
-        //Set rowHeader count and columnHeader count.
-        sheet.setRowCount(1, spreadNS.SheetArea.colHeader);
-        sheet.setColumnCount(setting.header.length, spreadNS.SheetArea.viewport);
-        sheet.options.colHeaderAutoTextIndex = 1;
-        sheet.options.colHeaderAutoText = spreadNS.HeaderAutoText.numbers;
-        sheet.showRowOutline(false);
-        //setup column header
-        me.buildHeader(sheet, setting);
-        //setup cells
-        if (rowCount > 0) sheet.setRowCount(rowCount);
-        sheet.resumeEvent();
-        sheet.resumePaint();
+        me.initSheet(sheet, setting, rowCount);
         return spreadBook;
     },
+
     buildHeader: function(sheet, setting){
         var me = this, ch = GC.Spread.Sheets.SheetArea.colHeader;
         for (var i = 0; i < setting.header.length; i++) {
@@ -194,7 +194,7 @@ var sheetCommonObj = {
             sheet.options.isProtected = true;
             sheet.getRange(-1, 0, -1, setting.header.length, GC.Spread.Sheets.SheetArea.viewport).locked(false);
             for (var i = 0; i < setting.view.lockColumns.length; i++) {
-                sheet.getRange(-1,setting.view.lockColumns[i] , -1, 1, GC.Spread.Sheets.SheetArea.viewport).locked(true);
+                sheet.getRange(-1,setting.view.lockColumns[i], -1, 1, GC.Spread.Sheets.SheetArea.viewport).locked(true);
             }
         }
     },

+ 10 - 0
public/web/sheet/sheet_data_helper.js

@@ -280,5 +280,15 @@ var SheetDataHelper = {
             sheet.setSelection(range.row, range.col, range.rowCount, range.colCount);
         }
         return target;
+    },
+    /**
+     * 在sheet中使用delete键,触发EndEdited事件
+     * @param sheet
+     */
+    deleteBind: function (sheet, fun) {
+        sheet.addKeyMap(46, false, false, false, false, function () {
+            let selections = sheet.getSelections();
+            
+        });
     }
 };

+ 7 - 7
public/web/string_util_light.js

@@ -5,23 +5,23 @@
 
 let stringUtil = {
     isEmptyString: function(str) {
-        var rst = false;
-        if (str == null || str == undefined) {
+        let rst = false;
+        if (str === null || str === undefined) {
             rst = true;
         } else if (typeof str) {
-            var reg = /^\s*$/;
+            let reg = /^\s*$/;
             rst = reg.test(str);
         }
         return rst;
     },
     convertStrToBoolean: function(str) {
-        var rst = false, me = this;
+        let rst = false, me = this;
         if (!me.isEmptyString(str)) {
-            var upperStr = str.toUpperCase();
-            if (upperStr == 'T' || upperStr == 'Y' || upperStr == 'YES' || upperStr == 'TRUE') {
+            let upperStr = str.toUpperCase();
+            if (upperStr === 'T' || upperStr === 'Y' || upperStr === 'YES' || upperStr === 'TRUE') {
                 rst = true;
             }
         }
         return rst;
     }
-}
+};

+ 1 - 1
public/web/tree_table/tree_table.js

@@ -270,7 +270,7 @@
  					} else {
  						parent.children.splice(parent.childIndex(next), 0, node);
  					}
- 				} else if (parent.childIndex(node) !== parent.childIndex(node.next) - 1) {
+ 				} else if (node.nextSibling && parent.childIndex(node) !== parent.childIndex(node.nextSibling) - 1) {
 					parent.children.splice(parent.childIndex(node), 1);
 					parent.children.splice(parent.childIndex(next), 0, node);
 				};

+ 13 - 0
test/calculation/testArrayCalc.js

@@ -0,0 +1,13 @@
+/**
+ * Created by Tony on 2017/10/31.
+ */
+
+var test = require('tape');
+
+test('测试数组运算', function(t){
+    let demo = [1, 2, 3];
+    let rst = demo*3; //failed!
+    console.log(rst);
+    t.pass('just pass');
+    t.end('just end');
+});

+ 337 - 0
test/unit/reports/rpt_cfg.js

@@ -0,0 +1,337 @@
+module.exports = {
+    "fonts" : [
+        {
+            "ID" : "ReportTitle_Main",
+            "CfgDispName" : "主标题",
+            "Name" : "smartSimSun",
+            "FontHeight" : "27",
+            "FontColor" : "BLACK",
+            "FontBold" : "T",
+            "FontItalic" : "F",
+            "FontUnderline" : "F",
+            "FontStrikeOut" : "F",
+            "FontAngle" : "0"
+        },
+        {
+            "ID" : "ReportTitle_Vice_1",
+            "CfgDispName" : "副标题",
+            "Name" : "smartSimSun",
+            "FontHeight" : "20",
+            "FontColor" : "BLACK",
+            "FontBold" : "T",
+            "FontItalic" : "F",
+            "FontUnderline" : "F",
+            "FontStrikeOut" : "F",
+            "FontAngle" : "0"
+        },
+        {
+            "ID" : "HeaderColumn",
+            "CfgDispName" : "栏头",
+            "Name" : "smartSimSun",
+            "FontHeight" : "12",
+            "FontColor" : "BLACK",
+            "FontBold" : "F",
+            "FontItalic" : "F",
+            "FontUnderline" : "F",
+            "FontStrikeOut" : "F",
+            "FontAngle" : "0"
+        },
+        {
+            "ID" : "Header",
+            "CfgDispName" : "表头",
+            "Name" : "smartSimSun",
+            "FontHeight" : "12",
+            "FontColor" : "BLACK",
+            "FontBold" : "F",
+            "FontItalic" : "F",
+            "FontUnderline" : "F",
+            "FontStrikeOut" : "F",
+            "FontAngle" : "0"
+        },
+        {
+            "ID" : "FooterColumn",
+            "CfgDispName" : "栏尾",
+            "Name" : "smartSimSun",
+            "FontHeight" : "12",
+            "FontColor" : "BLACK",
+            "FontBold" : "F",
+            "FontItalic" : "F",
+            "FontUnderline" : "F",
+            "FontStrikeOut" : "F",
+            "FontAngle" : "0"
+        },
+        {
+            "ID" : "Footer",
+            "CfgDispName" : "表尾",
+            "Name" : "smartSimSun",
+            "FontHeight" : "12",
+            "FontColor" : "BLACK",
+            "FontBold" : "F",
+            "FontItalic" : "F",
+            "FontUnderline" : "F",
+            "FontStrikeOut" : "F",
+            "FontAngle" : "0"
+        },
+        {
+            "ID" : "GramdTotal",
+            "CfgDispName" : "总合计",
+            "Name" : "smartSimSun",
+            "FontHeight" : "12",
+            "FontColor" : "BLACK",
+            "FontBold" : "F",
+            "FontItalic" : "F",
+            "FontUnderline" : "F",
+            "FontStrikeOut" : "F",
+            "FontAngle" : "0"
+        },
+        {
+            "ID" : "SectionTotal",
+            "CfgDispName" : "章合计",
+            "Name" : "smartSimSun",
+            "FontHeight" : "12",
+            "FontColor" : "BLACK",
+            "FontBold" : "F",
+            "FontItalic" : "F",
+            "FontUnderline" : "F",
+            "FontStrikeOut" : "F",
+            "FontAngle" : "0"
+        },
+        {
+            "ID" : "Content",
+            "CfgDispName" : "正文内容",
+            "Name" : "smartSimSun",
+            "FontHeight" : "12",
+            "FontColor" : "BLACK",
+            "FontBold" : "F",
+            "FontItalic" : "F",
+            "FontUnderline" : "F",
+            "FontStrikeOut" : "F",
+            "FontAngle" : "0"
+        },
+        {
+            "ID" : "Header_V1",
+            "Name" : "smartSimSun",
+            "FontHeight" : "12",
+            "FontColor" : "BLACK",
+            "FontBold" : "F",
+            "FontItalic" : "F",
+            "FontUnderline" : "F",
+            "FontStrikeOut" : "F",
+            "FontAngle" : "90"
+        },
+        {
+            "ID" : "Header_V2",
+            "Name" : "smartSimSun",
+            "FontHeight" : "12",
+            "FontColor" : "BLACK",
+            "FontBold" : "F",
+            "FontItalic" : "F",
+            "FontUnderline" : "F",
+            "FontStrikeOut" : "F",
+            "FontAngle" : "-90"
+        }
+    ],
+    "styles" : [
+        {
+            "ID" : "Default_None",
+            "CfgDispName" : "空白",
+            "border_style" : []
+        },
+        {
+            "ID" : "Default",
+            "CfgDispName" : "默认",
+            "border_style" : []
+        },
+        {
+            "ID" : "Default_Normal",
+            "CfgDispName" : "正常",
+            "border_style" : [
+                {
+                    "Position" : "Left",
+                    "LineWeight" : "1",
+                    "DashStyle" : "SOLID",
+                    "Color" : "BLACK"
+                },
+                {
+                    "Position" : "Right",
+                    "LineWeight" : "1",
+                    "DashStyle" : "SOLID",
+                    "Color" : "BLACK"
+                },
+                {
+                    "Position" : "Top",
+                    "LineWeight" : "1",
+                    "DashStyle" : "SOLID",
+                    "Color" : "BLACK"
+                },
+                {
+                    "Position" : "Bottom",
+                    "LineWeight" : "1",
+                    "DashStyle" : "SOLID",
+                    "Color" : "BLACK"
+                }
+            ]
+        },
+        {
+            "ID" : "Label_Underline",
+            "CfgDispName" : "字符底线",
+            "border_style" : [
+                {
+                    "Position" : "Left",
+                    "LineWeight" : "0",
+                    "DashStyle" : "SOLID",
+                    "Color" : "BLACK"
+                },
+                {
+                    "Position" : "Right",
+                    "LineWeight" : "0",
+                    "DashStyle" : "SOLID",
+                    "Color" : "BLACK"
+                },
+                {
+                    "Position" : "Top",
+                    "LineWeight" : "0",
+                    "DashStyle" : "SOLID",
+                    "Color" : "BLACK"
+                },
+                {
+                    "Position" : "Bottom",
+                    "LineWeight" : "1",
+                    "DashStyle" : "SOLID",
+                    "Color" : "BLACK"
+                }
+            ]
+        },
+        {
+            "ID" : "BORDER_ALL_AROUND",
+            "CfgDispName" : "报表边框",
+            "border_style" : [
+                {
+                    "Position" : "Left",
+                    "LineWeight" : "0.0",
+                    "DashStyle" : "SOLID",
+                    "Color" : "BLACK"
+                },
+                {
+                    "Position" : "Right",
+                    "LineWeight" : "0.0",
+                    "DashStyle" : "SOLID",
+                    "Color" : "BLACK"
+                },
+                {
+                    "Position" : "Top",
+                    "LineWeight" : "2.0",
+                    "DashStyle" : "SOLID",
+                    "Color" : "BLACK"
+                },
+                {
+                    "Position" : "Bottom",
+                    "LineWeight" : "2.0",
+                    "DashStyle" : "SOLID",
+                    "Color" : "BLACK"
+                }
+            ]
+        }
+    ],
+    "ctrls" : [
+        {
+            "ID" : "Default",
+            "CfgDispName" : "默认",
+            "Shrink" : "T",
+            "ShowZero" : "T",
+            "Horizon" : "left",
+            "Vertical" : "bottom",
+            "Wrap" : "false"
+        },
+        {
+            "ID" : "Title",
+            "CfgDispName" : "标题",
+            "Shrink" : "T",
+            "ShowZero" : "T",
+            "Horizon" : "center",
+            "Vertical" : "center",
+            "Wrap" : "false"
+        },
+        {
+            "ID" : "Header",
+            "CfgDispName" : "表头",
+            "Shrink" : "T",
+            "ShowZero" : "T",
+            "Horizon" : "left",
+            "Vertical" : "center",
+            "Wrap" : "false"
+        },
+        {
+            "ID" : "Footer",
+            "CfgDispName" : "表尾",
+            "Shrink" : "T",
+            "ShowZero" : "T",
+            "Horizon" : "left",
+            "Vertical" : "center",
+            "Wrap" : "false"
+        },
+        {
+            "ID" : "Column",
+            "CfgDispName" : "表栏",
+            "Shrink" : "T",
+            "ShowZero" : "T",
+            "Horizon" : "center",
+            "Vertical" : "center",
+            "Wrap" : "false",
+            "FillAfterWrap" : "true"
+        },
+        {
+            "ID" : "Column_Left",
+            "CfgDispName" : "表栏_左",
+            "Shrink" : "T",
+            "ShowZero" : "T",
+            "Horizon" : "left",
+            "Vertical" : "center",
+            "Wrap" : "false"
+        },
+        {
+            "ID" : "Column_Right",
+            "CfgDispName" : "表栏_右",
+            "Shrink" : "T",
+            "ShowZero" : "T",
+            "Horizon" : "right",
+            "Vertical" : "center",
+            "Wrap" : "false"
+        },
+        {
+            "ID" : "Content_Left",
+            "CfgDispName" : "正文内容",
+            "Shrink" : "T",
+            "ShowZero" : "F",
+            "Horizon" : "left",
+            "Vertical" : "bottom",
+            "Wrap" : "false"
+        },
+        {
+            "ID" : "Content_Right",
+            "CfgDispName" : "正文内容_右",
+            "Shrink" : "T",
+            "ShowZero" : "F",
+            "Horizon" : "right",
+            "Vertical" : "bottom",
+            "Wrap" : "false"
+        },
+        {
+            "ID" : "Content_Center",
+            "CfgDispName" : "正文内容_中",
+            "Shrink" : "T",
+            "ShowZero" : "F",
+            "Horizon" : "center",
+            "Vertical" : "bottom",
+            "Wrap" : "false"
+        },
+        {
+            "ID" : "Numeric",
+            "Shrink" : "T",
+            "ShowZero" : "F",
+            "Horizon" : "right",
+            "Vertical" : "bottom",
+            "Wrap" : "false"
+        }
+    ]
+}

+ 16 - 0
test/unit/reports/rpt_test_decimal.js

@@ -0,0 +1,16 @@
+/**
+ * Created by Tony on 2017/11/8.
+ */
+
+import mongoose from "mongoose";
+let Schema = mongoose.Schema;
+let MyDecimalSchema = new Schema({
+    "ID": Number,
+    "Value1" : Number,
+    "Value2": Number,
+    "Value3": Number
+});
+
+let Rpt_Decimal_Mdl = mongoose.model("rpt_decimal_test", MyDecimalSchema, "rpt_decimal_test");
+
+export {Rpt_Decimal_Mdl as default};

+ 65 - 18
test/unit/reports/test_tpl_09_1.js

@@ -38,8 +38,13 @@ let projectDataMdl = require('../../../modules/main/models/project');
 let demoPrjId = - 1;
 let demoRptId = 226, pagesize = "A4";
 
-demoPrjId = 610; //QA: 建筑工程
 let userId_Leng = 1142; //小冷User Id
+demoPrjId = 720; //QA: DW3
+/*/
+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");
@@ -73,8 +78,10 @@ test('测试 - 获取project部分数据: ', function (t) {
     filter.push(projectConsts.BILLS);
     filter.push(projectConsts.RATION);
     filter.push(projectConsts.RATION_GLJ);
+    filter.push(projectConsts.PROJECTGLJ);
     filter.push(projectConsts.FEERATE);
-    prjMdl.project.getUserProject(userId_Leng, demoPrjId, function(err, msg, prjObj){
+    filter.push(projectConsts.CALC_PROGRAM);
+    prjMdl.project.getUserProject(userId_Dft, demoPrjId, function(err, msg, prjObj){
         if (!err) {
             projectDataMdl.getFilterData(demoPrjId, filter, function (results) {
                 if (results) {
@@ -109,23 +116,29 @@ test('测试 - 测试模板啦: ', function (t) {
         let filter = rptDataUtil.getDataRequestFilter();
         console.log(filter);
         //正常应该根据报表模板定义的数据类型来请求数据
-        rptTplDataFacade.prepareProjectData(userId_Leng, demoPrjId, filter, function (err, msg, rawDataObj) {
+        rptTplDataFacade.prepareProjectData(userId_Dft, demoPrjId, filter, function (err, msg, rawDataObj) {
             if (!err) {
-                let tplData = rptDataUtil.assembleData(rawDataObj);
-                // fsUtil.wirteObjToFile(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;
-                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.wirteObjToFile(pageRst, "D:/GitHome/ConstructionCost/tmp/testBuiltPageResult.js");
-                } else {
-                    console.log("oh! no pages were created!")
+                try {
+                    let tplData = rptDataUtil.assembleData(rawDataObj);
+                    // fsUtil.wirteObjToFile(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;
+                    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.wirteObjToFile(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!');
@@ -140,6 +153,40 @@ test('测试 - 测试模板啦: ', function (t) {
 });
 //*/
 
+/*/
+test('测试 - 保存小数位数问题: ', function (t) {
+    require('./rpt_test_decimal');
+    let rpt_decimal_mdl = mongoose.model("rpt_decimal_test");
+    let num = 300000;
+    let ID = 1;
+    for (let i = 0.0001; i < 1; (i+=0.0001)) {
+        let test_doc = {};
+        test_doc.ID = ID;
+        test_doc.Value1 = num + i;
+        test_doc.Value2 = num + i + 0.1 + 0.2;
+        test_doc.Value3 = parseFloat((num + i + 0.1 + 0.2).toFixed(5));
+        rpt_decimal_mdl.create(test_doc);
+        ID++;
+    }
+    t.pass('pass save decimal ok!');
+    t.end();
+});
+//*/
+/*/
+test('测试 - 显示保存小数位数问题: ', function (t) {
+    require('./rpt_test_decimal');
+    let rpt_decimal_mdl = mongoose.model("rpt_decimal_test");
+    rpt_decimal_mdl.find({}).then(function (rst) {
+        //console.log(rst);
+        if (rst.length > 0) {
+            fsUtil.wirteObjToFile(rst, "D:/GitHome/ConstructionCost/tmp/testDecimalResult.js");
+        }
+        t.pass('pass get decimal ok!');
+        t.end();
+    });
+});
+//*/
+
 test('close the connection', function (t) {
     setTimeout(function () {
         mongoose.disconnect();

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 18062
tmp/07_1.page.js


+ 0 - 14
tmp/excel_test_raw_data/08-2/[Content_Types].xml

@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
-	<Override PartName="/xl/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>
-	<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
-	<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
-	<Default Extension="xml" ContentType="application/xml"/>
-	<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
-	<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
-	<Override PartName="/xl/worksheets/sheet2.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
-	<Override PartName="/xl/worksheets/sheet3.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
-	<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
-	<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
-	<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
-</Types>

+ 0 - 6
tmp/excel_test_raw_data/08-2/_rels/.rels

@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
-	<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
-	<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
-	<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
-</Relationships>

+ 0 - 26
tmp/excel_test_raw_data/08-2/docProps/app.xml

@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
-	<Application>Microsoft Excel</Application>
-	<DocSecurity>0</DocSecurity>
-	<ScaleCrop>false</ScaleCrop>
-	<HeadingPairs>
-		<vt:vector size="2" baseType="variant">
-			<vt:variant>
-				<vt:lpstr>工作表</vt:lpstr>
-			</vt:variant>
-			<vt:variant>
-				<vt:i4>1</vt:i4>
-			</vt:variant>
-		</vt:vector>
-	</HeadingPairs>
-	<TitlesOfParts>
-		<vt:vector size="1" baseType="lpstr">
-			<vt:lpstr>第1页</vt:lpstr>
-		</vt:vector>
-	</TitlesOfParts>
-	<Company>OOCL</Company>
-	<LinksUpToDate>false</LinksUpToDate>
-	<SharedDoc>false</SharedDoc>
-	<HyperlinksChanged>false</HyperlinksChanged>
-	<AppVersion>12.0000</AppVersion>
-</Properties>

+ 0 - 7
tmp/excel_test_raw_data/08-2/docProps/core.xml

@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-	<dc:creator>Tony</dc:creator>
-	<cp:lastModifiedBy>Tony</cp:lastModifiedBy>
-	<dcterms:created xsi:type="dcterms:W3CDTF">2011-05-14T09:11:44Z</dcterms:created>
-	<dcterms:modified xsi:type="dcterms:W3CDTF">2017-03-31T02:23:46Z</dcterms:modified>
-</cp:coreProperties>

+ 0 - 7
tmp/excel_test_raw_data/08-2/xl/_rels/workbook.xml.rels

@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
-    <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml" />
-    <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml" />
-    <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml" />
-    <Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml" />
-</Relationships>

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 17365
tmp/excel_test_raw_data/08-2/xl/sharedStrings.xml


+ 0 - 205
tmp/excel_test_raw_data/08-2/xl/styles.xml

@@ -1,205 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
-	<fonts count="6">
-		<font>
-			<sz val="11"/>
-			<color theme="1"/>
-			<name val="宋体"/>
-			<charset val="134"/>
-			<scheme val="minor"/>
-		</font>
-		<font>
-			<sz val="9"/>
-			<name val="宋体"/>
-			<charset val="134"/>
-		</font>
-		<font>
-			<b/>
-			<sz val="20"/>
-			<color indexed="8"/>
-			<name val="smartSimSun"/>
-			<charset val="134"/>
-		</font>
-		<font>
-			<sz val="9"/>
-			<color indexed="8"/>
-			<name val="smartSimSun"/>
-			<charset val="134"/>
-		</font>
-		<font>
-			<sz val="9"/>
-			<color indexed="8"/>
-			<name val="宋体"/>
-			<charset val="134"/>
-		</font>
-		<font>
-			<sz val="12"/>
-			<color indexed="8"/>
-			<name val="宋体"/>
-			<charset val="134"/>
-		</font>
-	</fonts>
-	<fills count="2">
-		<fill>
-			<patternFill patternType="none"/>
-		</fill>
-		<fill>
-			<patternFill patternType="gray125"/>
-		</fill>
-	</fills>
-	<borders count="6">
-		<border>
-			<left/>
-			<right/>
-			<top/>
-			<bottom/>
-			<diagonal/>
-		</border>
-		<border>
-			<left/>
-			<right style="thin">
-				<color indexed="8"/>
-			</right>
-			<top style="thin">
-				<color indexed="8"/>
-			</top>
-			<bottom style="thin">
-				<color indexed="8"/>
-			</bottom>
-			<diagonal/>
-		</border>
-		<border>
-			<left style="thin">
-				<color indexed="8"/>
-			</left>
-			<right style="thin">
-				<color indexed="8"/>
-			</right>
-			<top style="thin">
-				<color indexed="8"/>
-			</top>
-			<bottom style="thin">
-				<color indexed="8"/>
-			</bottom>
-			<diagonal/>
-		</border>
-		<border>
-			<left style="thin">
-				<color indexed="8"/>
-			</left>
-			<right/>
-			<top style="thin">
-				<color indexed="8"/>
-			</top>
-			<bottom style="thin">
-				<color indexed="8"/>
-			</bottom>
-			<diagonal/>
-		</border>
-		<border>
-			<left style="thin">
-				<color indexed="8"/>
-			</left>
-			<right/>
-			<top style="thin">
-				<color indexed="8"/>
-			</top>
-			<bottom/>
-			<diagonal/>
-		</border>
-		<border>
-			<left style="thin">
-				<color indexed="8"/>
-			</left>
-			<right/>
-			<top/>
-			<bottom/>
-			<diagonal/>
-		</border>
-	</borders>
-	<cellStyleXfs count="1">
-		<xf numFmtId="0" fontId="0" fillId="0" borderId="0">
-			<alignment vertical="center"/>
-		</xf>
-	</cellStyleXfs>
-	<cellXfs count="19">
-		<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0">
-			<alignment vertical="center"/>
-		</xf>
-		<xf numFmtId="0" fontId="2" fillId="0" borderId="0" xfId="0" applyFont="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="center" vertical="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="0" xfId="0" applyFont="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="left" vertical="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="1" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="center" vertical="center" wrapText="1" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="2" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="center" vertical="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="2" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="left" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="2" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="2" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="right" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="3" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="center" vertical="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="4" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="center" vertical="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="5" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="center" vertical="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="1" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="3" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="right" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="4" fillId="0" borderId="0" xfId="0" applyFont="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="left" vertical="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="1" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="center" vertical="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="5" fillId="0" borderId="2" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="center" vertical="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="5" fillId="0" borderId="3" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="center" vertical="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="2" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="right" vertical="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-		<xf numFmtId="0" fontId="3" fillId="0" borderId="3" xfId="0" applyFont="1" applyBorder="1" applyAlignment="1" applyProtection="1">
-			<alignment horizontal="right" vertical="center" shrinkToFit="1"/>
-			<protection locked="0"/>
-		</xf>
-	</cellXfs>
-	<cellStyles count="1"><cellStyle name="常规" xfId="0" builtinId="0"/></cellStyles>
-	<dxfs count="0"/>
-	<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleLight16"/>
-</styleSheet>

+ 0 - 281
tmp/excel_test_raw_data/08-2/xl/theme/theme1.xml

@@ -1,281 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office 主题">
-    <a:themeElements>
-        <a:clrScheme name="Office">
-            <a:dk1>
-                <a:sysClr val="windowText" lastClr="000000" />
-            </a:dk1>
-            <a:lt1>
-                <a:sysClr val="window" lastClr="FFFFFF" />
-            </a:lt1>
-            <a:dk2>
-                <a:srgbClr val="1F497D" />
-            </a:dk2>
-            <a:lt2>
-                <a:srgbClr val="EEECE1" />
-            </a:lt2>
-            <a:accent1>
-                <a:srgbClr val="4F81BD" />
-            </a:accent1>
-            <a:accent2>
-                <a:srgbClr val="C0504D" />
-            </a:accent2>
-            <a:accent3>
-                <a:srgbClr val="9BBB59" />
-            </a:accent3>
-            <a:accent4>
-                <a:srgbClr val="8064A2" />
-            </a:accent4>
-            <a:accent5>
-                <a:srgbClr val="4BACC6" />
-            </a:accent5>
-            <a:accent6>
-                <a:srgbClr val="F79646" />
-            </a:accent6>
-            <a:hlink>
-                <a:srgbClr val="0000FF" />
-            </a:hlink>
-            <a:folHlink>
-                <a:srgbClr val="800080" />
-            </a:folHlink>
-        </a:clrScheme>
-        <a:fontScheme name="Office">
-            <a:majorFont>
-                <a:latin typeface="Cambria" />
-                <a:ea typeface="" />
-                <a:cs typeface="" />
-                <a:font script="Jpan" typeface="MS Pゴシック" />
-                <a:font script="Hang" typeface="맑은 고딕" />
-                <a:font script="Hans" typeface="宋体" />
-                <a:font script="Hant" typeface="新細明體" />
-                <a:font script="Arab" typeface="Times New Roman" />
-                <a:font script="Hebr" typeface="Times New Roman" />
-                <a:font script="Thai" typeface="Tahoma" />
-                <a:font script="Ethi" typeface="Nyala" />
-                <a:font script="Beng" typeface="Vrinda" />
-                <a:font script="Gujr" typeface="Shruti" />
-                <a:font script="Khmr" typeface="MoolBoran" />
-                <a:font script="Knda" typeface="Tunga" />
-                <a:font script="Guru" typeface="Raavi" />
-                <a:font script="Cans" typeface="Euphemia" />
-                <a:font script="Cher" typeface="Plantagenet Cherokee" />
-                <a:font script="Yiii" typeface="Microsoft Yi Baiti" />
-                <a:font script="Tibt" typeface="Microsoft Himalaya" />
-                <a:font script="Thaa" typeface="MV Boli" />
-                <a:font script="Deva" typeface="Mangal" />
-                <a:font script="Telu" typeface="Gautami" />
-                <a:font script="Taml" typeface="Latha" />
-                <a:font script="Syrc" typeface="Estrangelo Edessa" />
-                <a:font script="Orya" typeface="Kalinga" />
-                <a:font script="Mlym" typeface="Kartika" />
-                <a:font script="Laoo" typeface="DokChampa" />
-                <a:font script="Sinh" typeface="Iskoola Pota" />
-                <a:font script="Mong" typeface="Mongolian Baiti" />
-                <a:font script="Viet" typeface="Times New Roman" />
-                <a:font script="Uigh" typeface="Microsoft Uighur" />
-            </a:majorFont>
-            <a:minorFont>
-                <a:latin typeface="Calibri" />
-                <a:ea typeface="" />
-                <a:cs typeface="" />
-                <a:font script="Jpan" typeface="MS Pゴシック" />
-                <a:font script="Hang" typeface="맑은 고딕" />
-                <a:font script="Hans" typeface="宋体" />
-                <a:font script="Hant" typeface="新細明體" />
-                <a:font script="Arab" typeface="Arial" />
-                <a:font script="Hebr" typeface="Arial" />
-                <a:font script="Thai" typeface="Tahoma" />
-                <a:font script="Ethi" typeface="Nyala" />
-                <a:font script="Beng" typeface="Vrinda" />
-                <a:font script="Gujr" typeface="Shruti" />
-                <a:font script="Khmr" typeface="DaunPenh" />
-                <a:font script="Knda" typeface="Tunga" />
-                <a:font script="Guru" typeface="Raavi" />
-                <a:font script="Cans" typeface="Euphemia" />
-                <a:font script="Cher" typeface="Plantagenet Cherokee" />
-                <a:font script="Yiii" typeface="Microsoft Yi Baiti" />
-                <a:font script="Tibt" typeface="Microsoft Himalaya" />
-                <a:font script="Thaa" typeface="MV Boli" />
-                <a:font script="Deva" typeface="Mangal" />
-                <a:font script="Telu" typeface="Gautami" />
-                <a:font script="Taml" typeface="Latha" />
-                <a:font script="Syrc" typeface="Estrangelo Edessa" />
-                <a:font script="Orya" typeface="Kalinga" />
-                <a:font script="Mlym" typeface="Kartika" />
-                <a:font script="Laoo" typeface="DokChampa" />
-                <a:font script="Sinh" typeface="Iskoola Pota" />
-                <a:font script="Mong" typeface="Mongolian Baiti" />
-                <a:font script="Viet" typeface="Arial" />
-                <a:font script="Uigh" typeface="Microsoft Uighur" />
-            </a:minorFont>
-        </a:fontScheme>
-        <a:fmtScheme name="Office">
-            <a:fillStyleLst>
-                <a:solidFill>
-                    <a:schemeClr val="phClr" />
-                </a:solidFill>
-                <a:gradFill rotWithShape="1">
-                    <a:gsLst>
-                        <a:gs pos="0">
-                            <a:schemeClr val="phClr">
-                                <a:tint val="50000" />
-                                <a:satMod val="300000" />
-                            </a:schemeClr>
-                        </a:gs>
-                        <a:gs pos="35000">
-                            <a:schemeClr val="phClr">
-                                <a:tint val="37000" />
-                                <a:satMod val="300000" />
-                            </a:schemeClr>
-                        </a:gs>
-                        <a:gs pos="100000">
-                            <a:schemeClr val="phClr">
-                                <a:tint val="15000" />
-                                <a:satMod val="350000" />
-                            </a:schemeClr>
-                        </a:gs>
-                    </a:gsLst>
-                    <a:lin ang="16200000" scaled="1" />
-                </a:gradFill>
-                <a:gradFill rotWithShape="1">
-                    <a:gsLst>
-                        <a:gs pos="0">
-                            <a:schemeClr val="phClr">
-                                <a:shade val="51000" />
-                                <a:satMod val="130000" />
-                            </a:schemeClr>
-                        </a:gs>
-                        <a:gs pos="80000">
-                            <a:schemeClr val="phClr">
-                                <a:shade val="93000" />
-                                <a:satMod val="130000" />
-                            </a:schemeClr>
-                        </a:gs>
-                        <a:gs pos="100000">
-                            <a:schemeClr val="phClr">
-                                <a:shade val="94000" />
-                                <a:satMod val="135000" />
-                            </a:schemeClr>
-                        </a:gs>
-                    </a:gsLst>
-                    <a:lin ang="16200000" scaled="0" />
-                </a:gradFill>
-            </a:fillStyleLst>
-            <a:lnStyleLst>
-                <a:ln w="9525" cap="flat" cmpd="sng" algn="ctr">
-                    <a:solidFill>
-                        <a:schemeClr val="phClr">
-                            <a:shade val="95000" />
-                            <a:satMod val="105000" />
-                        </a:schemeClr>
-                    </a:solidFill>
-                    <a:prstDash val="solid" />
-                </a:ln>
-                <a:ln w="25400" cap="flat" cmpd="sng" algn="ctr">
-                    <a:solidFill>
-                        <a:schemeClr val="phClr" />
-                    </a:solidFill>
-                    <a:prstDash val="solid" />
-                </a:ln>
-                <a:ln w="38100" cap="flat" cmpd="sng" algn="ctr">
-                    <a:solidFill>
-                        <a:schemeClr val="phClr" />
-                    </a:solidFill>
-                    <a:prstDash val="solid" />
-                </a:ln>
-            </a:lnStyleLst>
-            <a:effectStyleLst>
-                <a:effectStyle>
-                    <a:effectLst>
-                        <a:outerShdw blurRad="40000" dist="20000" dir="5400000" rotWithShape="0">
-                            <a:srgbClr val="000000">
-                                <a:alpha val="38000" />
-                            </a:srgbClr>
-                        </a:outerShdw>
-                    </a:effectLst>
-                </a:effectStyle>
-                <a:effectStyle>
-                    <a:effectLst>
-                        <a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0">
-                            <a:srgbClr val="000000">
-                                <a:alpha val="35000" />
-                            </a:srgbClr>
-                        </a:outerShdw>
-                    </a:effectLst>
-                </a:effectStyle>
-                <a:effectStyle>
-                    <a:effectLst>
-                        <a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0">
-                            <a:srgbClr val="000000">
-                                <a:alpha val="35000" />
-                            </a:srgbClr>
-                        </a:outerShdw>
-                    </a:effectLst>
-                    <a:scene3d>
-                        <a:camera prst="orthographicFront">
-                            <a:rot lat="0" lon="0" rev="0" />
-                        </a:camera>
-                        <a:lightRig rig="threePt" dir="t">
-                            <a:rot lat="0" lon="0" rev="1200000" />
-                        </a:lightRig>
-                    </a:scene3d>
-                    <a:sp3d>
-                        <a:bevelT w="63500" h="25400" />
-                    </a:sp3d>
-                </a:effectStyle>
-            </a:effectStyleLst>
-            <a:bgFillStyleLst>
-                <a:solidFill>
-                    <a:schemeClr val="phClr" />
-                </a:solidFill>
-                <a:gradFill rotWithShape="1">
-                    <a:gsLst>
-                        <a:gs pos="0">
-                            <a:schemeClr val="phClr">
-                                <a:tint val="40000" />
-                                <a:satMod val="350000" />
-                            </a:schemeClr>
-                        </a:gs>
-                        <a:gs pos="40000">
-                            <a:schemeClr val="phClr">
-                                <a:tint val="45000" />
-                                <a:shade val="99000" />
-                                <a:satMod val="350000" />
-                            </a:schemeClr>
-                        </a:gs>
-                        <a:gs pos="100000">
-                            <a:schemeClr val="phClr">
-                                <a:shade val="20000" />
-                                <a:satMod val="255000" />
-                            </a:schemeClr>
-                        </a:gs>
-                    </a:gsLst>
-                    <a:path path="circle">
-                        <a:fillToRect l="50000" t="-80000" r="50000" b="180000" />
-                    </a:path>
-                </a:gradFill>
-                <a:gradFill rotWithShape="1">
-                    <a:gsLst>
-                        <a:gs pos="0">
-                            <a:schemeClr val="phClr">
-                                <a:tint val="80000" />
-                                <a:satMod val="300000" />
-                            </a:schemeClr>
-                        </a:gs>
-                        <a:gs pos="100000">
-                            <a:schemeClr val="phClr">
-                                <a:shade val="30000" />
-                                <a:satMod val="200000" />
-                            </a:schemeClr>
-                        </a:gs>
-                    </a:gsLst>
-                    <a:path path="circle">
-                        <a:fillToRect l="50000" t="50000" r="50000" b="50000" />
-                    </a:path>
-                </a:gradFill>
-            </a:bgFillStyleLst>
-        </a:fmtScheme>
-    </a:themeElements>
-    <a:objectDefaults />
-    <a:extraClrSchemeLst />
-</a:theme>

+ 0 - 12
tmp/excel_test_raw_data/08-2/xl/workbook.xml

@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
-	<fileVersion appName="xl" lastEdited="4" lowestEdited="4" rupBuild="4505"/>
-	<workbookPr defaultThemeVersion="124226"/>
-	<bookViews>
-		<workbookView xWindow="360" yWindow="345" windowWidth="14655" windowHeight="4305"/>
-	</bookViews>
-	<sheets>
-		<sheet name="第1页" sheetId="1" r:id="rId1"/>
-	</sheets>
-	<calcPr calcId="124519"/>
-</workbook>

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 151683
tmp/excel_test_raw_data/08-2/xl/worksheets/sheet1.xml


+ 1 - 0
web/building_saas/complementary_glj_lib/html/tools-gongliaoji.html

@@ -188,6 +188,7 @@
     <script type="text/javascript" src="/lib/ztree/jquery.ztree.core.js"></script>
     <script type="text/javascript" src="/lib/ztree/jquery.ztree.excheck.js"></script>
     <script type="text/javascript" src="/lib/ztree/jquery.ztree.exedit.js"></script>
+    <script type="text/javascript" src="/public/web/common_ajax.js"></script>
     <script type="text/javascript" src="/public/web/treeDataHelper.js"></script>
     <script type="text/javascript" src="/public/web/QueryParam.js"></script>
     <script type="text/javascript" src="/web/building_saas/complementary_glj_lib/js/glj.js"></script>

+ 89 - 97
web/building_saas/complementary_glj_lib/js/glj.js

@@ -92,75 +92,46 @@ let repositoryGljObj = {
     },
     getGljDistType: function (callback) {
         let me = this;
-        $.ajax({
-            type: 'post',
-            url: "complementartGlj/api/getGljDistType",
-            dataType: 'json',
-            success: function (result) {
-                if(!result.error && callback){
-                    me.distTypeTree = me.getComboData(result.data);
-                    console.log(me.distTypeTree);
-                    callback();
-                }
+        CommonAjax.post('complementartGlj/api/getGljDistType', {}, function (rstData) {
+            if(callback){
+                me.distTypeTree = me.getComboData(rstData);
+                console.log(me.distTypeTree);
+                callback();
             }
-        })
+        });
     },
     getGljTree: function(gljLibId, callback) {
         let me = this;
-        $.ajax({
-            type:"POST",
-            url:"complementartGlj/api/getGljTree",
-            data:{"gljLibId": gljLibId},
-            dataType:"json",
-            cache:false,
-            timeout:20000,
-            success:function(result,textStatus,status){
-                if(status.status == 200) {
-                    zTreeHelper.createTree(result.data, gljSetting, "repositoryTree", me);
-                    zTreeHelper.createTree(result.data, componentSetting, "componentTree", componentOprObj);
-                    if (result.data && result.data.length > 0) {
-                        me.gljCurTypeId = result.data[0].ID;
-                    } else {
-                        //重新创建库?
-                        gljTypeTreeOprObj.addRootNode();
-                    }
-                    callback();
-                }
-            },
-            error:function(err){
-                alert(err.responseJSON.error);
+        CommonAjax.post('complementartGlj/api/getGljTree', {gljLibId: gljLibId}, function (rstData) {
+            zTreeHelper.createTree(rstData, gljSetting, "repositoryTree", me);
+            zTreeHelper.createTree(rstData, componentSetting, "componentTree", componentOprObj);
+            if (rstData && rstData.length > 0) {
+                me.gljCurTypeId = rstData[0].ID;
+            } else {
+                //重新创建库?
+               // gljTypeTreeOprObj.addRootNode();
+            }
+            if(callback){
+                callback();
             }
-        })
+        });
     },
     getGljItems: function(stdGljLibId, userId, compilationId) {
         let me = this;
-        $.ajax({
-            type:"POST",
-            url:"complementartGlj/api/getGljItems",
-            data:{stdGljLibId: stdGljLibId, userId: userId, compilationId: compilationId},
-            dataType:"json",
-            cache:false,
-            timeout:5000,
-            success:function(result){
-                if(!result.error) {
-                    me.stdGljList = result.data.stdGljs;
-                    me.complementaryGljList = result.data.complementaryGljs;
-                    me.workBook.getSheet(0).setRowCount(me.stdGljList.length);
-                    me.sortGlj(me.stdGljList);
-                    me.setProp('isStd', true, me.stdGljList);
-                    me.sortGlj(me.complementaryGljList);
-                    let rootNode = me.treeObj.getNodes()[0];
-                    if(rootNode && rootNode.isParent && rootNode.isFirstNode){
-                        componentOprObj.rootNode = rootNode;
-                        me.treeObj.selectNode(rootNode);
-                        gljTypeTreeOprObj.onClick(null, 'repositoryTree', rootNode);
-                    }
-                }
-            },
-            error:function(err){
-                alert(err.responseJSON.error);
+        CommonAjax.post('complementartGlj/api/getGljItems', {stdGljLibId: stdGljLibId, userId: userId, compilationId: compilationId}, function (rstData) {
+            me.stdGljList = rstData.stdGljs;
+            me.complementaryGljList = rstData.complementaryGljs;
+            me.workBook.getSheet(0).setRowCount(me.stdGljList.length);
+            me.sortGlj(me.stdGljList);
+            me.setProp('isStd', true, me.stdGljList);
+            me.sortGlj(me.complementaryGljList);
+            let rootNode = me.treeObj.getNodes()[0];
+            if(rootNode && rootNode.isParent && rootNode.isFirstNode){
+                componentOprObj.rootNode = rootNode;
+                me.treeObj.selectNode(rootNode);
+                gljTypeTreeOprObj.onClick(null, 'repositoryTree', rootNode);
             }
-        })
+        });
     },
     showGljItems: function(data, type) {
         let me = repositoryGljObj;
@@ -442,6 +413,12 @@ let repositoryGljObj = {
                         }
                         else if(rObj.basePrice !== me.currentEditingGlj.basePrice){//修改了单价,可修改单价的必为可成为组成物的
                             //寻找所有引用了此组成物的工料机,并从组成物中删去此工料机,并重算单价
+                            if(isNaN(parseFloat(rObj.basePrice))){
+                                alert('单价只能为数值!');
+                                args.sheet.setValue(args.row, args.col, me.currentEditingGlj.basePrice ? me.currentEditingGlj.basePrice : 0);
+                                return;
+                            }
+                            rObj.basePrice = !isNaN(parseFloat(rObj.basePrice))? scMathUtil.roundTo(parseFloat(rObj.basePrice), -2) : me.currentEditingGlj.basePrice;
                             let updateGljs = me.getUpdateGljs(rObj);
                             if(updateGljs.updateArr.length > 0 || updateGljs.updateBasePrcArr.length > 0){
                                 for(let i = 0; i < updateGljs.updateArr.length; i++){
@@ -451,7 +428,7 @@ let repositoryGljObj = {
                                     updateArr.push(updateGljs.updateBasePrcArr[i]);
                                 }
                             }
-                            rObj.basePrice = !isNaN(parseFloat(rObj.basePrice)) && (rObj.basePrice && typeof rObj.basePrice !== 'undefined') ? that.round(parseFloat(rObj.basePrice), 2) : 0;
+                            //rObj.basePrice = !isNaN(parseFloat(rObj.basePrice)) && (rObj.basePrice && typeof rObj.basePrice !== 'undefined') ? that.round(parseFloat(rObj.basePrice), 2) : 0;
                         }
                         rObj.component = me.currentGlj.component;
                         updateArr.push(rObj);
@@ -698,7 +675,7 @@ let repositoryGljObj = {
             if(!isExsit) isValid = false;
         }
         //
-        pasteObj.basePrice = !isNaN(parseFloat(pasteObj.basePrice)) && (pasteObj.basePrice && typeof pasteObj.basePrice !== 'undefined') ? that.round(parseFloat(pasteObj.basePrice), 2) :
+        pasteObj.basePrice = !isNaN(parseFloat(pasteObj.basePrice)) && (pasteObj.basePrice && typeof pasteObj.basePrice !== 'undefined') ? scMathUtil.roundTo(parseFloat(pasteObj.basePrice), 2) :
             me.currentCache[rowIdx].basePrice;
         if(pasteObj.basePrice !== me.currentCache[rowIdx].basePrice){
             reCalBasePrc = true;
@@ -951,39 +928,54 @@ let repositoryGljObj = {
     },*/
     mixUpdateRequest: function(updateArr, addArr, removeIds) {
         let me = repositoryGljObj;
-        $.ajax({
-            type:"POST",
-            url:"complementartGlj/api/mixUpdateGljItems",
-            data:{userId: pageOprObj.userId, compilationId: pageOprObj.compilationId, updateItems: JSON.stringify(updateArr), addItems: JSON.stringify(addArr), removeIds: JSON.stringify(removeIds)},
-            dataType:"json",
-            cache:false,
-            timeout:5000,
-            success:function(result){
-                if (result.error) {
-                    alert(result.message);
-                    me.getRationItems(me.currentRepositoryId);
-                } else {
-                    me.updateCache(addArr, updateArr, removeIds, result);
-                    me.sortGlj(me.complementaryGljList);
-                    if(me.currentOprParent === 1){
-                        me.currentCache = me.getParentCache(me.parentNodeIds["_pNodeId_" + me.gljCurTypeId]);
-                    }
-                    else{
-                        me.currentCache = me.getCache();
+        if(updateArr.length > 0){
+            me.saveInString(updateArr);
+        }
+        if(addArr.length > 0){
+            me.saveInString(addArr);
+        }
+        let url = 'complementartGlj/api/mixUpdateGljItems';
+        let post = {updateItems: updateArr, addItems: addArr, removeIds: removeIds};
+        let scCaller = function (rstData) {
+            me.updateCache(addArr, updateArr, removeIds, rstData);
+            me.sortGlj(me.complementaryGljList);
+            if(me.currentOprParent === 1){
+                me.currentCache = me.getParentCache(me.parentNodeIds["_pNodeId_" + me.gljCurTypeId]);
+            }
+            else{
+                me.currentCache = me.getCache();
+            }
+            me.showGljItems(me.complementaryGljList, me.gljCurTypeId);
+            //getCurrentGlj
+            let row = me.workBook.getSheet(0).getSelections()[0].row;
+            me.currentGlj = row < me.currentCache.length ? me.currentCache[row] : null;
+            me.currentComponent = me.currentGlj ?  me.getCurrentComponent(me.currentGlj.component) : [];
+            sheetOpr.cleanData(gljComponentOprObj.workBook.getSheet(0), gljComponentOprObj.setting, -1);
+            sheetOpr.showData(gljComponentOprObj.workBook.getSheet(0), gljComponentOprObj.setting, me.currentComponent);
+        }
+        let errCaller = function (err) {
+            alert('保存失败');
+        };
+        CommonAjax.post(url, post, scCaller, errCaller);
+    },
+    saveInString: function (datas) {
+        for(let i = 0, len = datas.length; i < len; i++){
+            let data = datas[i];
+            if(_exist(data, 'basePrice')){
+                data['basePrice'] = data['basePrice'].toString();
+            }
+            if(_exist(data, 'component')){
+                for(let j = 0, jLen = data['component'].length; j < jLen; j++){
+                    let comGljObj = data['component'][j];
+                    if(_exist(comGljObj, 'consumeAmt')){
+                        comGljObj['consumeAmt'] = comGljObj['consumeAmt'].toString();
                     }
-                    me.showGljItems(me.complementaryGljList, me.gljCurTypeId);
-                    //getCurrentGlj
-                    let row = me.workBook.getSheet(0).getSelections()[0].row;
-                    me.currentGlj = row < me.currentCache.length ? me.currentCache[row] : null;
-                    me.currentComponent = me.currentGlj ?  me.getCurrentComponent(me.currentGlj.component) : [];
-                    sheetOpr.cleanData(gljComponentOprObj.workBook.getSheet(0), gljComponentOprObj.setting, -1);
-                    sheetOpr.showData(gljComponentOprObj.workBook.getSheet(0), gljComponentOprObj.setting, me.currentComponent);
                 }
-            },
-            error:function(err){
-                alert("保存失败");
             }
-        })
+        }
+        function _exist(data, attr){
+            return data && data[attr] !== undefined && data[attr];
+        }
     },
     getParentCache: function (nodes) {
         let me = repositoryGljObj, rst = [];
@@ -1009,7 +1001,7 @@ let repositoryGljObj = {
         }
         return rst;
     },
-    updateCache: function(addArr, updateArr, removeIds, result) {
+    updateCache: function(addArr, updateArr, removeIds, rstData) {
         let me = this, cacheSection = me.complementaryGljList;
         if (addArr.length > 0) {
             me.complementaryGljList = me.complementaryGljList.concat(addArr);
@@ -1022,11 +1014,11 @@ let repositoryGljObj = {
                 }
             }
         }
-        if (result && result.data && result.data.ops && result.data.ops.length > 0) {
-            for (let i = 0; i < result.data.ops.length; i++) {
+        if (rstData && rstData.ops && rstData.ops.length > 0) {
+            for (let i = 0; i < rstData.ops.length; i++) {
                 for (let j = 0; j < cacheSection.length; j++) {
-                    if (cacheSection[j][me.setting.header[0].dataCode] == result.data.ops[i][me.setting.header[0].dataCode]) {
-                        cacheSection[j]["ID"] = result.data.ops[i]["ID"];
+                    if (cacheSection[j][me.setting.header[0].dataCode] == rstData.ops[i][me.setting.header[0].dataCode]) {
+                        cacheSection[j]["ID"] = rstData.ops[i]["ID"];
                     }
                 }
             }

+ 5 - 4
web/building_saas/complementary_glj_lib/js/gljComponent.js

@@ -309,7 +309,7 @@ let gljComponentOprObj = {
          if(args.col === 4 && me.currentEditingComponent.code && args.editingText && args.editingText.trim().length > 0){//消耗量
             let consumeAmt = parseFloat(args.editingText);
             if(!isNaN(consumeAmt) && consumeAmt !== me.currentEditingComponent.consumeAmt){
-                let roundCons = me.round(consumeAmt, 3);
+                let roundCons = scMathUtil.roundTo(parseFloat(consumeAmt), -3);
                 let component = that.currentGlj.component;
                 for(let i = 0; i < component.length; i++){
                     if(component[i].ID === that.currentComponent[args.row].ID){
@@ -430,7 +430,7 @@ let gljComponentOprObj = {
                 if(row + i < that.currentComponent.length){
                     let currentObj = that.currentComponent[row + i];
                     if(items[i].consumeAmt.trim().length > 0 && items[i].consumeAmt !== currentObj.consumeAmt){
-                        let roundCons = me.round(items[i].consumeAmt, 3);
+                        let roundCons = scMathUtil.roundTo(parseFloat(items[i].consumeAmt), 3);
                         isChange = true;
                         currentObj.consumeAmt = roundCons;
                         for(let j = 0; j < component.length; j++){
@@ -468,6 +468,7 @@ let gljComponentOprObj = {
     },
     updateComponent: function (updateArr) {
         let me = gljComponentOprObj, that = repositoryGljObj;
+        that.saveInString(updateArr);
         $.ajax({
             type: 'post',
             url: 'complementartGlj/api/updateComponent',
@@ -495,8 +496,8 @@ let gljComponentOprObj = {
     reCalGljBasePrc: function (component) {
         let me = gljComponentOprObj, gljBasePrc = 0;
         for(let i = 0; i < component.length; i++){
-            let roundBasePrc = me.round(component[i].basePrice, 2);
-            gljBasePrc += me.round(roundBasePrc * component[i].consumeAmt, 2);
+            let roundBasePrc = scMathUtil.roundTo(parseFloat(component[i].basePrice), -2);
+            gljBasePrc += scMathUtil.roundTo(roundBasePrc * parseFloat(component[i].consumeAmt), -2);
         }
         return gljBasePrc;
     }

+ 6 - 9
web/building_saas/css/main.css

@@ -10,6 +10,9 @@ body {
 .btn.disabled, .btn:disabled {
   color:#999
 }
+.btn-link:focus, .btn-link:hover{
+  text-decoration: none
+}
 /*自定义css*/
 .header {
     background: #e1e1e1
@@ -105,7 +108,7 @@ body {
   width:0%;
 }
 .sidebar-bottom,.sidebar-bottom .col-lg-6,.sidebar-bottom .col-lg-12 {
-  height:200px
+  height:300px
 }
 .top-content, .fluid-content {
     overflow: auto;
@@ -234,7 +237,7 @@ body {
     }
 }
 .bottom-content .tab-content .main-data-bottom{
-    height: 200px;
+    height: 300px;
     overflow: auto;
 }
 .bottom-content .tab-content .ovf-hidden{
@@ -306,11 +309,5 @@ body {
   border-bottom:1px solid #ddd
 }
 .navbar-crumb span{
-  max-width: 200px;
-  display: inline-block;
-}
-.gc-column-header-cell{
-    text-align: center!important;
+  max-width: 200px
 }
-.modal-lg{max-width: 1000px}
-.modal-feeRate {max-width: 550px}

+ 3 - 11
web/building_saas/fee_rates/fee_rate.html

@@ -1,14 +1,6 @@
-<!DOCTYPE html>
-<html lang="en">
 
-<head>
-    <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">
 
-</head>
-
-<body >
+<div >
 <div class="toolsbar px-1">
     <div class="form-inline py-1">
         <label class="mx-2" >当前使用:<span id="feeRateFileName">费率1</span>(<a href="#" id="pop-lv"><span id="projectCount">3</span> 单位工程使用</a>)
@@ -164,5 +156,5 @@
 </div>
 
 
-</body>
-</html>
+</div>
+

+ 2 - 2
web/building_saas/glj/html/glj_index.html

@@ -21,7 +21,7 @@
             <div class="bottom-content">
                 <ul class="nav nav-tabs" role="tablist">
                     <li class="nav-item">
-                        <a class="nav-link active" data-toggle="tab" data-name="ration" href="#de" role="tab">相关定额</a>
+                        <a class="nav-link active" data-toggle="tab" data-name="ration" href="#glj_de" role="tab">相关定额</a>
                     </li>
                     <li class="nav-item">
                         <a class="nav-link" data-toggle="tab" data-name="mix-ratio" href="#ph" role="tab">配合比表</a>
@@ -32,7 +32,7 @@
                 </ul>
                 <!-- Tab panes -->
                 <div class="tab-content">
-                    <div class="tab-pane active" id="de" role="tabpanel">
+                    <div class="tab-pane active" id="glj_de" role="tabpanel">
                         <div class="main-data-bottom ovf-hidden">
                             相关定额
                         </div>

+ 3 - 2
web/building_saas/glj/js/common_spread.js

@@ -29,11 +29,12 @@ function CommonSpreadJs (header) {
 CommonSpreadJs.prototype.init = function(target) {
 
     let setting = {
-        header: []
+        header: [],
+        view:{rowHeaderWidth:35}
     };
     this.columnInfo = [];
     for(let tmp of this.header) {
-        let width = tmp.field === 'name' ? 200 : 120;
+        let width = tmp.width==undefined ? 120 : tmp.width;
         setting.header.push({headerName: tmp.name, headerWidth: width});
         this.columnInfo.push({name: tmp.field, displayName: tmp.name, visible: tmp.visible, cellType: tmp.cellType, size: width});
     }

+ 4 - 0
web/building_saas/glj/js/composition.js

@@ -26,6 +26,7 @@ $(document).ready(function() {
 
             // 筛选数据显示(显示混凝土、砂浆、配合比)
             projectGLJSheet.filterData('unit_price.type', [GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR, GLJTypeConst.MIX_RATIO]);
+
             projectGLJSheet.selectRow(projectGLJSpread.firstMixRatioRow);
             projectGLJId = projectGLJSheet.getActiveDataByField('id');
 
@@ -69,4 +70,7 @@ function compositionSuccess(info) {
     projectGLJSheet.setCellByField('unit_price.market_price', info.parentMarketPrice, false);
     projectGLJSheet.setCellByField('unit_price.base_price', info.parentBasePrice, false);
     projectGLJSheet.setCellByField('adjust_price', info.parentMarketPrice, false);
+
+    // 更新组成物缓存
+    projectObj.project.composition.loadData();
 }

+ 11 - 3
web/building_saas/glj/js/composition_spread.js

@@ -46,12 +46,20 @@ CompositionSpread.prototype.init = function(target) {
     let codeColumn = this.sheetObj.getFieldColumn('code');
     let unitColumn = this.sheetObj.getFieldColumn('unit');
     let consumptionColumn = this.sheetObj.getFieldColumn('consumption');
-
+    let basePriceCol = this.sheetObj.getFieldColumn('unit_price.base_price');
+    let adjustPriceCol = this.sheetObj.getFieldColumn('adjust_price');
+    let marketPriceCol = this.sheetObj.getFieldColumn('unit_price.market_price');
     // 居中样式
     let centerStyleSetting = {hAlign: 1};
     this.sheetObj.setStyle(-1, codeColumn, centerStyleSetting);
     this.sheetObj.setStyle(-1, unitColumn, centerStyleSetting);
 
+    //靠右设置
+    let rightStyleSetting={hAlign: 2};
+    this.sheetObj.setStyle(-1, basePriceCol, rightStyleSetting);
+    this.sheetObj.setStyle(-1, adjustPriceCol, rightStyleSetting);
+    this.sheetObj.setStyle(-1, marketPriceCol, rightStyleSetting);
+    this.sheetObj.setStyle(-1, consumptionColumn, rightStyleSetting);
 
     // 设置可编辑列
     this.sheetObj.setColumnEditable(consumptionColumn);
@@ -92,7 +100,7 @@ CompositionSpread.prototype.initRightClick = function(target) {
                     let row = self.rightClickTarget.row;
                     let idColumn = self.sheetObj.getFieldColumn('mix_ratio_id');
                     let deleteId = activeSheet.getValue(row, idColumn);
-                    self.deleteComposition(deleteId, row, mixRatioSuccess);
+                    self.deleteComposition(deleteId, row, self.successCallback);
                 }
             },
         }
@@ -125,7 +133,7 @@ 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);

+ 19 - 11
web/building_saas/glj/js/project_glj.js

@@ -19,6 +19,7 @@ let GLJTypeConst = [];
 // spreadjs载入数据所需
 let jsonData = [];
 let mixRatioConnectData = [];
+let mixRatioMap={};
 // 单价文件相关
 let usedUnitPriceInfo = {};
 let otherFileData = {};
@@ -26,6 +27,7 @@ let currentTag = '';
 let isChanging = false;
 $(document).ready(function () {
     $('#tab_gongliaoji').on('show.bs.tab', function (e) {
+        $(e.relatedTarget.hash).removeClass('active');
         init();
     });
 
@@ -203,18 +205,9 @@ function init() {
         if (jsonData.length <= 0) {
             // 赋值
             jsonData = data.gljList !== undefined && data.gljList.length > 0 ? data.gljList : [];
-            if (jsonData.length > 0) {
-                // 不显示消耗量为0的数据
-                let tmpData = [];
-                for(let data of jsonData) {
-                    if (data.quantity !== 0) {
-                        tmpData.push(data);
-                    }
-                }
-                jsonData = tmpData;
-            }
+            jsonData= filterProjectGLJ(jsonData);
             mixRatioConnectData = data.mixRatioConnectData !== undefined ? data.mixRatioConnectData : mixRatioConnectData;
-
+            mixRatioMap = data.mixRatioMap !== undefined ? data.mixRatioMap : mixRatioMap;
             host = data.constData.hostname !== undefined ? data.constData.hostname : '';
             materialIdList = data.constData.materialIdList !== undefined ? data.constData.materialIdList : materialIdList;
             roomId = data.constData.roomId !== undefined ? data.constData.roomId : roomId;
@@ -353,3 +346,18 @@ function socketInit() {
         $("#notify").slideDown('fast');
     });
 }
+
+//过滤消耗量为0的项目工料机
+function filterProjectGLJ(jsonData) {
+    if (jsonData.length > 0) {
+        // 不显示消耗量为0的数据
+        let tmpData = [];
+        for(let data of jsonData) {
+            if (data.quantity !== 0&&data.quantity !=='0') {
+                tmpData.push(data);
+            }
+        }
+        jsonData = tmpData;
+    }
+    return jsonData;
+}

+ 23 - 16
web/building_saas/glj/js/project_glj_spread.js

@@ -40,34 +40,36 @@ ProjectGLJSpread.prototype.init = function () {
     selectBox.items(supplySelect);
     selectBox.editorValueType(GC.Spread.Sheets.CellTypes.EditorValueType.text);
     let header = [
-        {name: '编码', field: 'code', visible: true},
-        {name: '名称', field: 'name', visible: true},
-        {name: '规格型号', field: 'specs', visible: true},
-        {name: '单位', field: 'unit', visible: true},
-        {name: '类型', field: 'unit_price.short_name', visible: true},
+        {name: '编码', field: 'code', visible: true,width:80},
+        {name: '名称', field: 'name', visible: true,width:160},
+        {name: '规格型号', field: 'specs', visible: true,width:120},
+        {name: '单位', field: 'unit', visible: true,width:45},
+        {name: '类型', field: 'unit_price.short_name', visible: true,width:45},
         {name: 'ID', field: 'id', visible: false},
         {name: '类型', field: 'unit_price.type', visible: false},
-        {name: '总消耗量', field: 'quantity', visible: true},
-        {name: '基价单价', field: "unit_price.base_price", visible: true},
-        {name: '调整基价', field: 'adjust_price', visible: true},
-        {name: '市场单价', field: "unit_price.market_price", visible: true, validator: 'number'},
+        {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: 'is_evaluate',
             visible: true,
             cellType: new GC.Spread.Sheets.CellTypes.CheckBox(),
             validator: 'boolean'
+            ,width:60
         },
-        {name: '供货方式', field: 'supply', visible: true, cellType: selectBox},
-        {name: '甲供数量', field: 'supply_quantity', visible: true},
-        {name: '交货方式', field: 'delivery', visible: true},
-        {name: '送达地点', field: 'delivery_address', visible: true},
+        {name: '供货方式', field: 'supply', visible: true, cellType: selectBox,width:80},
+        {name: '甲供数量', field: 'supply_quantity', visible: true,width:100},
+        {name: '交货方式', field: 'delivery', visible: true,width:90},
+        {name: '送达地点', field: 'delivery_address', visible: true,width:100},
         {
             name: '不调价',
             field: 'is_adjust_price',
             visible: true,
             cellType: new GC.Spread.Sheets.CellTypes.CheckBox(),
-            validator: 'boolean'
+            validator: 'boolean',
+            width:55
         },
         {name: 'UID', field: 'unit_price.id', visible: false},
         {name: '工料机ID', field: 'glj_id', visible: false},
@@ -259,6 +261,11 @@ ProjectGLJSpread.prototype.specialColumn = function (sourceData) {
             // 锁定该单元格
             activeSheet.getCell(rowCounter, isEvaluateColumn, GC.Spread.Sheets.SheetArea.viewport).locked(true);
             activeSheet.setValue(rowCounter, isEvaluateColumn, '');
+        }else {
+            let checkBox = new GC.Spread.Sheets.CellTypes.CheckBox();
+            activeSheet.setCellType(rowCounter, isEvaluateColumn, checkBox, GC.Spread.Sheets.SheetArea.viewport);
+            activeSheet.getCell(rowCounter, isEvaluateColumn, GC.Spread.Sheets.SheetArea.viewport).locked(false);
+            activeSheet.setValue(rowCounter, isEvaluateColumn, data.is_evaluate);
         }
         // 设置供货方式列是否可选
         if (this.supplyReadonlyType.indexOf(data.unit_price.type) >= 0) {
@@ -391,14 +398,14 @@ ProjectGLJSpread.prototype.priceCalculate = function(info) {
     // 获取类型
     let type = activeSheet.getValue(row, typeColumn);
 
-    // 基价单价计算
+  /*  // 基价单价计算
     switch (type) {
         // 主材、设备自动赋值基价单价=市场单价
         case GLJTypeConst.MAIN_MATERIAL:
         case GLJTypeConst.EQUIPMENT:
             activeSheet.setValue(info.row, basePriceColumn, info.newValue);
             break;
-    }
+    }*/
 
     // 调整基价计算
     switch (type) {

+ 2 - 2
web/building_saas/js/global.js

@@ -6,8 +6,8 @@ function autoFlashHeight(){
     var bottomContentHeight = $(".bottom-content").height();
     var toolsBarHeightQ = $(".tools-bar-height-q").height();
     var toolsBarHeightD = $(".tools-bar-height-d").height();
-    $(".main-data-side-q").height($(window).height()-headerHeight-toolsbarHeight-toolsBarHeightQ-202);
-    $(".main-data-side-d").height($(window).height()-headerHeight-toolsbarHeight-toolsBarHeightD-202);
+    $(".main-data-side-q").height($(window).height()-headerHeight-toolsbarHeight-toolsBarHeightQ-302);
+    $(".main-data-side-d").height($(window).height()-headerHeight-toolsbarHeight-toolsBarHeightD-302);
     $(".main-data-top").height($(window).height()-headerHeight-toolsbarHeight-bottomContentHeight-1);
     $(".main-data-full").height($(window).height()-headerHeight-toolsbarHeight-1);
     $(".main-data-full-fl").height($(window).height()-headerHeight-toolsbarHeight-37);

+ 6 - 6
web/building_saas/main/html/calc_program_manage.html

@@ -15,16 +15,16 @@
     </div>
     <div class="container-fluid">
         <div class="row">
-        <div class="col-lg-3 p-0">
-            <div class="main-data-not" id="mainSpread">
+            <div class="col-lg-2 p-0">
+                <div class="main-data-not" id="mainSpread">
+                </div>
             </div>
-        </div>
-        <div class="col-lg-9 p-0">
-            <div class="main-data-not" id="detailSpread">
+            <div class="col-lg-10 p-0">
+                <div class="main-data-not" id="detailSpread">
+                </div>
             </div>
         </div>
     </div>
-    </div>
 
     <!--弹出 计算基数-->
 <div class="modal fade" id="jsjs" data-backdrop="static">

Diferenças do arquivo suprimidas por serem muito extensas
+ 232 - 311
web/building_saas/main/html/main.html


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

@@ -263,12 +263,13 @@ class BillsCalcHelper {
     };
     calcRationLeaf (node, fields, isIncre) {
         nodeCalcObj.node = node;
-        nodeCalcObj.digit = this.project.Decimal.unitFee;
+        nodeCalcObj.digit = this.project.Decimal.common.unitFee;
         calcFees.checkFields(node.data, fields);
         let nodeCalc = nodeCalcObj, virData= null, decimal = this.project.Decimal;
 
         // 清单单价:套用定额计算程序
-        if (this.project.calcFlag === billsPrice) {
+        // if (this.project.calcFlag === billsPrice) {
+        if (this.project.projSetting.billsCalcMode === billsPrice) {
             rationCalcObj.calcGljs = this.getBillsGLjs(node);
             console.log(rationCalcObj.calcGljs);
             rationCalcObj.calcFields = rationCalcFields;
@@ -322,6 +323,12 @@ class BillsCalcHelper {
             this.setTotalFee(node, field, value, isIncre);
         }
     };
+    clearFeeFields(node, fields, isIncre) {
+        for (let field of fields) {
+            node.data.feesIndex[field.type].unitFee = 0;
+            this.setTotalFee(node, field, 0, isIncre);
+        }
+    }
     calcNode(node, isIncre) {
         if (node.source.children.length > 0) {
             this.calcParent(node, this.project.calcFields, isIncre);
@@ -333,7 +340,7 @@ class BillsCalcHelper {
                     this.calcVolumePriceLeaf(node, this.project.calcFields, isIncre);
                 }
             } else {
-
+                this.clearFeeFields(node, this.project.calcFields, isIncre);
             }
         }
     };
@@ -352,6 +359,7 @@ class BillsCalcHelper {
         if (parent && parent.sourceType === this.project.Bills.getSourceType()) {
             calcFees.checkFields(parent.data, [field]);
             parent.data.feesIndex[field.type].totalFee = (parent.data.feesIndex[field.type].totalFee + Incre).toDecimal(this.project.Decimal.common.totalFee);
+            parent.data.feesIndex[field.type].unitFee = 0;   // AAAAA 临时补上,使存储 unitFee.toFixed(2) 时不出错
             this.updateParent(parent.parent, field, Incre);
         }
     };
@@ -359,9 +367,11 @@ class BillsCalcHelper {
         if (isIncre) {
             let incre = value - node.data.feesIndex[field.type].totalFee;
             node.data.feesIndex[field.type].totalFee = value;
+            node.data.feesIndex[field.type].unitFee = 0; // AAAAA 临时补上,使存储 unitFee.toFixed(2) 时不出错
             this.updateParent(node.parent, field, incre);
         } else {
             node.data.feesIndex[field.type].totalFee = value;
+            node.data.feesIndex[field.type].unitFee = 0; // AAAAA 临时补上,使存储 unitFee.toFixed(2) 时不出错
         }
     };
     converseCalc (node) {

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


Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff