Browse Source

1.0.0_online合并到master分支。

chenshilong 5 years ago
parent
commit
aefa2a667e
100 changed files with 4687 additions and 772 deletions
  1. 10 1
      .babelrc
  2. 0 0
      .tgitconfig
  3. 30 6
      Dockerfile
  4. 18 6
      config/config.js
  5. 2 2
      config/db/db_manager.js
  6. 19 3
      config/gulpConfig.js
  7. 70 0
      importserver.js
  8. 132 0
      lib/bootstrap/bootstrap-submenu.js
  9. 29 0
      lib/bootstrap/css/bootstrap-submenu.css
  10. 2 2
      lib/bootstrap/css/bootstrap.min.css
  11. 3 0
      lib/fileSaver/FileSaver.min.js
  12. 16 0
      lib/jquery-contextmenu/jquery.contextMenu.js
  13. 1 0
      lib/js-base64/base64.min.js
  14. 15 0
      lib/js-zip/jszip.min.js
  15. 8 0
      lib/jspdf/SmartSimsun-bold.js
  16. 8 0
      lib/jspdf/SmartSimsun-normal.js
  17. 286 0
      lib/jspdf/jspdf.min.js
  18. 1 0
      lib/x2js/xml2json.min.js
  19. 43 0
      logs/online_logs.js
  20. 25 0
      modules/all_models/basic_info_lib.js
  21. 16 4
      modules/all_models/bills.js
  22. 1 1
      modules/all_models/calc_program_model.js
  23. 4 1
      modules/all_models/compleRation_ration.js
  24. 74 0
      modules/all_models/config_material_list.js
  25. 37 1
      modules/all_models/engineering_lib.js
  26. 3 3
      modules/all_models/fee_rates.js
  27. 1 1
      modules/all_models/mix_ratio.js
  28. 17 0
      modules/all_models/online_logs.js
  29. 31 0
      modules/all_models/product.js
  30. 33 6
      modules/all_models/project_glj.js
  31. 2 1
      modules/all_models/projects.js
  32. 15 5
      modules/all_models/ration.js
  33. 2 2
      modules/all_models/ration_coe.js
  34. 5 2
      modules/all_models/ration_glj.js
  35. 5 0
      modules/all_models/stdBills_bills.js
  36. 1 0
      modules/all_models/stdRation_lib.js
  37. 5 2
      modules/all_models/stdRation_ration.js
  38. 25 0
      modules/all_models/std_economic_lib.js
  39. 25 0
      modules/all_models/std_engineer_feature_lib.js
  40. 25 0
      modules/all_models/std_engineer_info_lib.js
  41. 5 1
      modules/all_models/std_glj.js
  42. 25 0
      modules/all_models/std_main_quantity_lib.js
  43. 25 0
      modules/all_models/std_material_lib.js
  44. 25 0
      modules/all_models/std_over_height_lib.js
  45. 2 2
      modules/all_models/unit_price.js
  46. 13 0
      modules/all_models/user.js
  47. 2 2
      modules/bills_lib/models/bills_lib_interfaces.js
  48. 2 1
      modules/common/base/base_model.js
  49. 26 1
      modules/common/const/bills_fixed.js
  50. 14 7
      modules/complementary_glj_lib/controllers/gljController.js
  51. 110 58
      modules/complementary_glj_lib/models/gljModel.js
  52. 1 0
      modules/complementary_glj_lib/routes/routes.js
  53. 18 5
      modules/complementary_ration_lib/controllers/compleViewController.js
  54. 52 54
      modules/complementary_ration_lib/models/compleRationModel.js
  55. 65 32
      modules/complementary_ration_lib/models/searchModel.js
  56. 8 2
      modules/complementary_ration_lib/models/sectionTreeModel.js
  57. 1 0
      modules/complementary_ration_lib/routes/routes.js
  58. 1 2
      modules/fee_rates/facade/fee_rates_facade.js
  59. 40 81
      modules/glj/controllers/glj_controller.js
  60. 38 2
      modules/glj/facade/glj_facade.js
  61. 159 77
      modules/glj/models/glj_list_model.js
  62. 1 2
      modules/glj/models/mix_ratio_model.js
  63. 2 2
      modules/glj/models/unit_price_file_model.js
  64. 4 0
      modules/glj/routes/glj_router.js
  65. 59 0
      modules/import/controllers/import_controller.js
  66. 16 0
      modules/import/routes/import_route.js
  67. 1 1
      modules/main/controllers/bills_controller.js
  68. 32 0
      modules/main/controllers/project_controller.js
  69. 21 6
      modules/main/controllers/ration_controller.js
  70. 24 0
      modules/main/facade/bid_facade.js
  71. 13 10
      modules/main/facade/bill_facade.js
  72. 24 0
      modules/main/facade/contractor_facade.js
  73. 24 0
      modules/main/facade/evaluate_facade.js
  74. 239 10
      modules/main/facade/project_facade.js
  75. 76 49
      modules/main/facade/ration_facade.js
  76. 9 3
      modules/main/models/bills.js
  77. 8 0
      modules/main/models/project.js
  78. 24 2
      modules/main/models/project_consts.js
  79. 7 3
      modules/main/models/ration.js
  80. 17 2
      modules/main/routes/main_route.js
  81. 2 1
      modules/main/routes/project_route.js
  82. 6 0
      modules/pm/controllers/new_proj_controller.js
  83. 214 24
      modules/pm/controllers/pm_controller.js
  84. 1092 69
      modules/pm/facade/pm_facade.js
  85. 83 49
      modules/pm/models/project_model.js
  86. 3 2
      modules/pm/models/project_property_template.js
  87. 11 1
      modules/pm/routes/pm_route.js
  88. 25 0
      modules/ration_glj/controllers/ration_glj_controller.js
  89. 69 11
      modules/ration_glj/facade/glj_calculate_facade.js
  90. 50 30
      modules/ration_glj/facade/ration_glj_facade.js
  91. 1 0
      modules/ration_glj/routes/ration_glj_route.js
  92. 65 22
      modules/reports/controllers/rpt_controller.js
  93. 2 2
      modules/reports/rpt_component/helper/jpc_helper_common.js
  94. 5 1
      modules/reports/rpt_component/helper/jpc_helper_common_output.js
  95. 39 7
      modules/reports/rpt_component/helper/jpc_helper_cross_tab.js
  96. 1 1
      modules/reports/rpt_component/helper/jpc_helper_discrete.js
  97. 4 1
      modules/reports/rpt_component/helper/jpc_helper_field.js
  98. 513 0
      modules/reports/rpt_component/helper/jpc_helper_font_width.js
  99. 224 85
      modules/reports/rpt_component/jpc_cross_tab.js
  100. 0 0
      modules/reports/rpt_component/jpc_ex.js

+ 10 - 1
.babelrc

@@ -1,3 +1,12 @@
 {
-  "presets": ["es2015", "stage-2"]
+  "presets": ["es2015", "stage-2"],
+  "plugins": [[
+    "transform-runtime",
+    {
+      "helpers": false,
+      "polyfill": false,
+      "regenerator": true,
+      "moduleName": "babel-runtime"
+    }
+  ]]
 }

+ 0 - 0
.tgitconfig


+ 30 - 6
Dockerfile

@@ -1,17 +1,41 @@
-FROM costbase:2.0
+FROM costbase:2.0 as build
 
 WORKDIR /home/ConstructionCost
 
-COPY . /home/ConstructionCost
+COPY package.json /home/ConstructionCost/
+COPY .babelrc /home/ConstructionCost/
 
 RUN cnpm install
 
-RUN gulp build
+FROM base-alpine:latest as babel
 
-EXPOSE 6060
+COPY --from=build /home/ConstructionCost /home/ConstructionCost
 
-ENV NODE_ENV=prod
+COPY config /home/ConstructionCost/src/config/
+COPY logs /home/ConstructionCost/src/logs/
+COPY modules /home/ConstructionCost/src/modules/
+COPY public /home/ConstructionCost/src/public/
+COPY importserver.js server.js socket.js  /home/ConstructionCost/src/
+
+WORKDIR /home/ConstructionCost
+
+RUN babel src -d dist
+
+FROM base-alpine:latest
+
+COPY . /home/ConstructionCost
+
+WORKDIR /home/ConstructionCost
+
+COPY --from=babel /home/ConstructionCost/dist /home/ConstructionCost
+COPY --from=babel /home/ConstructionCost/node_modules /home/ConstructionCost/node_modules/
+COPY public/web /home/ConstructionCost/public/web/
 
-ENTRYPOINT babel-node server.js
 
+RUN gulp build
+
+ENV NODE_ENV=prod
+
+ENTRYPOINT ["npm", "run"]
 
+CMD ["server"]

File diff suppressed because it is too large
+ 18 - 6
config/config.js


+ 2 - 2
config/db/db_manager.js

@@ -50,11 +50,11 @@ module.exports = {
         var config = require("../config.js");
         var dbURL = 'mongodb://' + config[env].server + ":" + config[env].port + '/scConstruct';
         if(config[env].dbURL){
-            mg.connect(config[env].dbURL,{connectTimeoutMS: 20000,useMongoClient: true});
+            mg.connect(config[env].dbURL,{connectTimeoutMS: 100000,useMongoClient: true});
         } else if(config[env].options){
             mg.connect(dbURL,config[env].options);
         }else {
-            mg.connect(dbURL,{connectTimeoutMS: 20000,useMongoClient: true});//useMongoClient': true*! //报 DeprecationWarning: `open()` is deprecated in mongoose这个错
+            mg.connect(dbURL,{connectTimeoutMS: 100000,useMongoClient: true});//useMongoClient': true*! //报 DeprecationWarning: `open()` is deprecated in mongoose这个错
         }
         var db = mg.connection;
         db.on("error",function (err) {

+ 19 - 3
config/gulpConfig.js

@@ -3,12 +3,13 @@
  */
 
 module.exports = {
-    version:'1.0.1',
+    version:'1.0.2',
     common_jspaths:[
         'lib/jquery/jquery-3.2.1.min.js',
         'lib/jquery-ui/jquery-ui.min.js',
         'lib/popper/popper.min.js',
         'lib/bootstrap/bootstrap.min.js',
+        'lib/bootstrap/bootstrap-submenu.js',
         'web/building_saas/js/*.js',
         'public/web/scMathUtil.js',
         'public/web/gljUtil.js',
@@ -21,6 +22,7 @@ module.exports = {
     common_css:[
         'lib/jquery-ui/jquery-ui.css',
         'lib/bootstrap/css/bootstrap.min.css',
+        'lib/bootstrap/css/bootstrap-submenu.js',
         'lib/spreadjs/sheets/css/gc.spread.sheets.sc.css',
         'web/building_saas/css/main.css',
         'web/building_saas/css/custom.css',
@@ -29,7 +31,8 @@ module.exports = {
     ],
     login_jspaths:[
         'public/web/url_util.js',
-        'web/users/js/login.js',
+        'web/users/js/gt.js',
+        'web/users/js/login.js'
     ],
     pm_css:[
         'lib/ztree/css/zTreeStyle.css',
@@ -46,6 +49,8 @@ module.exports = {
         'lib/JSExpressionEval_src/Date.js',
         'web/building_saas/glj/js/socket.io.slim.js',
         'public/web/socket/connection.js',
+        'public/billsUtil.js',
+        'web/building_saas/main/js/models/importStandardInterface.js',
         'web/building_saas/pm/js/**/*.js',
         'lib/ztree/*.js',
         'lib/jquery-contextmenu/jquery.contextMenu.min.js'
@@ -65,6 +70,7 @@ module.exports = {
         //'lib/lodash/lodash.js',
         // 'test/tmp_data/test_ration_calc/ration_calc_base.js',
         'web/building_saas/main/js/models/main_consts.js',
+        'web/over_write/config/compilation_config.js',
         'public/web/common_util.js',
         'web/building_saas/glj/js/project_glj.js',
         'web/building_saas/glj/js/composition.js',
@@ -106,6 +112,10 @@ module.exports = {
         'public/web/id_tree.js',
         'web/building_saas/main/js/models/cache_tree.js',
         'web/building_saas/main/js/calc/calc_fees.js',
+        'web/building_saas/main/js/models/exportStdInterfaceBase.js',
+        'web/building_saas/main/js/models/exportStandardInterface.js',
+        'web/building_saas/main/js/models/exportSEIInterface.js',
+        'web/building_saas/main/js/models/overHeight.js',
         // 'web/building_saas/main/js/calc/ration_calc.js',
         // 'web/building_saas/main/js/calc/bills_calc.js',
         // 'public/calc_util.js',
@@ -115,6 +125,7 @@ module.exports = {
         'web/building_saas/main/js/views/glj_col.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/export_view.js',
         'web/building_saas/main/js/views/project_view.js',
         'web/building_saas/main/js/views/options_view.js',
         'web/building_saas/main/js/views/project_property_bills_quantity_decimal.js',
@@ -139,12 +150,16 @@ module.exports = {
         'web/building_saas/main/js/views/confirm_modal.js',
         'web/building_saas/main/js/views/zlfb_view.js',
         'web/building_saas/main/js/views/installation_fee_view.js',
+        'web/building_saas/main/js/views/material_adjust_view.js',
+        'web/building_saas/main/js/views/config_material_view.js',
+        'web/building_saas/main/js/views/index_view.js',
         'web/building_saas/main/js/views/project_glj_view.js',
         'web/building_saas/main/js/views/importBills.js',
         'public/web/rpt_tpl_def.js',
         'public/web/treeDataHelper.js',
         'public/web/string_util_light.js',
         'public/web/ztree_common.js',
+        'web/building_saas/report/js/rpt_jspdf.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',
@@ -224,7 +239,8 @@ module.exports = {
         'web/building_saas/complementary_ration_lib/js/ration_assist.js',
         'web/building_saas/complementary_ration_lib/js/ration_installation.js.js',
         'public/web/slideResize.js',
-        'web/building_saas/complementary_ration_lib/js/coe.js'
+        'web/building_saas/complementary_ration_lib/js/coe.js',
+        'web/building_saas/complementary_ration_lib/js/init.js'
     ],
     compleRation_glj_css: [
     ],

+ 70 - 0
importserver.js

@@ -0,0 +1,70 @@
+/**
+ * Created by zhang on 2019/12/5.
+ */
+let express = require('express');
+
+let config = require("./config/config.js");
+let fileUtils = require("./modules/common/fileUtils");
+let dbm = require("./config/db/db_manager");
+let log = require("./logs/log_helper");
+
+config.setupDb(process.env.NODE_ENV);
+
+let path = require('path');
+let fs = require('fs');
+
+
+dbm.connect(process.env.NODE_ENV);
+//这里现在只引入了定额工料机里的models,当其它模块的models修改后使用:./modules/**/models/*.js引入所有的模块
+fileUtils.getGlobbedFiles('./modules/all_models/*.js').forEach(function(modelPath) {
+    require(path.resolve(modelPath));
+})
+
+
+
+
+let app = express();
+let _rootDir = __dirname;
+app.locals.rootDir = _rootDir;
+log.use(app);
+app.use(express.static(_rootDir));
+
+app.set('views', path.join(__dirname, 'web'));
+app.engine('.html', require('ejs').__express);
+app.set('view engine', 'html');
+
+let bodyParser = require('body-parser');
+app.use(bodyParser.urlencoded({limit: '100mb', extended: false}));
+app.use(bodyParser.json({limit: '100mb'}));
+
+
+
+//加载路由文件
+fileUtils.getGlobbedFiles('./modules/import/routes/*.js').forEach(function(modelPath) {
+    require(path.resolve(modelPath))(app);
+});
+
+//app.use(express.static(_rootDir+"/web"));
+//app.use(express.static(_rootDir+"/lib"));
+//let rations_Router = require("./modules/rationLibEditor/routes/rationLibEditor_route");
+//app.use("/rationLibEditor",rations_Router);
+
+//-----------------
+
+app.use(function(req, res, next) {
+    res.status(404).send('404 Error');
+});
+app.use(function(err, req, res, next) {
+    console.error(err.stack);
+    res.status(500).send('500 Error');
+});
+
+//设置外增的Date对象Format函数
+//备注: 经过测试nodejs 8.9.3版本不支持eval的方式修改prototype,为兼容考虑,把方法调整到stringUtil文件里
+require('./public/stringUtil').setupDateFormat();
+
+app.listen(6050, function(){
+    console.log('import server started!');
+});
+
+

+ 132 - 0
lib/bootstrap/bootstrap-submenu.js

@@ -0,0 +1,132 @@
+(function(factory) {
+  if (typeof define === 'function' && define.amd) {
+    // AMD. Register as an anonymous module
+    define(['jquery'], factory);
+  } else if (typeof exports === 'object') {
+    // Node/CommonJS
+    module.exports = factory(require('jquery'));
+  } else {
+    // Browser globals
+    factory(jQuery);
+  }
+})(function($) {
+  class DropdownSubmenu {
+    constructor(element) {
+      this.element = element.parentElement;
+      this.menuElement = this.element.querySelector('.dropdown-menu');
+
+      this.init();
+    }
+
+    init() {
+      $(this.element).off('keydown.bs.dropdown.data-api');
+
+      this.menuElement.addEventListener('keydown', this.itemKeydown.bind(this));
+
+      const dropdownItemNodeList = this.menuElement.querySelectorAll('.dropdown-item');
+
+      for (const element of dropdownItemNodeList) {
+        element.addEventListener('keydown', this.handleKeydownDropdownItem.bind(this));
+      }
+
+      $(this.menuElement).on('keydown', '.dropdown-submenu > .dropdown-item', this.handleKeydownSubmenuDropdownItem.bind(this));
+      $(this.menuElement).on('click', '.dropdown-submenu > .dropdown-item', this.handleClickSubmenuDropdownItem.bind(this));
+      $(this.element).on('hidden.bs.dropdown', () => {
+        this.close(this.menuElement);
+      });
+    }
+
+    handleKeydownDropdownItem(event) {
+      // 27: Esc
+      if (event.keyCode !== 27) {
+        return;
+      }
+
+      event.target.closest('.dropdown-menu').previousElementSibling.focus();
+      event.target.closest('.dropdown-menu').classList.remove('show');
+    }
+
+    handleKeydownSubmenuDropdownItem(event) {
+      // 32: Spacebar
+      if (event.keyCode !== 32) {
+        return;
+      }
+
+      // NOTE: Off vertical scrolling
+      event.preventDefault();
+
+      this.toggle(event.target);
+    }
+
+    handleClickSubmenuDropdownItem(event) {
+      event.stopPropagation();
+
+      this.toggle(event.target);
+    }
+
+    itemKeydown(event) {
+      // 38: Arrow up, 40: Arrow down
+      if (![38, 40].includes(event.keyCode)) {
+        return;
+      }
+
+      // NOTE: Off vertical scrolling
+      event.preventDefault();
+
+      event.stopPropagation();
+
+      const itemNodeList = this.element.querySelectorAll('.show > .dropdown-item:not(:disabled):not(.disabled), .show > .dropdown > .dropdown-item');
+
+      let index = Array.from(itemNodeList).findIndex((element) => {
+        return element === event.target;
+      });
+
+      if (event.keyCode === 38 && index !== 0) {
+        index--;
+      } else if (event.keyCode === 40 && index !== itemNodeList.length - 1) {
+        index++;
+      } else {
+        return;
+      }
+
+      itemNodeList[index].focus();
+    }
+
+    toggle(element) {
+      const dropdownElement = element.closest('.dropdown');
+      const parentMenuElement = dropdownElement.closest('.dropdown-menu');
+      const menuElement = dropdownElement.querySelector('.dropdown-menu');
+      const isOpen = menuElement.classList.contains('show');
+
+      this.close(parentMenuElement);
+
+      menuElement.classList.toggle('show', !isOpen);
+    }
+
+    close(menuElement) {
+      const menuNodeList = menuElement.querySelectorAll('.dropdown-menu.show');
+
+      for (const element of menuNodeList) {
+        element.classList.remove('show');
+      }
+    }
+  }
+
+  // For AMD/Node/CommonJS used elements (optional)
+  // http://learn.jquery.com/jquery-ui/environments/amd/
+  $.fn.submenupicker = function(elements) {
+    const $elements = this instanceof $ ? this : $(elements);
+
+    return $elements.each(function() {
+      let data = $.data(this, 'bs.submenu');
+
+      if (!data) {
+        data = new DropdownSubmenu(this);
+
+        $.data(this, 'bs.submenu', data);
+      }
+    });
+  };
+
+  return DropdownSubmenu;
+});

+ 29 - 0
lib/bootstrap/css/bootstrap-submenu.css

@@ -0,0 +1,29 @@
+.dropdown-submenu.dropright .dropdown-menu {
+  margin-left: 1px;
+}
+
+.dropdown-submenu .dropleft .dropdown-menu {
+  margin-right: 1px;
+}
+
+[x-placement^="bottom-"] .dropdown-submenu .dropdown-menu,
+.navbar .dropdown-submenu .dropdown-menu {
+  bottom: auto;
+  margin-top: calc(-0.5rem - 1px);
+}
+
+[x-placement^="top-"] .dropdown-submenu .dropdown-menu {
+  top: auto;
+  bottom: 0;
+  margin-bottom: calc(-0.5rem - 1px);
+}
+
+.dropdown-submenu.dropright > .dropdown-toggle {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.dropdown-submenu.dropright > .dropdown-toggle::after {
+  margin-right: -12px;
+}

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


File diff suppressed because it is too large
+ 3 - 0
lib/fileSaver/FileSaver.min.js


+ 16 - 0
lib/jquery-contextmenu/jquery.contextMenu.js

@@ -199,6 +199,21 @@
 
                 opt.$menu.css(offset);
             },
+            // 有的电脑会出现主菜单和子菜单距离比较远的情况,这里检查一下。
+            checkSubmenuPosition: function ($menu) {
+                if (!$menu) {
+                    return;
+                }
+                const parentOffsetWidth = $menu.parentElement.offsetWidth;
+                const curLeft = parseFloat(window.getComputedStyle($menu).left) || 0;
+                const maxDiff = 8;
+                const minDiff = 4;
+                const diff = parentOffsetWidth - curLeft;
+                if (diff > maxDiff || diff < minDiff) {
+                    const fixedLeft = parentOffsetWidth - 5;
+                    $menu.style.left = `${fixedLeft}px`;
+                }
+            },
             // position the sub-menu
             positionSubmenu: function ($menu) {
                 if (typeof $menu === 'undefined') {
@@ -224,6 +239,7 @@
                     };
                     $menu.css(offset);
                 }
+                defaults.checkSubmenuPosition($menu[0]);
             },
             // offset to add to zIndex
             zIndex: 1,

File diff suppressed because it is too large
+ 1 - 0
lib/js-base64/base64.min.js


File diff suppressed because it is too large
+ 15 - 0
lib/js-zip/jszip.min.js


File diff suppressed because it is too large
+ 8 - 0
lib/jspdf/SmartSimsun-bold.js


File diff suppressed because it is too large
+ 8 - 0
lib/jspdf/SmartSimsun-normal.js


File diff suppressed because it is too large
+ 286 - 0
lib/jspdf/jspdf.min.js


File diff suppressed because it is too large
+ 1 - 0
lib/x2js/xml2json.min.js


+ 43 - 0
logs/online_logs.js

@@ -0,0 +1,43 @@
+/**
+ * Created by zhang on 2019/4/12.
+ */
+module.exports = {
+    saveOnlineTime:saveOnlineTime
+};
+
+let mongoose = require("mongoose");
+import moment from "moment";
+let logs_model = mongoose.model("online_logs");
+
+async function saveOnlineTime(req) {
+    let online_times = 0;
+    let end = + new Date();
+    try {
+        let interval_time = 10 * 60 *1000;
+        let start = req.session.online_start_time;
+        if(start === undefined) return req.session.online_start_time ==end;
+        let online_times =  end - start;
+        //1秒内只记一次就好
+        if(online_times < 500) return;//如果间隔太短,则忽略
+        if(online_times > interval_time ){//如果间隔超过有效时长,则不累加这次时长,从头开始算
+            req.session.online_start_time = end;
+            return
+        }
+        if(!req.session.sessionUser||!req.session.sessionCompilation) return;
+        let dataString = moment(end).format('YYYY-MM-DD');
+        let condition = {userID:req.session.sessionUser.id,compilationID:req.session.sessionCompilation._id,dateString:dataString};
+        let record = await logs_model.findOne(condition);
+        if(record){ //如果找到,则累加
+            await logs_model.update(condition,{$inc:{'online_times' : online_times }});
+        }else {//如果没找到,则新增一条记录
+            condition["online_times"] = online_times;
+            let today = moment(dataString).toDate();
+            condition["dateTime"] = +today;
+            await logs_model.create(condition);
+        }
+    }catch (e){
+        console.log("统计登录时间错误,online_times值:"+online_times);
+        console.log(e);
+    }
+    req.session.online_start_time = end;
+}

+ 25 - 0
modules/all_models/basic_info_lib.js

@@ -0,0 +1,25 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/3/5
+ * @version
+ */
+//建设项目基本信息库
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const oprSchema = require('../all_schemas/opr_schema');
+const basicInfoLib = new Schema({
+    ID: {type: String, index: true},
+    name: String,
+    creator: String,
+    createDate: Number,
+    recentOpr: [oprSchema],
+    info: {
+        type: Schema.Types.Mixed,
+        default: []
+    }
+});
+mongoose.model('std_basic_info_lib', basicInfoLib, 'std_basic_info_lib');

+ 16 - 4
modules/all_models/bills.js

@@ -6,10 +6,10 @@ let subSchema = require("../all_schemas/bills_sub_schemas");
 let deleteSchema = require('../all_schemas/delete_schema');
 let Schema = mongoose.Schema;
 let billsSchema = new Schema({
-    ID: String,
+    ID: {type: String, index: true},
     ParentID: String,
     NextSiblingID: String,
-    projectID: Number,
+    projectID: {type: Number, index: true} ,
     serialNo: Number,
     chapterID: Number,
     billsLibId: Number,
@@ -64,10 +64,22 @@ let billsSchema = new Schema({
     installationKey:String,//用来记录安装增加费的关联字段
     deleteInfo: deleteSchema,
     isEstimate:{type: Number,default:0},       // 1 true 0 false 是否暂估
+    mainBills:{type:Schema.Types.Mixed,default:false},//true 是,false否,null 不确定,三个状态
+    outPutMaxPrice:{type:Schema.Types.Mixed,default:false},//输出最高限价 true 是,false否,null 不确定,三个状态
+    maxPrice:String,//最高限价
     remark:String,
     engineeringContent:String,//工程内容
     serviceContent:String,//服务内容
-    claimVisa:String//签证及索赔依据
+    claimVisa:String,//签证及索赔依据
+    //经济指标属性
+    economicType:String,//工程经济指标类别
+    quantityIndexType:String,//工程量指标类别
+    quantityIndexUnit:String,//工程量指标单位
+    quantityIndexCoe:Number,//单位转换系数
+    bookmarkBackground:String,//书签背景色
+    bookmarkAnnotation:String,//批注
+    overHeight: String // 超高降效
+
 });
 
-mongoose.model("bills", billsSchema);
+mongoose.model("bills", billsSchema);

+ 1 - 1
modules/all_models/calc_program_model.js

@@ -37,7 +37,7 @@ let stdCalcPrograms = new Schema({
 
 let projectCalcPrograms = new Schema({
     ID: String,
-    projectID: Number,
+    projectID: {type: Number, index: true},
     name: String,
     libID: Number,
     libName: String,

+ 4 - 1
modules/all_models/compleRation_ration.js

@@ -24,7 +24,10 @@ const compleRationAssItemSchema = new Schema({
     decimal: Number,
     carryBit: String,
     minValue: String,
-    maxValue: String
+    maxValue: String,
+    paramName:String,//参数名称
+    param:String,//参数
+    thirdRationCode:String//第三定额
 }, { _id: false });
 
 //定额安装增加费用

+ 74 - 0
modules/all_models/config_material_list.js

@@ -0,0 +1,74 @@
+/**
+ * Created by zhang on 2019/9/6.
+ */
+/**
+ * Created by zhang on 2019/3/18.
+ */
+import mongoose from "mongoose";
+
+let Schema = mongoose.Schema;
+let evaluateSchema = {
+    ID:{type:String,  index: true},
+    projectID: {type:Number,index: true},
+    is_related:{type: Number, default: 0},//关联,1关,0不关
+    projectGLJID:Number,//关联工料机ID
+    seq:String,//序号
+    code:String,
+    name:String,
+    specs:String,
+    unit:String,
+    type:Number,
+    market_price:String,
+    quantity:String,
+    locked:{type: Number, default: 0},//锁定,1锁,0不锁
+    remark:String,
+    originPlace:String,//产地
+    vender:String //厂家
+};
+mongoose.model("evaluate_list", new Schema(evaluateSchema, {versionKey: false, collection: "evaluate_list"}));
+
+let bidEvaluationSchema = {
+    ID:{type:String,  index: true},
+    projectID: {type:Number,index: true},
+    is_related:{type: Number, default: 0},//关联,1关,0不关
+    projectGLJID:Number,//关联工料机ID
+    seq:String,//序号
+    code:String,
+    name:String,
+    specs:String,
+    unit:String,
+    type:Number,
+    market_price:String,
+    quantity:String,
+    remark:String,
+    originPlace:String,//产地
+    vender:String //厂家
+};
+
+mongoose.model("bid_evaluation_list", new Schema(bidEvaluationSchema, {versionKey: false, collection: "bid_evaluation_list"}));
+
+
+let contractorSchema = {
+    ID:{type:String,  index: true},
+    projectID: {type:Number,index: true},
+    is_related:{type: Number, default: 0},//关联,1关,0不关
+    projectGLJID:Number,//关联工料机ID
+    seq:String,//序号
+    code:String,
+    name:String,
+    specs:String,
+    unit:String,
+    type:Number,
+    riskCoe:String,//风险系数
+    standardPrice:String,//基准单价
+    FO:String,//基本价格指数
+    FI:String,//现行价格指数
+    market_price:String,
+    quantity:String,
+    remark:String,
+    supply: {type: Number, default: 0},
+    originPlace:String,//产地
+    vender:String //厂家
+};
+
+mongoose.model("contractor_list", new Schema(contractorSchema, {versionKey: false, collection: "contractor_list"}));

+ 37 - 1
modules/all_models/engineering_lib.js

@@ -57,6 +57,40 @@ let modelSchema = {
         type: Schema.Types.Mixed,
         default: []
     },
+    //基本信息库
+    info_lib: {
+        type: Schema.Types.Mixed,
+        default: []
+    },
+    engineer_info_lib: {
+        type: Schema.Types.Mixed,
+        default: []
+    },
+    //工程特征指标库
+    engineer_feature_lib: {
+        type: Schema.Types.Mixed,
+        default: []
+    },
+    //主要工料指标库
+    material_lib: {
+        type: Schema.Types.Mixed,
+        default: []
+    },
+    //主要工程量指标库
+    main_quantity_lib: {
+        type: Schema.Types.Mixed,
+        default: []
+    },
+    //主要经济指标库
+    economic_lib: {
+        type: Schema.Types.Mixed,
+        default: []
+    },
+    //超高降效库
+    over_height_lib: {
+        type: Schema.Types.Mixed,
+        default: []
+    },
     //设置人材机显示列
     glj_col:{
         showAdjustPrice:Boolean//是否显示调整价列
@@ -72,7 +106,9 @@ let modelSchema = {
     //取费专业
     engineering:Number,
     //是否计算安装增加费
-    isInstall:{type: Boolean, default: false}
+    isInstall:{type: Boolean, default: false},
+    indexName:String,//指标专业名称
+    seq:Number//序列号
 };
 mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
 

+ 3 - 3
modules/all_models/fee_rates.js

@@ -38,7 +38,7 @@ let ratesSchema = new Schema({
 },{versionKey:false,_id: false});
 
 let feeRatesSchema = new Schema({
-    ID: String,
+    ID:  {type: String, index: true},
     rates: [ratesSchema],
     deleteInfo: deleteSchema
 },{versionKey:false});
@@ -47,7 +47,7 @@ mongoose.model('fee_rates', feeRatesSchema, 'fee_rates');
 
 
 let feeRateFileSchema = new Schema({
-    ID: String,
+    ID:  {type: String, index: true},
     rootProjectID:Number,//顶层项目ID
     userID:String,
     name:String,
@@ -62,7 +62,7 @@ mongoose.model('fee_rate_file', feeRateFileSchema, 'fee_rate_file');
 
 
 let feeRatesLibSchema = new Schema({
-    ID: String,
+    ID:  {type: String, index: true},
     region: String,                     // 工程所在地
     libName: String,
     rates: [ratesSchema]

+ 1 - 1
modules/all_models/mix_ratio.js

@@ -23,7 +23,7 @@ let modelSchema = {
         index: true
     },
     // 单价文件表id (因为选择单价文件后配合比数据也需要同步,所以记录单价文件id)
-    unit_price_file_id: Number,
+    unit_price_file_id: {type: Number, index: true},
     // 关联项目工料机的key 不能关联id,因为单价文件导入别的项目后项目工料机id不同
     connect_key: {
         type: String,

+ 17 - 0
modules/all_models/online_logs.js

@@ -0,0 +1,17 @@
+/**
+ * Created by zhang on 2019/4/12.
+ */
+//用户在线时长统计
+
+let mongoose = require("mongoose");
+let Schema = mongoose.Schema;
+
+// 表结构
+let schema = {
+    userID: String,
+    compilationID: String,
+    dateString: String,
+    dateTime:Number,//dateString转换回毫秒数对应的数值,方便查询
+    online_times: Number
+};
+mongoose.model("online_logs", new Schema(schema, {versionKey: false}),"online_logs");

+ 31 - 0
modules/all_models/product.js

@@ -0,0 +1,31 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/3/21
+ * @version
+ */
+/*
+ * 与产品绑定的信息都可以在此设置
+ * */
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const productSchema = new Schema({
+    name: {
+        type: String,
+        default: '纵横建筑计价'
+    },
+    company: {
+        type: String,
+        default: '珠海纵横创新软件有限公司'
+    },
+    icp: {
+        type: String,
+        default: '粤ICP备14032472号'
+    },
+    version: String
+}, {versionKey: false});
+
+mongoose.model('product', productSchema, 'product');

+ 33 - 6
modules/all_models/project_glj.js

@@ -18,7 +18,10 @@ let modelSchema = {
     // 工料机总库ID
     glj_id: Number,
     // 标段ID
-    project_id: Number,
+    project_id: {
+        type: Number,
+        index: true
+    },
     // 编码
     code: {
         type: String,
@@ -32,7 +35,7 @@ let modelSchema = {
     // 名称
     name: {
         type: String,
-        index: true,
+        index: false,
         default: ''
     },
     // 是否暂估 (0为否 1为是)
@@ -65,6 +68,16 @@ let modelSchema = {
         type: Number,
         default: 0
     },
+    //不计税设备
+    no_tax_eqp:{
+        type: Number,
+        default: 0
+    },
+    //评标材料
+    is_eval_material:{
+        type: Number,
+        default: 0
+    },
     // 调整系数ID
     adjCoe: Number,
 
@@ -87,7 +100,6 @@ let modelSchema = {
     quantity: String,
     techQuantity:String,//技术措施项目消耗量
     subdivisionQuantity:String,//分部分项消耗量
-    // 不调价
     tenderPrice: String,//调整后价格
     // 显示组成物的消耗量
     consumption: String,
@@ -95,15 +107,30 @@ let modelSchema = {
     mix_ratio_id: Number,
     // 显示关联父级工料机code(组合物用)
     connect_code: String,
-    materialType: Number,   //三材类别
-    materialCoe: Number,    //三材系数
+    materialType: Number,//三材类别
+    materialCoe: Number,//三材系数
+    //经济指标数据
+    materialIndexType:String,//工料指标类别
+    materialIndexUnit:String,//工料指标单位
+    materialIndexCoe:Number,//单位转换系数
     // 是否主要材料 (0为否 1为是)
     is_main_material: {
         type: Number,
         default: 0
     },
+    is_contractor_material:{type: Number, default: 0},//承包人主要材料设备
+  /*  is_info_adjust:{type: Number, default: 0},//是否造价信息差额调整
+    is_coe_adjust:{type: Number, default: 0},//是否价格指数调整
+    riskCoe:String,//风险系数
+    standardPrice:String,//standardPrice
+    FO:String,//基本价格指数
+    FI:String,//现行价格指数*/
     ratio_data: Schema.Types.Mixed,
-    remark:String
+    remark:String,
+    originPlace:String,//产地
+    vender:String, //厂家
+    qualityGrace:String,//质量等级
+    brand:String//品牌
 };
 mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false}));
 

+ 2 - 1
modules/all_models/projects.js

@@ -14,10 +14,11 @@ const shareSchema = new Schema({
     shareDate: String,
 }, {versionKey: false, _id: false});
 const ProjectSchema = new Schema({
-    "ID": Number,
+    "ID": {type: Number, index: true},
     "ParentID": Number,
     "NextSiblingID": Number,
     "userID": String,
+    "code": {type: String, default: ''},
     "name": String,
     "projType": String,
     "recentDateTime": Date,

+ 15 - 5
modules/all_models/ration.js

@@ -16,14 +16,19 @@ var rationAssItemSchema = mongoose.Schema({
     decimal: Number,
     carryBit: String,
     minValue: String,
-    maxValue: String
+    maxValue: String,
+    paramName:String,//参数名称
+    param:String,//参数
+    thirdRationCode:String,//第三定额
+    isAdjust:Number,//0不调整,1调整
+    groupList:[Schema.Types.Mixed]//当有分组的时候用这个
 }, { _id: false });
 
 // 定额、量价、工料机定额 合并存储
 let rationSchema = new Schema({
     // 公用属性部分
-    ID: String,
-    projectID: Number,
+    ID: {type: String, index: true},
+    projectID: {type: Number, index: true},
     billsItemID: String,
     serialNo: Number,
     code: String,
@@ -68,6 +73,8 @@ let rationSchema = new Schema({
     ruleText: String,                            // 计算规则
     prefix: {type: String, default: ''},                              //定额是补充、借用时用  补 借
     referenceRationID:String,//如果是通过模板关联子目生成的定额,这里记录对应的主定额ID
+    // 工作内容 (选择自清单)
+    jobContentText: String,
 
     //工料机特有属性
     projectGLJID:Number,  //项目工料机ID
@@ -78,7 +85,10 @@ let rationSchema = new Schema({
     customQuantity:String,//自定义消耗
     model: Number,// 机型
     adjCoe:Number,
-    remark:String
+    remark:String,
+    bookmarkBackground:String,//书签背景色
+    bookmarkAnnotation:String,//批注
+    overHeight: String // 超高降效
 });
 
-let ration = mongoose.model("ration", rationSchema, "ration");
+let ration = mongoose.model("ration", rationSchema, "ration");

+ 2 - 2
modules/all_models/ration_coe.js

@@ -24,8 +24,8 @@ var coeListSchema = mongoose.Schema({
     option_codes:String,                //可选人材机编码
     option_list:[Schema.Types.Mixed],//下拉列表选项
     select_code:String,
-    rationID:String,
-    projectID:Number,
+    rationID:{type: String, index: true},
+    projectID:{type: Number, index: true},
     coeID:Number,
     isAdjust:Number, //0不调整,1调整
     seq:Number,//序数,排序用

+ 5 - 2
modules/all_models/ration_glj.js

@@ -5,11 +5,14 @@ var mongoose = require('mongoose'),
     Schema = mongoose.Schema;
 
 var ration_glj = new Schema({
-    ID:String,
+    ID:{
+        type: String,
+        index: true
+    },
     GLJID:Number,
     repositoryId:Number,
     projectID: Number,
-    rationID:String,
+    rationID:{type: String, index: true},
     projectGLJID:Number,
     name:String,
     code:String,

+ 5 - 0
modules/all_models/stdBills_bills.js

@@ -19,6 +19,11 @@ const stdBills_bills = new Schema({
             items: [],
             recharge:String,
             billsLibId: {type:Number, index: true},
+            //经济指标属性
+            economicType:String,//工程经济指标类别
+            quantityIndexType:String,//工程量指标类别
+            quantityIndexUnit:String,//工程量指标单位
+            quantityIndexCoe:Number,//单位转换系数
             deleted: Boolean
     },
     {versionKey: false}

+ 1 - 0
modules/all_models/stdRation_lib.js

@@ -7,6 +7,7 @@ const Schema = mongoose.Schema;
 const RepositoryMapSchema = new Schema({
     "ID": Number,
     "dispName" : String,
+    "libCode": String, //定额库编号
     "appType" : String, //如:"建筑" / "公路"
     "localeType": String, //如 各个省份 / 部颁
     "descr" : String,

+ 5 - 2
modules/all_models/stdRation_ration.js

@@ -19,7 +19,10 @@ const rationAssItemSchema = new Schema({
     decimal: Number,
     carryBit: String,
     minValue: String,
-    maxValue: String
+    maxValue: String,
+    paramName:String,//参数名称
+    param:String,//参数
+    thirdRationCode:String//第三定额
 }, { _id: false });
 
 //定额安装增加费用
@@ -37,7 +40,7 @@ const rationItemSchema = new Schema({
     labourPrice: Number,
     materialPrice: Number,
     machinePrice: Number,
-    sectionId: Number,
+    sectionId: {type: Number,index: true},
     rationRepId: Number,
     caption: String,
     feeType: Number,

+ 25 - 0
modules/all_models/std_economic_lib.js

@@ -0,0 +1,25 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/3/5
+ * @version
+ */
+//主要工程量指标库
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const oprSchema = require('../all_schemas/opr_schema');
+const economicLib = new Schema({
+    ID: {type: String, index: true},
+    name: String,
+    creator: String,
+    createDate: Number,
+    recentOpr: [oprSchema],
+    index: {
+        type: Schema.Types.Mixed,
+        default: []
+    }
+}, {versionKey: false});
+mongoose.model('std_economic_lib', economicLib, 'std_economic_lib');

+ 25 - 0
modules/all_models/std_engineer_feature_lib.js

@@ -0,0 +1,25 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/3/5
+ * @version
+ */
+//建设项目基本信息库
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const oprSchema = require('../all_schemas/opr_schema');
+const engineerFeatureLib = new Schema({
+    ID: {type: String, index: true},
+    name: String,
+    creator: String,
+    createDate: Number,
+    recentOpr: [oprSchema],
+    features: {
+        type: Schema.Types.Mixed,
+        default: []
+    }
+}, {versionKey: false});
+mongoose.model('std_engineer_feature_lib', engineerFeatureLib, 'std_engineer_feature_lib');

+ 25 - 0
modules/all_models/std_engineer_info_lib.js

@@ -0,0 +1,25 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/3/5
+ * @version
+ */
+//建设工程信息指标库
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const oprSchema = require('../all_schemas/opr_schema');
+const engineerInfoLib = new Schema({
+    ID: {type: String, index: true},
+    name: String,
+    creator: String,
+    createDate: Number,
+    recentOpr: [oprSchema],
+    info: {
+        type: Schema.Types.Mixed,
+        default: []
+    }
+}, {versionKey: false});
+mongoose.model('std_engineer_info_lib', engineerInfoLib, 'std_engineer_info_lib');

+ 5 - 1
modules/all_models/std_glj.js

@@ -20,7 +20,7 @@ const std_gljComponent = new Schema(
 
 const std_glj = new Schema({
     deleted: Boolean,
-    repositoryId: Number,
+    repositoryId: {type: Number,index: true},
     ID: Number,
     code: String,
     name: String,
@@ -38,6 +38,10 @@ const std_glj = new Schema({
     adjCoe: Number,
     materialType: Number,   //三材类别
     materialCoe: Number,    //三材系数
+    //经济指标数据
+    materialIndexType:String,//工料指标类别
+    materialIndexUnit:String,//工料指标单位
+    materialIndexCoe:Number,//单位转换系数
     component: [std_gljComponent]
 },{versionKey: false});
 

+ 25 - 0
modules/all_models/std_main_quantity_lib.js

@@ -0,0 +1,25 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/3/5
+ * @version
+ */
+//主要工程量指标库
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const oprSchema = require('../all_schemas/opr_schema');
+const quantityLib = new Schema({
+    ID: {type: String, index: true},
+    name: String,
+    creator: String,
+    createDate: Number,
+    recentOpr: [oprSchema],
+    index: {
+        type: Schema.Types.Mixed,
+        default: []
+    }
+}, {versionKey: false});
+mongoose.model('std_main_quantity_lib', quantityLib, 'std_main_quantity_lib');

+ 25 - 0
modules/all_models/std_material_lib.js

@@ -0,0 +1,25 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/3/5
+ * @version
+ */
+//建设项目基本信息库
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const oprSchema = require('../all_schemas/opr_schema');
+const materialLib = new Schema({
+    ID: {type: String, index: true},
+    name: String,
+    creator: String,
+    createDate: Number,
+    recentOpr: [oprSchema],
+    materials: {
+        type: Schema.Types.Mixed,
+        default: []
+    }
+}, {versionKey: false});
+mongoose.model('std_material_lib', materialLib, 'std_material_lib');

+ 25 - 0
modules/all_models/std_over_height_lib.js

@@ -0,0 +1,25 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/3/5
+ * @version
+ */
+//建设项目基本信息库
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const oprSchema = require('../all_schemas/opr_schema');
+const overHeightLib = new Schema({
+    ID: {type: String, index: true},
+    name: String,
+    creator: String,
+    createDate: Number,
+    recentOpr: [oprSchema],
+    list: {
+        type: Schema.Types.Mixed,
+        default: []
+    }
+}, {versionKey: false});
+mongoose.model('std_over_height_lib', overHeightLib, 'std_over_height_lib');

+ 2 - 2
modules/all_models/unit_price.js

@@ -11,7 +11,7 @@ let Schema = mongoose.Schema;
 let collectionName = 'unit_price';
 let modelSchema = {
     // 自增ID
-    id: {type:Number,unique: true},
+    id: {type:Number,unique: true, index: true},
     // 基价单价
     base_price: String,
     // 市场单价
@@ -43,7 +43,7 @@ let modelSchema = {
     // 类型简称
     short_name: String,
     // 单价文件表id
-    unit_price_file_id: Number,
+    unit_price_file_id: {type: Number, index: true},
     // 对应标准库工料机id
     glj_id: Number,
     //是否新增1为是,0为否

+ 13 - 0
modules/all_models/user.js

@@ -83,6 +83,19 @@ let schema = {
     used_list: {
         type: [userdList],
         default: []
+    },
+    // 是否邮箱已通过验证
+    isUserActive: Number,
+    // 是否只允许短信登录
+    isSmsLogin: {
+        type: Number,
+        default: 0
+    },
+    // 登录异常短信通知
+    isLoginValid: {
+        type: Number,
+        default: 0
     }
+
 };
 mongoose.model(collectionName, new Schema(schema, {versionKey: false}));

+ 2 - 2
modules/bills_lib/models/bills_lib_interfaces.js

@@ -1791,7 +1791,7 @@ billsLibDao.prototype.deleteBills = function(delData, callback){
 
 billsLibDao.prototype.getJobContent = function(gJobData, callback){
     let billsLibId = gJobData.billsLibId;
-    JobContent.find({billsLibId: billsLibId, deleted: false}, '-_id').sort({code: 1}).exec(function(err, result){
+    JobContent.find({billsLibId: billsLibId, deleted: false}, '-_id').lean().sort({code: 1}).exec(function(err, result){
         if(err){
             callback(1, 'Error', null);
         }
@@ -2249,7 +2249,7 @@ billsLibDao.prototype.edUpdateJob = function(data, callback){
 //----------------------ItemCharacter---------------------
 billsLibDao.prototype.getItemCharacter = function(gdata, callback){
     let billsLibId = gdata.billsLibId;
-    ItemCharacter.find({billsLibId: billsLibId, deleted: false}, '-_id').sort({code: 1}).exec(function(err, result){
+    ItemCharacter.find({billsLibId: billsLibId}, '-_id').lean().sort({code: 1}).exec(function(err, result){
         if(err){
             callback(1, 'Error', null);
         }

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

@@ -131,7 +131,8 @@ class BaseModel {
 
         return result.ok !== undefined && result.ok === 1;
     }
-    getIndex(obj,pops){
+    getIndex(obj,tpops){
+        let pops = tpops?tpops:['code','name','specs','unit','type'];
         let t_index = '';
         let k_arr=[];
         for(let p of pops){

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

@@ -41,7 +41,32 @@ const fixedFlag = {
     //工程造价
     ENGINEERINGCOST: 19,
     //增值税
-    ADDED_VALUE_TAX: 20
+    ADDED_VALUE_TAX: 20,
+    //专项技术措施暂估价
+    SPECIAL_TECH_PROVISIONAL: 21,
+    //专业发包工程管理费
+    LET_CONTRACT_MANAGEMENT: 22,
+    //人工
+    LABOUR: 23,
+    //材料
+    MATERIAL: 24,
+    //施工机械
+    MACHINE: 25,
+    //索赔
+    CLAIM: 26,
+    //现场签证
+    VISA: 27,
+    ADDITIONAL_TAX: 28,
+    //环境保护税
+    ENVIRONMENTAL_PROTECTION_TAX: 29,
+    //建设工程竣工档案编制费
+    PROJECT_COMPLETE_ARCH_FEE:30,
+    //住宅工程质量分户验收费
+    HOUSE_QUALITY_ACCEPT_FEE:31,
+    //组织措施费
+    ORGANIZATION:32,
+    //其他措施费
+    OTHER_MEASURE_FEE:33
 };
 
 export default fixedFlag;

+ 14 - 7
modules/complementary_glj_lib/controllers/gljController.js

@@ -40,7 +40,7 @@ class GljController extends BaseController{
             gljLibId: gljLibId,
             compilationId: sessionCompilation._id,
             compilationName: sessionCompilation.name,
-            versionName: `纵横建筑云计价(${req.session.compilationVersion})`,
+            versionName: req.session.compilationVersion,
             LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
             overWriteUrl: overWriteUrl,
         });
@@ -101,12 +101,19 @@ class GljController extends BaseController{
     }
     getGljItems(req, res) {
         let data = JSON.parse(req.body.data);
-        let stdGljLibId = data.stdGljLibId,
-            userId = data.userId,
-            compilationId = data.compilationId
-            gljDao.getGljItems(stdGljLibId, req.session.sessionUser.id, req.session.sessionCompilation._id, function(err, data){
-                callback(req,res,err,'Get Items',data)
-            });
+        let stdGljLibId = data.stdGljLibId;
+        let projection = data.projection;
+        gljDao.getGljItems(stdGljLibId, req.session.sessionUser.id, req.session.sessionCompilation._id, projection, function(err, data){
+            callback(req,res,err,'Get Items',data)
+        });
+    }
+    getStdItems(req, res) {
+        let data = JSON.parse(req.body.data),
+            stdGljLibId = data.stdGljLibId,
+            projection = data.projection;
+        gljDao.getStdItems(stdGljLibId, projection, function (err, data) {
+            callback(req, res, err, '获取人材机数据失败', data);
+        });
     }
 
     updateComponent(req, res){

+ 110 - 58
modules/complementary_glj_lib/models/gljModel.js

@@ -6,9 +6,14 @@ const complementaryGljModel = mongoose.model('complementary_glj_lib');
 const stdGljModel = mongoose.model('std_glj_lib_gljList');
 const gljClassModel = mongoose.model('std_glj_lib_gljClass');
 const compleClassModel = mongoose.model('complementary_glj_section');
+const _ = require('lodash');
 import counter from "../../../public/counter/counter";
 import async from "async";
 import STDGLJLibGLJListModel from "../../common/std/std_glj_lib_glj_list_model";
+const libType = {
+    stdGLJ: 1,
+    complementaryGLJs: 2
+};
 
 class GljDao {
     getGljTypes (gljLibId, callback){
@@ -40,71 +45,109 @@ class GljDao {
             }
         }
     }
+
+    async getQueryByType ({userID, compilationId, gljLibId, code, type, replace, location, classList, search}) {
+        let model = null,
+            query = {},
+            countQuery = {},
+            matchLocation = false;
+        if (type === libType.stdGLJ) {
+            model = stdGljModel;
+            query.repositoryId = gljLibId;
+        } else {
+            model = complementaryGljModel;
+            query.userId = userID;
+            query.compilationId = compilationId;
+        }
+        // 分类树
+        if (classList.length) {
+            query.gljClass = {$in: classList};
+        }
+        // 定位
+        if (location) {
+            // 替换定位,则先看看能不能找到替换的人材机,能找到的话就能定位要被替换人材机所在分类树
+            const replaceQuery = {...query, ...replace};
+            const replaceData = await model.findOne(replaceQuery);
+            if (replaceData) {
+                query.gljClass = replaceData.gljClass;
+                matchLocation = true;
+            }
+        }
+        // 替换过滤类型
+        if (replace) {
+            // 人材机类型是“混凝土、砂浆、配合比、商品混凝土、商品砂浆”时,筛选的可替换的人材机类型应是“混凝土、或砂浆、或配合比、或商品混凝土、或商品砂浆”。
+            const materialTypes = [202, 203, 204, 205, 206];
+            query.gljType = materialTypes.includes(replace.gljType) ? {$in: materialTypes} : replace.gljType;
+        }
+        // 搜索关键字
+        if (search) {
+            query.$or = [{code: {'$regex': search, $options: '$i'}}, {name: {'$regex': search, $options: '$i'}}];
+        }
+        countQuery = {...query};
+        // 上一次分页的最末人材机编码
+        if (code) {
+            query.code = {$gt: code};
+        }
+        return {
+            model,
+            query,
+            countQuery,
+            matchLocation
+        };
+    }
+
+    async getGLJPaging (condition) {
+        const queryData = await this.getQueryByType(condition);
+        // 定位(替换初始化)
+        // 替换触发的人材机选择界面,只有在初始化时才定位,其他操作下是正常的分页
+        if (queryData.matchLocation) {
+            // 返回的数据,为编码小于等于替换的编码,再加附加条数
+            const limitQuery = _.cloneDeep(queryData.query);
+            limitQuery.code = {$lte: condition.replace.code};
+            const lteLimit = await queryData.model.count(limitQuery);
+            const additionalLimit = 10;
+            const limit = lteLimit + additionalLimit;
+            // 显示的数据不能太少,否则数据没有占满整个表格区域(实际上数据条目应该超过表格显示区域),让人误以为数据只有那么点,不再滚动触发分页
+            condition.limit = limit > condition.limit ? limit : condition.limit;
+        }
+        //const gljs = await queryData.model.find(queryData.query).lean().sort({code: 1}).skip(condition.index).limit(condition.limit);
+        // 分页使用skip的话,跳过的条数多的话性能会下降,因此不使用
+        const gljs = await queryData.model.find(queryData.query).lean().sort({code: 1}).limit(condition.limit);
+        const total = await queryData.model.find(queryData.countQuery).count();
+        return condition.type === libType.stdGLJ
+            ? {stdGLJ: gljs, complementaryGLJs: [], total: total}
+            : {stdGLJ: [], complementaryGLJs: gljs, total: total}
+    }
+
+    async getGLJDataSync (gljLibId, userId, compilationId) {
+        const stdGljs = await stdGljModel.find({repositoryId: gljLibId}).lean();
+        const complementaryGljs = await complementaryGljModel.find({userId, compilationId}).lean();
+        return {
+            stdGljs,
+            complementaryGljs
+        }
+    }
+
     //获得用户的补充工料机和用户当前所在编办的标准工料机
-    async getGljItems (stdGljLibId, userId, compilationId, callback){
+    async getGljItems (stdGljLibId, userId, compilationId, projection, callback){
         let me = this;
         let rst = {stdGljs: [], complementaryGljs: []};
         //批量获取异步
-       /* let functions = [];
-        let count = await stdGljModel.find({repositoryId: stdGljLibId, $or: [{deleted: null}, {deleted: false}]}).count();
-        let findCount = Math.ceil(count/500);
-        for(let i = 0, len = findCount; i < len; i++){
-            functions.push((function(flag) {
-                return function (cb) {
-                    stdGljModel.find({repositoryId: stdGljLibId, deleted: null}, cb).skip(flag).sort({ID: 1}).limit(500);
+        async.parallel([
+            async function (cb) {
+                try{
+                    let stdGljs = await stdGljModel.find({repositoryId: stdGljLibId}, projection).lean();
+                    me.sortToNumber(stdGljs);
+                    rst.stdGljs = stdGljs;
+                    cb(null);
                 }
-            })(i*500));
-        }
-        async.parallel(functions,  function (err, results) {
-            if(err){
-                callback(err, null);
-            }
-            else{
-                for(let stdGljs of results){
-                    rst.stdGljs = rst.stdGljs.concat(stdGljs);
+                catch (err){
+                    cb(err);
                 }
-                callback(0, rst);
-            }
-        });*/
-        async.parallel([
-           async function (cb) {
-               try{
-                   let stdGljs = [];
-                   let first = await stdGljModel.find({repositoryId: stdGljLibId}).sort({ID: 1}).limit(1);
-                   let count = await stdGljModel.find({repositoryId: stdGljLibId, $or: [{deleted: null}, {deleted: false}]}).count();
-                   let findCount = Math.ceil(count/500);
-                   let flag = first[0].ID;
-                   //let flag = 0;
-                   //批量获取,非skip
-                   for(let i = 0, len = findCount; i < len; i++){
-                       let tempStdGlj;
-                       if(i === 0){
-                           tempStdGlj = await stdGljModel.find({repositoryId: stdGljLibId, deleted: null, ID: {$gte: flag}}).sort({ID: 1}).limit(500);
-                           if(tempStdGlj.length > 0){
-                               flag = tempStdGlj[tempStdGlj.length - 1].ID;
-                           }
-                       }
-                       else {
-                           tempStdGlj = await stdGljModel.find({repositoryId: stdGljLibId, deleted: null, ID: {$gt: flag}}).sort({ID: 1}).limit(500);
-                           if(tempStdGlj.length > 0){
-                               flag = tempStdGlj[tempStdGlj.length - 1].ID;
-                           }
-                       }
-                       if(tempStdGlj){
-                           stdGljs = stdGljs.concat(tempStdGlj);
-                       }
-                   }
-                   me.sortToNumber(stdGljs);
-                   rst.stdGljs = stdGljs;
-                   cb(null);
-               }
-               catch (err){
-                   cb(err);
-               }
 
             },
             function (cb) {
-                complementaryGljModel.find({userId: userId, compilationId: compilationId}, function (err, complementaryGljs) {
+                complementaryGljModel.find({userId: userId, compilationId: compilationId}, '-_id', {lean: true}, function (err, complementaryGljs) {
                     if(err){
                         cb(err);
                     }
@@ -126,6 +169,15 @@ class GljDao {
 
     };
 
+    async getStdItems (stdGljLibId, projection, callback) {
+        try {
+            let stdItems = await stdGljModel.find({repositoryId: stdGljLibId}, projection).lean();
+            callback(0, stdItems);
+        } catch (err) {
+            callback(1, null);
+        }
+    }
+
     getGljItemsByCode (repositoryId, codes, callback){
         gljModel.find({"repositoryId": repositoryId,"code": {"$in": codes}},function(err,data){
             if(err) callback(true, "")
@@ -339,8 +391,8 @@ class GljDao {
 
     async getMixedTree(gljLibId, userId, compilationId){
         let rst = {std: [], comple: []};
-        rst.std = await gljClassModel.find({repositoryId: gljLibId});
-        rst.comple = await compleClassModel.find({userId: userId, compilationId: compilationId});
+        rst.std = await gljClassModel.find({repositoryId: gljLibId}).lean();
+        rst.comple = await compleClassModel.find({userId: userId, compilationId: compilationId}).lean();
         return rst;
     }
 }

+ 1 - 0
modules/complementary_glj_lib/routes/routes.js

@@ -21,6 +21,7 @@ module.exports = function (app) {
     router.post("/getGljDistType", gljController.init, gljController.getGljDistType);
     router.post("/getGljTree", gljController.init, gljController.getGljTree);
     router.post("/getGljItems", gljController.init, gljController.getGljItems);
+    router.post('/getStdItems', gljController.init, gljController.getStdItems);
     router.post("/updateComponent", gljController.init, gljController.updateComponent);
     router.post("/mixUpdateGljItems", gljController.init, gljController.mixUpdateGljItems);
     router.post("/updateRationBasePrc",compleRationController.init, compleRationController.updateRationBasePrc);//更新定额单价

+ 18 - 5
modules/complementary_ration_lib/controllers/compleViewController.js

@@ -6,8 +6,9 @@ import BaseController from '../../common/base/base_controller';
 import CompleViewModel from '../models/compleViewModel';
 import EngineeringLibModel from "../../users/models/engineering_lib_model";
 let config = require("../../../config/config.js");
-
 let compleViewModel = new CompleViewModel();
+import CompleRationDao from '../models/compleRationModel';
+const compleRationDao = new CompleRationDao();
 let callback = function (req, res, err, msg, data) {
     res.json({error: err, message: msg, data: data});
 };
@@ -48,11 +49,23 @@ class CompleViewController extends BaseController{
             redirectInstallation: redirectInstallation,
             gljLibId: gljLibId,
             compilationName: req.session.sessionCompilation.name,
-            versionName: `纵横建筑云计价(${req.session.compilationVersion})`,
+            versionName: req.session.compilationVersion,
             LicenseKey:config.getLicenseKey(process.env.NODE_ENV)
         });
     }
 
+    async prepareInitData (req, res) {
+        try {
+            const gljLibId = await getGljLibId(req.session.sessionCompilation);
+            const userId = req.session.sessionUser.id;
+            const compilationId = req.session.sessionCompilation._id;
+            const initData = await compleRationDao.prepareInitData(userId, compilationId, gljLibId);
+            res.json({error: 0, message: 'success', data: initData});
+        } catch (err) {
+            res.json({error: 1, message: 'fail', data: null});
+        }
+    }
+
     async redirectGljList(req, res){
         const gljLibId = await getGljLibId(req.session.sessionCompilation);
         const redirectRation = `/complementaryRation/ration`;
@@ -65,7 +78,7 @@ class CompleViewController extends BaseController{
             redirectInstallation: redirectInstallation,
             gljLibId: gljLibId,
             compilationName: req.session.sessionCompilation.name,
-            versionName: `纵横建筑云计价(${req.session.compilationVersion})`,
+            versionName: req.session.compilationVersion,
             LicenseKey:config.getLicenseKey(process.env.NODE_ENV)
         });
     }
@@ -82,7 +95,7 @@ class CompleViewController extends BaseController{
             redirectInstallation: redirectInstallation,
             gljLibId: gljLibId,
             compilationName: req.session.sessionCompilation.name,
-            versionName: `纵横建筑云计价(${req.session.compilationVersion})`,
+            versionName: req.session.compilationVersion,
             LicenseKey:config.getLicenseKey(process.env.NODE_ENV)
         });
     }
@@ -98,7 +111,7 @@ class CompleViewController extends BaseController{
             redirectGlj: redirectGlj,
             redirectCoe: redirectCoe,
             compilationName: req.session.sessionCompilation.name,
-            versionName: `纵横建筑云计价(${req.session.compilationVersion})`,
+            versionName: req.session.compilationVersion,
             LicenseKey:config.getLicenseKey(process.env.NODE_ENV)
         });
     }

+ 52 - 54
modules/complementary_ration_lib/models/compleRationModel.js

@@ -8,10 +8,17 @@ const installSectionModel = mongoose.model("std_ration_lib_installationSection")
 const installFeeItemModel = mongoose.model("std_ration_lib_installation");
 const complementaryGljModel = mongoose.model('complementary_glj_lib');
 const stdGljModel = mongoose.model('std_glj_lib_gljList');
+const stdgljutil = require('../../../public/cache/std_glj_type_util');
+const installFacade = require('../facades/compleInstallFacade');
 import async from 'async';
 let stdRationModel = require ('../../ration_repository/models/ration_item').Model;
+import SectionTreeDao from '../models/sectionTreeModel';
+const sectionTreeDao = new SectionTreeDao();
+import GljDao from "../../complementary_glj_lib/models/gljModel";
+const gljDao = new GljDao();
 let counter = require('../../../public/counter/counter');
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
+let gljUtil = require('../../../public/gljUtil');
 
 class CompleRatoinDao {
     async updateRation(userID, compilationId, updateData, callback){
@@ -152,32 +159,24 @@ class CompleRatoinDao {
                 }
             }
             if(stdGljIds.length > 0) {
-                stdGljs = await stdGljModel.find({ID: {$in: stdGljIds}});
+                stdGljs = await stdGljModel.find({ID: {$in: stdGljIds}}).lean();
             }
             if(comGljIds.length > 0) {
-                comGljs = await complementaryGljModel.find({userId: userId, ID: {$in: comGljIds}});
+                comGljs = await complementaryGljModel.find({userId: userId, ID: {$in: comGljIds}}).lean();
             }
-            let gljDatas = stdGljs.concat(comGljs);
-            gljDatas.sort(function (a, b) {
-                let aV = a.gljType + a.code,
-                    bV = b.gljType + b.code;
-                if(aV > bV) {
-                    return 1;
-                } else if (aV < bV) {
-                    return -1;
-                }
-                return 0;
-            });
-            for(let glj of gljDatas){
-                hintsArr.push(` ${glj.code} ${glj.name}${glj.specs ? '&nbsp;&nbsp;&nbsp;' + glj.specs : ''}&nbsp;&nbsp&nbsp;${glj.unit}&nbsp;&nbsp;&nbsp;${gljAmtMapping[glj.ID]}`)
-            }
-            hintsArr.push(`基价 元 ${ration.basePrice}`);
+            let gljDatas = gljUtil.sortRationGLJ(stdGljs.concat(comGljs),true);
             if(ration.jobContent && ration.jobContent.toString().trim() !== ''){
-                hintsArr.push(`工作内容:`);
+                hintsArr.push(`<label class="nomargin font_blue">工作内容:`);
                 hintsArr = hintsArr.concat(ration.jobContent.split('\n'));
+                hintsArr.push("</label>");
+                hintsArr.push("");
             }
+            for(let glj of gljDatas){
+                hintsArr.push(`<label class="nomargin ${glj.gljType==4?"font_blue":""}"> ${glj.code} ${glj.name}${glj.specs ? '&nbsp;&nbsp;&nbsp;' + glj.specs : ''}&nbsp;&nbsp&nbsp;${glj.unit}&nbsp;&nbsp;&nbsp;${gljAmtMapping[glj.ID]}</label>`)
+            }
+            hintsArr.push(`基价 元 ${ration.basePrice}`);
             if(ration.annotation && ration.annotation.toString().trim() !== ''){
-                hintsArr.push(`附注:`);
+                hintsArr.push(`<br>附注:`);
                 hintsArr = hintsArr.concat(ration.annotation.split('\n'));
             }
             ration._doc.hint = hintsArr.join('<br>');
@@ -241,29 +240,24 @@ class CompleRatoinDao {
                 stdGljIds.push(rationGlj.gljId);
             }
             if(stdGljIds.length > 0) {
-                stdGljs = await stdGljModel.find({ID: {$in: stdGljIds}});
-            }
-            let gljDatas = stdGljs;
-            gljDatas.sort(function (a, b) {
-                let aV = a.gljType + a.code,
-                    bV = b.gljType + b.code;
-                if(aV > bV) {
-                    return 1;
-                } else if (aV < bV) {
-                    return -1;
-                }
-                return 0;
-            });
-            for(let glj of gljDatas){
-                hintsArr.push(` ${glj.code} ${glj.name}${glj.specs ? '&nbsp;&nbsp;&nbsp;' + glj.specs : ''}&nbsp;&nbsp&nbsp;${glj.unit}&nbsp;&nbsp;&nbsp;${gljAmtMapping[glj.ID]}`)
+                stdGljs = await stdGljModel.find({ID: {$in: stdGljIds}}).lean();
+                /*stdGljs.forEach(function (glj) {
+                    glj.type = glj.gljType;
+                });*/
             }
-            hintsArr.push(`基价 元 ${ration.basePrice}`);
+            let gljDatas =  gljUtil.sortRationGLJ(stdGljs,true);
             if(ration.jobContent && ration.jobContent.toString().trim() !== ''){
-                hintsArr.push(`工作内容:`);
+                hintsArr.push(`<label class="nomargin font_blue">工作内容:`);
                 hintsArr = hintsArr.concat(ration.jobContent.split('\n'));
+                hintsArr.push("</label>");
+                hintsArr.push("");
             }
+            for(let glj of gljDatas){
+                hintsArr.push(`<label class="nomargin ${glj.gljType==4?"font_blue":""}"> ${glj.code} ${glj.name}${glj.specs ? '&nbsp;&nbsp;&nbsp;' + glj.specs : ''}&nbsp;&nbsp&nbsp;${glj.unit}&nbsp;&nbsp;&nbsp;${gljAmtMapping[glj.ID]}</label>`)
+            }
+            hintsArr.push(`基价 元 ${ration.basePrice}`);
             if(ration.annotation && ration.annotation.toString().trim() !== ''){
-                hintsArr.push(`附注:`);
+                hintsArr.push(`<br>附注:`);
                 hintsArr = hintsArr.concat(ration.annotation.split('\n'));
             }
             ration._doc.hint = hintsArr.join('<br>');
@@ -522,23 +516,27 @@ class CompleRatoinDao {
         });
     }
 
-    async getInstallation(rationRepId, callback){
-        try {
-            let feeItems = await installFeeItemModel.find({rationRepId: rationRepId, $or: [{deleted: false}, {deleted: null}]});
-            for(let feeItem of feeItems){
-                let sids = [];
-                for(let sec of feeItem.section){
-                    sids.push(sec.ID);
-                }
-                if(sids.length > 0){
-                    let sections = await installSectionModel.find({ID: {$in: sids}, $or: [{deleted: false}, {deleted: null}]});
-                    feeItem._doc.section = sections;
-                }
-            }
-            callback(0, feeItems);
-        }
-        catch(err){
-            callback(err, null);
+    async getCodes (userId, compilationId) {
+        const compleRations = await compleRationModel.find({userId, compilationId}, '-_id code').lean();
+        const codes = [];
+        compleRations.forEach(item => codes.push(item.code));
+        return codes;
+    }
+
+    async prepareInitData (userId, compilationId, gljLibId) {
+        const rationsCodes = await this.getCodes(userId, compilationId);
+        const gljDistTypeCache = stdgljutil.getStdGljTypeCacheObj().toArray();
+        const installationData = await installFacade.getInstallation(userId, compilationId);
+        const rationTreeData = await sectionTreeDao.getComplementaryTree(userId, compilationId);
+        const mixedTreeData = await gljDao.getMixedTree(gljLibId, userId, compilationId);
+        const mixedGLJData = await gljDao.getGLJDataSync(gljLibId, userId, compilationId);
+        return {
+            rationsCodes,
+            gljDistTypeCache,
+            installationData,
+            rationTreeData,
+            mixedTreeData,
+            mixedGLJData
         }
     }
 }

+ 65 - 32
modules/complementary_ration_lib/models/searchModel.js

@@ -8,34 +8,38 @@ const stdGljModel = mongoose.model('std_glj_lib_gljList');
 const compleRationSectionTreeModel = mongoose.model('complementary_ration_section_tree');
 let stdSectionTreeModel = require ('../../ration_repository/models/ration_section_tree').Model;
 let stdRationModel = require ('../../ration_repository/models/ration_item').Model;
+let gljUtil = require('../../../public/gljUtil');
 
 const compleRationLib = 'compleRationLib';
 
 class SearchDao{
     async getRationItem(userId, compilationId, rationRepIds, code, ID, callback){
         let ration = null;
+        let otherLibs=[];
         try{
-            if(rationRepIds.includes(compleRationLib)) {
-                rationRepIds.splice(rationRepIds.indexOf(compleRationLib), 1);
-            }
-            let stdQuery = {rationRepId: {$in: rationRepIds}, code: code, $or: [{isDeleted: null}, {isDeleted: false}]};
-            if(ID){
-                stdQuery = {ID: ID, $or: [{isDeleted: null}, {isDeleted: false}]};
+            let firstLib = rationRepIds[0];//优先取第一个
+            for (let l of rationRepIds){
+                if(l != firstLib && l != compleRationLib){
+                    otherLibs.push(l);
+                }
             }
-            let stdRation = await stdRationModel.findOne(stdQuery);
-            if(isDef(stdRation)){
-                ration = stdRation._doc;
-                ration.type = 'std';
-            } else{
-                let compleQuery = {userId: userId, compilationId: compilationId, code: code, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]};
+            if(firstLib == compleRationLib){//说明选中的是补充定额库
+                ration = await this.getCompleRation(userId,compilationId,code,ID);
+            }else {
+                firstLib = parseInt(firstLib);
+                let firstQuery = {rationRepId: firstLib, code: code, $or: [{isDeleted: null}, {isDeleted: false}]};
                 if(ID){
-                    compleQuery.ID = ID;
+                    firstQuery = {ID: ID, $or: [{isDeleted: null}, {isDeleted: false}]};
                 }
-                let compleRation = await compleRationModel.findOne(compleQuery);
-                if(isDef(compleRation)){
-                    ration = compleRation._doc;
-                    ration.type = 'complementary';
+                ration = await this.getStdRation(firstQuery);
+            }
+            if(ration == null){//选中的定额库或者默认的定额库中没有找到定额,才走常规的流程查找其它定额库
+                let stdQuery = {rationRepId: {$in: otherLibs}, code: code, $or: [{isDeleted: null}, {isDeleted: false}]};
+                if(ID){
+                    stdQuery = {ID: ID, $or: [{isDeleted: null}, {isDeleted: false}]};
                 }
+                ration = await this.getStdRation(stdQuery);
+                if(ration == null) ration = await this.getCompleRation(userId,compilationId,code,ID);
             }
             if(isDef(ration)){
                 if (ration.type === 'std') {
@@ -61,7 +65,28 @@ class SearchDao{
         }
         return ration;
     }
-
+    async getCompleRation(userId,compilationId,code,ID){
+        let ration = null;
+        let compleQuery = {userId: userId, compilationId: compilationId, code: code, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]};
+        if(ID){
+            compleQuery.ID = ID;
+        }
+        let compleRation = await compleRationModel.findOne(compleQuery);
+        if(isDef(compleRation)){
+            ration = compleRation._doc;
+            ration.type = 'complementary';
+        }
+        return ration
+    }
+    async getStdRation(query){
+        let ration = null;
+        let stdRation = await stdRationModel.findOne(query);
+        if(isDef(stdRation)){
+            ration = stdRation._doc;
+            ration.type = 'std';
+        }
+        return ration;
+    }
     //@param {Object}skip({std: Number, comple: Number})
     async findRation(userId, compilationId, rationRepId, keyword, skip, callback){
         //每次限制结果数
@@ -102,9 +127,9 @@ class SearchDao{
             //搜索定额
             let stdGljIds = [],
                 comGljIds = [];
-            let stdRations = rationRepId.length === 0 ? [] : await stdRationModel.find(filter).sort({code: 1}).skip(skip.std).limit(limit);
+            let stdRations = rationRepId.length === 0 ? [] : await stdRationModel.find(filter).lean().sort({code: 1}).skip(skip.std).limit(limit);
             for(let i = 0, len = stdRations.length; i < len; i++){
-                stdRations[i]._doc.type = 'std';
+                stdRations[i].type = 'std';
                 for(let glj of stdRations[i].rationGljList){
                     stdGljIds.push(glj.gljId);
                 }
@@ -112,9 +137,9 @@ class SearchDao{
             let compleRations = [];
             let residueLimit = limit - stdRations.length;
             if (residueLimit > 0) {
-                compleRations = findCompleRtion ? await compleRationModel.find(compleFilter).sort({code: 1}).skip(skip.comple).limit(residueLimit) : [];
+                compleRations = findCompleRtion ? await compleRationModel.find(compleFilter).lean().sort({code: 1}).skip(skip.comple).limit(residueLimit) : [];
                 for(let i = 0, len = compleRations.length; i <len; i++){
-                    compleRations[i]._doc.type = 'complementary';
+                    compleRations[i].type = 'complementary';
                     for(let glj of compleRations[i].rationGljList){
                         if(glj.type === 'std'){
                             stdGljIds.push(glj.gljId);
@@ -147,10 +172,13 @@ class SearchDao{
                 //对人材机进行排序
                 ration.rationGljList.sort(function (a, b) {
                     let gljA = gljIDMapping[a.gljId],
-                        gljB = gljIDMapping[b.gljId];
+                        gljB = gljIDMapping[b.gljId],
+                        seqs = gljUtil.getGljTypeSeq() ;
                     if(gljA && gljB){
-                        let aV = gljA.gljType + gljA.code,
-                            bV = gljB.gljType + gljB.code;
+                        let indA = seqs.indexOf(gljA.gljType)+"",
+                            indB = seqs.indexOf(gljB.gljType)+"";
+                        let aV = indA+gljA.gljType + gljA.code,
+                            bV = indB+gljB.gljType + gljB.code;
                         if(aV > bV) {
                             return 1;
                         } else if(aV < bV) {
@@ -159,22 +187,27 @@ class SearchDao{
                     }
                     return 0;
                 });
+
+                if(ration.jobContent && ration.jobContent.toString().trim() !== ''){
+                    hintsArr.push(`<label class="nomargin font_blue">工作内容:`);
+                    hintsArr = hintsArr.concat(ration.jobContent.split('\n'));
+                    hintsArr.push("</label>");
+                    hintsArr.push("");
+                }
+
                 for(let rationGlj of ration.rationGljList){
                     let glj = gljIDMapping[rationGlj.gljId];
                     if(glj){
-                        hintsArr.push(` ${glj.code} ${glj.name}${glj.specs ? ' ' + glj.specs : ''} ${glj.unit} ${rationGlj.consumeAmt}`);
+                        hintsArr.push(`<label class="nomargin ${glj.gljType==4?"font_blue":""}"> ${glj.code} ${glj.name}${glj.specs ? '&nbsp;&nbsp;&nbsp;' + glj.specs : ''}&nbsp;&nbsp&nbsp;${glj.unit}&nbsp;&nbsp;&nbsp;${rationGlj.consumeAmt}</label>`)
+                        //hintsArr.push(` ${glj.code} ${glj.name}${glj.specs ? ' ' + glj.specs : ''} ${glj.unit} ${rationGlj.consumeAmt}`);
                     }
                 }
                 hintsArr.push(`基价 元 ${ration.basePrice}`);
-                if(ration.jobContent && ration.jobContent.toString().trim() !== ''){
-                    hintsArr.push(`工作内容:`);
-                    hintsArr = hintsArr.concat(ration.jobContent.split('\n'));
-                }
                 if(ration.annotation && ration.annotation.toString().trim() !== ''){
                     hintsArr.push(`附注:`);
                     hintsArr = hintsArr.concat(ration.annotation.split('\n'));
                 }
-                ration._doc.hint = hintsArr.join('<br>');
+                ration.hint = hintsArr.join('<br>');
             }
             for(let ration of compleRations){
                 let hintsArr = [];
@@ -193,7 +226,7 @@ class SearchDao{
                     hintsArr.push(`附注:`);
                     hintsArr = hintsArr.concat(ration.annotation.split('\n'));
                 }
-                ration._doc.hint = hintsArr.join('<br>');
+                ration.hint = hintsArr.join('<br>');
             }
             rst.data = stdRations.concat(compleRations);
             callback(0, rst);

+ 8 - 2
modules/complementary_ration_lib/models/sectionTreeModel.js

@@ -48,6 +48,12 @@ class SectionTreeDao {
         });
 
     }
+
+    async getComplementaryTree (userId, compilationId) {
+        const treeData = await compleRationSectionTreeModel.find({userId, compilationId}).lean();
+        return treeData;
+    }
+
     async getRationTree(userId, compilationId, rationRepId, type, callback) {
         //区分要获取的是标准的数据还是补充的数据
         const rationLibType = {
@@ -57,9 +63,9 @@ class SectionTreeDao {
         try {
             let treeData;
             if (type === rationLibType.complementary) {
-                treeData = await compleRationSectionTreeModel.find({userId: userId, compilationId: compilationId, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]});
+                treeData = await compleRationSectionTreeModel.find({userId: userId, compilationId: compilationId, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]}).lean();
             } else {
-                treeData = await stdSectionTreeModel.find({rationRepId: rationRepId});
+                treeData = await stdSectionTreeModel.find({rationRepId: rationRepId}).lean();
             }
             callback(0, treeData);
         } catch (err) {

+ 1 - 0
modules/complementary_ration_lib/routes/routes.js

@@ -21,6 +21,7 @@ module.exports = function (app) {
     app.get('/complementaryRation/glj', compleViewController.init, compleViewController.redirectGljList);
     app.get('/complementaryRation/coe', compleViewController.init, compleViewController.redirectCoeList);
     app.get('/complementaryRation/installation', compleViewController.init, compleViewController.redirectInstallation);
+    router.get('/initData', compleViewController.init, compleViewController.prepareInitData);
 
     router.post('/getRationLib', compleViewController.init, compleViewController.getRationLib);
     router.post('/getRationLibs', compleViewController.init, compleViewController.getRationLibs);

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

@@ -262,9 +262,8 @@ async function changeFeeRateStandard(jdata){
         libName:template.libName,
         feeRateID: newFeeRate.ID
     };
-    let deleteInfo = {deleted: true, deleteDateTime: new Date(), deleteBy: data.user_id};
     await feeRateFileModel.findOneAndUpdate({ID:data.feeRateFileID,deleteInfo:null},doc);
-    await feeRateModel.findOneAndUpdate({ID:data.feeRateID},{deleteInfo:deleteInfo});
+    await feeRateModel.deleteOne({ID:data.feeRateID});
     doc.rates=newFeeRate.rates;
     return doc;
 }

+ 40 - 81
modules/glj/controllers/glj_controller.js

@@ -14,11 +14,12 @@ import UnitPriceFileModel from "../models/unit_price_file_model";
 let logger = require("../../../logs/log_helper").logger;
 let consts = require('../../main/models/project_consts');
 let glj_type_util = require('../../../public/cache/std_glj_type_util');
+let ration_glj_facade = require('../../ration_glj/facade/ration_glj_facade');
 let mongoose = require('mongoose');
 let ration = mongoose.model('ration');
 let projectModel = mongoose.model('projects');
 let _ = require('lodash');
-
+let glj_facade = require('../facade/glj_facade');
 
 const ProjectModel = require('../../pm/models/project_model').project;
 class GLJController extends BaseController {
@@ -84,8 +85,8 @@ class GLJController extends BaseController {
         };
         try {
             // 可编辑的字段
-            let editableField = ['is_evaluate', 'unit_price.market_price', 'is_adjust_price', 'mix_ratio.consumption',
-                'supply', 'supply_quantity','delivery_address','delivery','materialType','materialCoe','is_main_material','remark'];
+            let editableField = ['is_evaluate', 'unit_price.market_price', 'is_adjust_price', 'mix_ratio.consumption','is_eval_material','no_tax_eqp','is_coe_adjust','is_info_adjust','FI','FO','standardPrice','riskCoe',
+                'supply', 'supply_quantity','delivery_address','delivery','materialType','materialCoe','is_main_material','originPlace','vender','qualityGrace','brand','remark'];
             if (editableField.indexOf(field) < 0) {
                 throw '对应字段不能编辑';
             }
@@ -244,23 +245,29 @@ class GLJController extends BaseController {
             let gljList = data.gljList, parentInfo = data.parentInfo,mixRatios = [];
             let projectGljModel = new GLJListModel();
             let mixRatioModel = new MixRatioModel();
-            for(let g of gljList){
-                let newProjectGLJ = await projectGljModel.addList(g,parentInfo.unit_price_file_id);
-                let mixRatio = {
-                    glj_id:newProjectGLJ.glj_id,
-                    consumption:0,
-                    unit_price_file_id:parentInfo.unit_price_file_id,
-                    connect_key:parentInfo.connect_key,
-                    type: newProjectGLJ.type,
-                    code: newProjectGLJ.code,
-                    specs:newProjectGLJ.specs,
-                    name:newProjectGLJ.name,
-                    unit:newProjectGLJ.unit,
-                    model:newProjectGLJ.model
-                };
-                newProjectGLJ.ratio_data = await mixRatioModel.add(mixRatio);
-                mixRatios.push(newProjectGLJ);
+            if(gljList.length > 0){
+                let [unitFileId,ext] = await ration_glj_facade.prepareExtData(gljList[0].project_id,request.session.sessionCompilation);
+                console.log(ext);
+                for(let g of gljList){
+                    let newProjectGLJ = await projectGljModel.addList(g,unitFileId,ext);
+                    let mixRatio = {
+                        glj_id:newProjectGLJ.glj_id,
+                        consumption:0,
+                        unit_price_file_id:unitFileId,
+                        connect_key:parentInfo.connect_key,
+                        type: newProjectGLJ.type,
+                        code: newProjectGLJ.code,
+                        specs:newProjectGLJ.specs,
+                        name:newProjectGLJ.name,
+                        unit:newProjectGLJ.unit,
+                        model:newProjectGLJ.model
+                    };
+                    newProjectGLJ.ratio_data = await mixRatioModel.add(mixRatio);
+                    mixRatios.push(newProjectGLJ);
+                }
+
             }
+
             result.data = mixRatios;
         }catch (err){
             logger.err(err);
@@ -358,68 +365,7 @@ class GLJController extends BaseController {
             msg: ''
         };
         try {
-            let currentUnitPriceId = await ProjectModel.getUnitPriceFileId(projectId);
-            let unitPriceFileModel = new UnitPriceFileModel();
-
-            let insertData = null;
-            if (type > 0) {
-                let currentUnitPrice = await unitPriceFileModel.findDataByCondition({id: changeUnitPriceId});
-                if (currentUnitPrice === null) {
-                    throw '不存在对应单价文件';
-                }
-                // 获取当前项目的rootProjectId
-                let projectData = await ProjectModel.getProject(projectId);
-                let rootProjectId = projectData.property.rootProjectID !== undefined ? projectData.property.rootProjectID : 0;
-
-                insertData = JSON.parse(JSON.stringify(currentUnitPrice));
-                insertData.root_project_id = rootProjectId;
-                newName?insertData.name = newName:'';
-                insertData.user_id = insertData.user_id === undefined ? request.session.sessionUser.id : insertData.user_id;
-                delete insertData._id;
-                delete insertData.ID;
-            }
-            // 获取即将更改的单价文件信息
-            let targetUnitPriceFile = type === 0 ? await unitPriceFileModel.findDataByCondition({id: changeUnitPriceId}) :
-                await unitPriceFileModel.add(insertData);
-            if (targetUnitPriceFile === null) {
-                throw '没有找到对应的单价文件';
-            }
-
-            // 查找对应单价文件的项目工料机数据
-            let unitPriceModel = new UnitPriceModel();
-            if(type ===1){//从其它项目复制,则先复制一份数据。
-                let needCopyList = await unitPriceModel.findDataByCondition({unit_price_file_id: changeUnitPriceId}, null, false);
-                if(needCopyList){
-                    // 过滤mongoose格式
-                    needCopyList = JSON.stringify(needCopyList);
-                    needCopyList = JSON.parse(needCopyList);
-                    let copyList = [];
-                    for(let n of needCopyList){
-                        delete n._id;  // 删除原有id信息
-                        delete n.id;
-                        n.unit_price_file_id = targetUnitPriceFile.id;
-                        copyList.push(n);
-                    }
-                    copyList.length>0 ? await unitPriceModel.add(copyList):'';
-                }
-            }
-
-
-            let copyResult = await unitPriceModel.copyNotExist(currentUnitPriceId, targetUnitPriceFile.id,projectId);
-            // 复制成功后更改project数据
-            if (!copyResult) {
-                throw '复制数据失败';
-            }
-
-            let changeUnitPriceFileInfo = {
-                id: targetUnitPriceFile.id,
-                name: targetUnitPriceFile.name
-            };
-            let result = ProjectModel.changeUnitPriceFileInfo(projectId, changeUnitPriceFileInfo);
-            if (!result) {
-                throw '切换单价文件失败!';
-            }
-
+            responseData.unitFile = await glj_facade.changeUnitFile({projectID:projectId}, {id:changeUnitPriceId,name:newName},type,request.session.sessionUser.id);
         } catch (error) {
             console.log(error);
             responseData.err = 1;
@@ -753,6 +699,19 @@ class GLJController extends BaseController {
         res.json(result);
     }
 
+    async updateEvaluateMaterial(request, response){
+        let result={error:0};
+        try {
+            let data = JSON.parse(request.body.data);
+            result.data = await glj_facade.updateEvaluateMaterial(data);
+        }catch (err){
+            logger.err(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
+
     async modifyKeyValue(req,res){//修改工料机关键的属性:名称、类型、规格、型号等
         let result={
             error:0

+ 38 - 2
modules/glj/facade/glj_facade.js

@@ -2,12 +2,19 @@
  * Created by zhang on 2019/1/2.
  */
 module.exports={ //先导出后require可以解决循环引用问题
-    changeUnitFile:changeUnitFile
+    changeUnitFile:changeUnitFile,
+    updateEvaluateMaterial:updateEvaluateMaterial
 };
 
+const mongoose = require('mongoose');
 const ProjectModel = require('../../pm/models/project_model').project;
 import UnitPriceFileModel from "../models/unit_price_file_model";
 import UnitPriceModel from "../models/unit_price_model";
+let evaluateListModel = mongoose.model("evaluate_list");
+let bidEvaluationMode = mongoose.model("bid_evaluation_list");
+let contractorListModel = mongoose.model("contractor_list");
+let projectGLJModel = mongoose.model("glj_list");
+let unitFileMode = mongoose.model("unit_price");
 
 async function changeUnitFile(projectData,unitFile,type,userID) {
     let projectId = projectData.projectID;
@@ -34,6 +41,7 @@ async function changeUnitFile(projectData,unitFile,type,userID) {
             insertData.user_id = userID;
             delete insertData._id;
             delete insertData.ID;
+            currentUnitPriceId = changeUnitPriceId;//从其它建设项目复制时,这个ID要变成被选中的来源的单价文件ID
         }
         // 获取即将更改的单价文件信息
         let targetUnitPriceFile = type === 0 ? await unitPriceFileModel.findDataByCondition({id: changeUnitPriceId}) :
@@ -77,4 +85,32 @@ async function changeUnitFile(projectData,unitFile,type,userID) {
             throw '切换单价文件失败!';
         }
         return changeUnitPriceFileInfo;
-}
+}
+
+
+async function updateEvaluateMaterial(data) {
+    let modelMap = {
+        "glj_list":projectGLJModel,
+        "evaluate_list":evaluateListModel,
+        "unit_price":unitFileMode,
+        "bid_evaluation_list":bidEvaluationMode,
+        "contractor_list":contractorListModel
+    };
+    for(let t of data.tasks){
+        let model = modelMap[t.type];
+        switch (t.action) {
+            case "update":
+                let query = {};
+                if(t.hasOwnProperty('ID')) query['ID'] = t.ID;
+                if(t.hasOwnProperty('id')) query['id'] = t.id;
+                await model.update(query,t.doc);
+                break;
+            case "add":
+                await model.create(t.doc);
+                break;
+            case "delete":
+                await model.deleteOne({ID:t.ID});
+                break;
+        }
+    }
+}

+ 159 - 77
modules/glj/models/glj_list_model.js

@@ -20,6 +20,7 @@ const scMathUtil = require('../../../public/scMathUtil').getUtil();
 import decimal_facade from "../../main/facade/decimal_facade";
 let gljCollectionName = 'glj_list';
 let GLJSchemas = mongoose.model(gljCollectionName);
+let _ = require("lodash");
 
 class GLJListModel extends BaseModel {
 
@@ -61,7 +62,7 @@ class GLJListModel extends BaseModel {
         switch (scene) {
             // 新增数据的验证规则
             case 'add':
-                this.model.schema.path('glj_id').required(true);
+                //this.model.schema.path('glj_id').required(true);
                 this.model.schema.path('project_id').required(true);
                 this.model.schema.path('code').required(true);
                 // this.model.schema.path('name').required(true);
@@ -84,6 +85,7 @@ class GLJListModel extends BaseModel {
         let mixRationMap={};
         let keyMap={};
         let unitPriceList={};
+        unitPriceFileId = unitPriceFileId?unitPriceFileId:await ProjectModel.getUnitPriceFileId(projectId);
         try {
             // 首先获取对应标段下所有的项目工料机数据
             let condition = {project_id: projectId};
@@ -241,15 +243,27 @@ class GLJListModel extends BaseModel {
             if (Object.keys(data).length <= 0) {
                 throw '新增数据为空';
             }
+            //if(data.specs==undefined||data.specs==null||data.specs=="") data.specs = null;
             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 projectGljData = null;
+            let  projectGLJList = await this.findDataByCondition(condition,null,false);
+            if(projectGLJList.length > 0){//specs有可能为空或"",所以这里做这样处理
+                let p = data.specs;
+                if(p==undefined||p==null||p=="") p = null;
+                for(let g of projectGLJList){
+                    let t = g.specs;
+                    if(t==undefined||t==null||t=="") t = null;
+                    if(p == t) projectGljData = g;
+                }
+            }
+
+          //  let projectGljData = await this.findDataByCondition(condition);
             let isAddProjectGLJ = false;
 
             // 获取标段对应的单价文件id
@@ -543,44 +557,37 @@ class GLJListModel extends BaseModel {
             throw '参数错误';
         }
         let fromTable = data.from === undefined ? 'std' : data.from;
+        var connect_key =this.getIndex(data,['code','name','specs','unit','type']);
+        let e_mList = await this.getCompositionGLJListFromMixRatio(projectId,unitPriceFileId,connect_key,fromTable);
+        //如果组成物数据已经存在,则以单价文件中组成物的数据为准,不用再往下做判断了(项目工料机中需要插入的已经在上一步中生成插入)
+        if(e_mList.length > 0) return;
+
         // 查找对应组成物的项目工料机数据
         let indexs=['code','name','specs','unit','type'];
         let [projectGljList, compositionGljList] = await this.getCompositionGLJList(gljId, projectId, indexs, fromTable);
-        // 整理配合比待插入数据
+        // 整理配合比待插入数据,单价文件中配合比没有数据,则以库中的组成物信息为准
         let mixRatioInsertData = [];
-        var connect_key =this.getIndex(data,['code','name','specs','unit','type']);
-        //先查找是否已存在配合比数据,在共用单价文件的情况下,有可能配合比数据存在,项目工料机数据不存在
-        let e_mList = await mixRatioModel.findDataByCondition({unit_price_file_id: unitPriceFileId,connect_key: connect_key},null,false);
-        if(e_mList.length <= 0){
-            for (let tmp of compositionGljList) {
-                let consumpiton = tmp.consumption;
-                //只有标准的工料机的组成物才会有多单价、多组成物消耗量的情况 fromTable
-                if(fromTable == 'std' && ext && ext.quantityField &&( tmp.consumption[ext.quantityField]!= undefined && tmp.consumption[ext.quantityField]!=null)){
-                    consumpiton = tmp.consumption[ext.quantityField];
-                }
-                // 配合比数据插入
-                let mixRatioData = {
-                    consumption: consumpiton,
-                    glj_id: tmp.ID,
-                    unit_price_file_id: unitPriceFileId,
-                    connect_key: connect_key,
-                    type: tmp.gljType,
-                    code: tmp.code,
-                    specs:tmp.specs,
-                    name:tmp.name,
-                    unit:tmp.unit
-                };
-                mixRatioInsertData.push(mixRatioData);
-            }
-        }
-        // 插入配合比表
-        // 因为有可能项目工料机与单价数据已存在,但配合比数据不存在,所以先插入配合比,后续判断如果存在项目工料机则可以省下数据库操作
-        if(mixRatioInsertData.length>0){
-            let addMixRatioResult = await mixRatioModel.add(mixRatioInsertData);
-            if (!addMixRatioResult) {
-                throw '组成物插入单价数据失败!';
+        for (let tmp of compositionGljList) {
+            let consumpiton = tmp.consumption;
+            //只有标准的工料机的组成物才会有多单价、多组成物消耗量的情况 fromTable
+            if(fromTable == 'std' && ext && ext.quantityField &&( tmp.consumption[ext.quantityField]!= undefined && tmp.consumption[ext.quantityField]!=null)){
+                consumpiton = tmp.consumption[ext.quantityField];
             }
+            // 配合比数据插入
+            let mixRatioData = {
+                consumption: consumpiton,
+                glj_id: tmp.ID,
+                unit_price_file_id: unitPriceFileId,
+                connect_key: connect_key,
+                type: tmp.gljType,
+                code: tmp.code,
+                specs:tmp.specs,
+                name:tmp.name,
+                unit:tmp.unit
+            };
+            mixRatioInsertData.push(mixRatioData);
         }
+
         let unitPriceModel = new UnitPriceModel();
         let unitPriceList = await unitPriceModel.model.find({unit_price_file_id:unitPriceFileId});//查找按文件id查找单价文件数据,用来判断单价文件信息是否已存在
 
@@ -591,19 +598,7 @@ class GLJListModel extends BaseModel {
             let key = this.getIndex(tmp,['code','name','specs','unit','gljType']);
             if (projectGljList[key] === undefined) {
                 // 项目工料机插入的数据
-                let gljData = {
-                    glj_id: tmp.ID,
-                    project_id: projectId,
-                    code: tmp.code,
-                    name: tmp.name,
-                    specs: tmp.specs,
-                    unit: tmp.unit === undefined ? '' : tmp.unit,
-                    type: tmp.gljType,
-                    adjCoe:tmp.adjCoe,
-                    original_code:tmp.code,
-                    materialType: tmp.materialType,   //三材类别
-                    materialCoe: tmp.materialCoe
-                };
+                let gljData = this.getProjectGLJNewData(tmp,projectId);
                 gljInsertData.push(gljData);
             }
             let basePrice = tmp.basePrice;
@@ -639,11 +634,96 @@ class GLJListModel extends BaseModel {
 
         // 插入单价数据表
         if(unitPriceInsertData.length >0) await unitPriceModel.add(unitPriceInsertData);
+
+        // 插入配合比表
+        // 因为有可能项目工料机与单价数据已存在,但配合比数据不存在,所以先插入配合比,后续判断如果存在项目工料机则可以省下数据库操作
+        if(mixRatioInsertData.length>0){
+            let addMixRatioResult = await mixRatioModel.add(mixRatioInsertData);
+            if (!addMixRatioResult) {
+                throw '组成物插入单价数据失败!';
+            }
+        }
        //插入项目工料机
         if(gljInsertData.length > 0) await this.add(gljInsertData);
         return
     }
 
+    getProjectGLJNewData(tmp,projectId){
+        let gljData = {
+            glj_id: tmp.ID,
+            project_id: projectId,
+            code: tmp.code,
+            name: tmp.name,
+            specs: tmp.specs,
+            unit: tmp.unit === undefined ? '' : tmp.unit,
+            type: tmp.gljType,
+            adjCoe:tmp.adjCoe,
+            original_code:tmp.code,
+            materialType: tmp.materialType,   //三材类别
+            materialCoe: tmp.materialCoe,
+            materialIndexType: tmp.materialIndexType,
+            materialIndexUnit: tmp.materialIndexUnit,
+            materialIndexCoe: tmp.materialIndexCoe
+        };
+        return gljData;
+    }
+
+    //从配合比表中取组成物的信息以及对应的项目工料机信息,再从配合比数据中反查工料机库中的信息
+    async getCompositionGLJListFromMixRatio (projectId,unitPriceFileId,connect_key,fromTable = 'std'){
+        let mixRatioModel = new MixRatioModel();
+        let e_mList = await mixRatioModel.model.find({unit_price_file_id: unitPriceFileId,connect_key: connect_key}).lean();
+        if (e_mList.length <= 0) return [];
+        let codeList = [];
+        let ratioMap = {};
+        for(let tmp of e_mList) {
+            codeList.push(tmp.code);
+            let ckey = this.getIndex(tmp);
+            ratioMap[ckey] = tmp
+        }
+        // 查找对应的项目工料机数据
+        let condition = {project_id: projectId,code: {"$in": codeList} };
+        let gljData = await this.model.find(condition, {_id: 0}).lean();
+        let matchGljs = {};
+        for(let g of gljData){
+            let gkey = this.getIndex(g);
+            if(ratioMap[gkey]) matchGljs[gkey] = g;
+        }
+
+        //如果有组成物信息存在,但项目工料机信息不全的情况,应从数据库中查存询原始信息
+        // (只有在父工料机是来自标准的情况下做这个操作,因为组成物表中没有存具体的工料机是来自补充的还是标准库,而父工料机是补充库的,组成物有可能来自两个库,而父来自标准,子只会来自标准)
+        let lessIDList = [],lessMix=[];//lessMix只有在父工料机是来自补充工料机的时候才要用到
+        let componentGljData = [];
+        let gljInsertData = [];
+        for(let mk in ratioMap){//找出缺少的工料机
+            if(!matchGljs[mk]){
+                lessMix.push(ratioMap[mk]);
+                lessIDList.push(ratioMap[mk].glj_id);
+            }
+        }
+        if(lessIDList.length > 0){
+            if(fromTable == "std"){
+                let gljListModel = new STDGLJLibGLJListModel();
+                let condition = {ID: {$in: lessIDList}};
+                componentGljData = await gljListModel.model.find(condition).lean();
+                for(let tmp of componentGljData){
+                    let newProjctData = this.getProjectGLJNewData(tmp,projectId);
+                    gljInsertData.push(newProjctData)
+                }
+            }else {
+                for(let m of lessMix){
+                    m.from = 'cpt';
+                    let newProjctData =  this.getProjectGLJNewData(m,projectId);
+                    gljInsertData.push(newProjctData);
+                }
+            }
+
+        }
+
+        //这边只插入项目工料机数据,对于组成物文件中有的数据,理论上单价文件也是应该要有的,如果没有,那只能是mongodb数据库不支持事务的情况的问题
+        if(gljInsertData.length > 0)  await this.add(gljInsertData);
+        return e_mList;
+    }
+
     /**
      * 获取组成物具体数据
      *
@@ -702,24 +782,23 @@ class GLJListModel extends BaseModel {
             return [{},[]];
         }
 
+        //2019-11-15 优化,用5个属性条件去查意义不大,用编号就能过滤得差不多了,最后再用5个属性去匹配。 同时可以提高查询效率
         let codeList = [];
-        let nameList = [];
-        let specsList= [];
-        let typeList = [];
-        let unitList = [];
+        let componentMap = {};
         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 ckey = this.getIndex(tmp,['code','name','specs','unit','gljType']);
+            componentMap[ckey] = tmp
         }
-
         // 查找对应的项目工料机数据
-        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];
+        let condition = {project_id: projectId,code: {"$in": codeList} };
+        let gljData = await this.model.find(condition, {_id: 0}).lean();
+        let matchGljs = {};
+        for(let g of gljData){
+            let gkey = this.getIndex(g);
+            if(componentMap[gkey]) matchGljs[gkey] = g;
+        }
+        return [matchGljs, componentGljList];
     }
 
     async getCompositionGLJByData(glj,unitPriceFileId){
@@ -747,42 +826,41 @@ class GLJListModel extends BaseModel {
         // 查找对应的配合比数据
         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 mixRatios = await mixRatioModel.model.find(condition, {_id: 0}).lean();
+        //2019-11-15 优化,用5个属性条件去查意义不大,用编号就能过滤得差不多了,最后再用5个属性去匹配。 同时可以提高查询效率
         let codeList = [];
-        let nameList = [];
-        let specsList= [];
-        let typeList = [];
-        let unitList = [];
         if(mixRatios.length<=0){
             return [[],[],[]];
         }
         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;
         }
+        //数组去重
+        codeList = _.uniq(codeList);
         // 查找对应的项目工料机数据
-        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 gcondition = {project_id: glj.project_id?glj.project_id:glj.projectID,code: {"$in": codeList}};
+        let gljData = await this.model.find(gcondition, {_id: 0}).lean();
         // 查找对应的单价数据
         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 ucondition = { unit_price_file_id: unitPriceFileId,code: {"$in": codeList}};
+        let unitPriceList = await unitPriceModel.model.find(ucondition, {_id: 0}).lean();
 
 
         // 整理数据
         let unitPriceData = {};
         for(let tmp of unitPriceList) {
             let u_index = this.getIndex(tmp,['code','name','specs','unit','type']);
-            unitPriceData[u_index] = tmp;
+            if(mixRatioData[u_index]) unitPriceData[u_index] = tmp;//过滤掉不完全匹配的工料机单价
+        }
+        let c_gljData = [];
+        for(let g of gljData){//过滤掉不是组成物的工料机
+            let g_index =  this.getIndex(g,['code','name','specs','unit','type']);
+            if(mixRatioData[g_index]) c_gljData.push(g);
         }
-        return [gljData,mixRatioData,unitPriceData];
+        return [c_gljData,mixRatioData,unitPriceData];
 
     }
 
@@ -809,6 +887,10 @@ class GLJListModel extends BaseModel {
             specs:projectGLJData.specs,
             type:projectGLJData.type,
             unit:projectGLJData.unit
+        };
+        if (!condition.specs) {
+            delete condition.specs;
+            condition.$or = [{specs: null}, {specs: ''}];
         }
         let unitPrice = await unitPriceModel.findDataByCondition(condition);
 

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

@@ -33,7 +33,7 @@ class MixRatioModel extends BaseModel {
         switch (scene) {
             // 新增数据的验证规则
             case 'add':
-                this.model.schema.path('glj_id').required(true);
+                //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_key').required(true);
@@ -57,7 +57,6 @@ class MixRatioModel extends BaseModel {
             data.id = await counterModel.getId(collectionName);
         }
 
-        this.setScene('add');
         return this.db.model.create(data);
     }
 

+ 2 - 2
modules/glj/models/unit_price_file_model.js

@@ -33,8 +33,8 @@ class UnitPriceFileModel extends BaseModel {
         switch (scene) {
             // 新增数据的验证规则
             case 'add':
-                this.model.schema.path('name').required(true);
-                this.model.schema.path('project_id').required(true);
+               /* this.model.schema.path('name').required(true);
+                this.model.schema.path('project_id').required(true);*/
                 break;
         }
     }

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

@@ -28,6 +28,10 @@ router.post('/batchUpdatePrices', gljController.init, gljController.batchUpdateP
 router.post('/batchUpdateGLJProperty', gljController.init, gljController.batchUpdateGLJProperty);
 router.post('/batchUpdateConsumption', gljController.init, gljController.batchUpdateConsumption);
 router.post('/modifyKeyValue',gljController.init, gljController.modifyKeyValue);
+router.post('/updateEvaluateMaterial', gljController.updateEvaluateMaterial);
+
+
+
 
 router.get('/test', gljController.init, gljController.test);
 router.get('/testModify', gljController.init, gljController.testModify);

+ 59 - 0
modules/import/controllers/import_controller.js

@@ -0,0 +1,59 @@
+/**
+ * Created by zhang on 2019/12/3.
+ */
+/**
+ * Created by jimiz on 2017/4/9.
+ */
+let logger = require("../../../logs/log_helper").logger;
+let pm_facade = require('../../pm/facade/pm_facade');
+let controller = {
+    importProject:async function (req){
+        let data = req.body.data;
+        let sessionInfo = {session:req.body.session};
+        let fields = req.body.fields;
+        return await pm_facade.importProjects(data,sessionInfo,fields);
+    },
+    exportProject:async function(req){
+        let result={
+            error:0
+        };
+        let data = JSON.parse(req.body.dataString);
+        result.data = await pm_facade.exportProject(req.body.userID, data);
+        return result
+    },
+    copyProject:async function(req){
+        let result={
+            error:0
+        };
+        let data = JSON.parse(req.body.dataString);
+        result.data = await pm_facade.copyProject(req.body.userID,req.body.compilationID,data);
+        return result
+    }
+};
+
+
+module.exports = {
+    action:async function(req,res){//自动跳转到URL对应的controller方法
+        let result={
+            error:0
+        };
+        try {
+            let functionName = req.url.replace(/\//g,"");
+            result = controller[functionName]?await controller[functionName](req):"";
+        }catch (err){
+            logger.err(err);
+            if(typeof err == "string"){
+                result.error=2;
+                result.msg = err;
+            }else {
+                result.error=1;
+                result.msg = "导入失败请检查文件!"
+            }
+        }
+        res.json(result);
+    }
+};
+
+
+
+

+ 16 - 0
modules/import/routes/import_route.js

@@ -0,0 +1,16 @@
+/**
+ * Created by zhang on 2019/12/3.
+ */
+let express = require('express');
+
+module.exports = function (app) {
+    let importRouter = express.Router();
+    let importController = require('../controllers/import_controller');
+    importRouter.post('/importProject',importController.action);
+    importRouter.post('/exportProject',importController.action);
+    importRouter.post('/copyProject',importController.action);
+    importRouter.get('/test',function (req,res) {
+        res.json("hello word");
+    })
+    app.use('/import', importRouter);
+};

+ 1 - 1
modules/main/controllers/bills_controller.js

@@ -183,7 +183,7 @@ module.exports = {
         try {
             let data = req.body.data;
             data = JSON.parse(data);
-            let pasteResult = await bill_facade.pasteBlock(data);
+            let pasteResult = await bill_facade.pasteBlock(data,req.session.sessionCompilation);
             result.data = pasteResult;
         }catch (err){
             logger.err(err);

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

@@ -151,5 +151,37 @@ module.exports = {
             result.message = err.message;
         }
         res.json(result);
+    },
+    getSEIProjects:async function(req,res){
+        let data = JSON.parse(req.body.data);
+        let result={
+            error: 0,
+            data: null
+        };
+        try{
+            result.data = await project_facade.getSEIProjects(data.projectID);
+        }
+        catch(err){
+            logger.err(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
+    },
+    loadSEIProjectData:async function(req,res){
+        let data = JSON.parse(req.body.data);
+        let result={
+            error: 0,
+            data: null
+        };
+        try{
+            result.data = await project_facade.loadSEIProjectData(data.projectID);
+        }
+        catch(err){
+            logger.err(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
     }
 };

+ 21 - 6
modules/main/controllers/ration_controller.js

@@ -8,6 +8,7 @@ let ration_facade = require('../facade/ration_facade');
 let bill_facade = require('../facade/bill_facade');
 let project_facade = require("../facade/project_facade");
 let logger = require("../../../logs/log_helper").logger;
+import GLJController from "../../glj/controllers/glj_controller";
 let controller = {
     insertGLJAsRation:async function (req){
         let data = req.body.data;
@@ -26,7 +27,12 @@ let controller = {
             data = JSON.stringify(data);
         }
         data = JSON.parse(data);
-        return await ration_facade.addNewRation(data,req.session.sessionCompilation);
+        let result = await ration_facade.addNewRation(data,req.session.sessionCompilation);
+        //合并取项目工料机数据的情求,用于刷新项目工料机数据,当有添加、替换项目工料机的情况,才需要刷新
+        if(result.ration_gljs && result.ration_gljs.length > 0 && data.newData){
+            result.projectGLJDatas =  await getProjectGLJData(data.newData.projectID);
+        }
+        return result;
     },
     addMultiRation: async function (req) {
         let data = req.body.data;
@@ -59,13 +65,14 @@ let controller = {
         let applyTasks = [
             ration_facade.addMultiRation(data.rations.create,req.session.sessionCompilation),//先生成新定额
             bill_facade.createNewBills(data.bills.create),
+            ration_facade.deleteMultiRation(data.rations.delete)
         ];
         //整理更新的数据,调用一个方法更新
         updateDatas.push(data.ration_template);
         if(data.rations.update.length > 0)  prepareUpdateNodes(data.rations.update,updateDatas,"ration");
         if(data.bills.update.length > 0)  prepareUpdateNodes(data.bills.update,updateDatas,"bills");
         applyTasks.push(project_facade.updateNodes(updateDatas));
-        let [rationResult,billsResult,updates] = await Promise.all(applyTasks);
+        let [rationResult,billsResult,deleteResult,updates] = await Promise.all(applyTasks);
         return {rationResult:rationResult,billsResult:billsResult,updateDatas:updateDatas};
     },
     //更新辅助定额
@@ -78,13 +85,21 @@ let controller = {
     updateCoeAdjust:async function(req){
         let data = req.body.data;
         data = JSON.parse(data);
-        return await ration_facade.updateCoeAdjust(data,req.session.sessionCompilation);
-
+        let result = await ration_facade.updateCoeAdjust(data,req.session.sessionCompilation);
+        //合并取项目工料机数据的情求,用于刷新项目工料机数据,当有添加、替换项目工料机的情况,才需要刷新
+        if(result.add.length > 0 || result.replace.length > 0){
+            result.projectGLJDatas =  await getProjectGLJData(data.projectID);
+        }
+        return result;
     }
-
-
 };
 
+async function getProjectGLJData(projectID) {
+    let gljController = new GLJController();
+    let responseData = await gljController.getProjectGLJsByProjectID(projectID);
+    return responseData.data;
+}
+
 function prepareUpdateNodes(datas,nodes,type) {
     for(let d of datas){
         nodes.push({type:type,data:d});

+ 24 - 0
modules/main/facade/bid_facade.js

@@ -0,0 +1,24 @@
+/**
+ * Created by zhang on 2019/9/11.
+ */
+
+
+module.exports={
+    getData:getData
+};
+
+const mongoose = require('mongoose');
+let bidEvaluationListModel = mongoose.model("bid_evaluation_list");
+let consts = require('../../main/models/project_consts');
+
+
+
+function getData(projectID, callback) {
+    bidEvaluationListModel.find({'projectID': projectID}).lean().exec((err, datas) => {
+        if (err) {
+            callback(1, '', null);
+        } else {
+            callback(0, consts.projectConst.BID_EVALUATION_LIST, datas);
+        }
+    })
+}

+ 13 - 10
modules/main/facade/bill_facade.js

@@ -72,9 +72,9 @@ module.exports={
         }
        return result;
     },
-    pasteBlock : async function(data){
+    pasteBlock : async function(data,compilation){
         let pasteTasks = [
-            pasteRationsAndRationGLJ(data.rations,data.ration_gljs),//这一步会花费比较多时间
+            pasteRationsAndRationGLJ(data.rations,data.ration_gljs,compilation),//这一步会花费比较多时间
             pasteOtherData(data)
         ];
         let [rationsAndRationGLJ,resultMap] = await Promise.all(pasteTasks);
@@ -148,19 +148,22 @@ async function pasteOtherData(data) {
     return {bills:bills,quantity_details:quantity_details,ration_coes:ration_coes,ration_installations:ration_installations,ration_templates:ration_templates,updateData:updateData}
 }
 
-async function pasteRationsAndRationGLJ (rations,ration_gljs) {
+async function pasteRationsAndRationGLJ (rations,ration_gljs,compilation) {
     let projectGljModel = new GLJListModel();
-    let gljMap = {};
-    let new_ration_gljs=[];
+    let gljMap = {}, new_ration_gljs=[],projectID=null;
+    if(rations.length > 0) projectID = rations[0].projectID;
+    if(projectID==null && ration_gljs.length > 0) projectID = ration_gljs[0].projectID;
+    if(projectID == null) return {rations:rations,new_ration_gljs:new_ration_gljs};
+    let [unitFileId,ext] = await ration_glj_facade.prepareExtData(projectID,compilation);
     //先根据定额类型的工料机,插入到项目工料机中
     for(let r of rations){
         if(r.type == rationType.gljRation){
-            await getProjectGLJ(r,rationKeyArray,gljMap);
+            await getProjectGLJ(r,rationKeyArray,gljMap,unitFileId,ext);
         }
     }
 
     for(let rg of ration_gljs){
-        await getProjectGLJ(rg,gljKeyArray,gljMap);
+        await getProjectGLJ(rg,gljKeyArray,gljMap,unitFileId,ext);
         let temRecord = ration_glj_facade.createNewRecord(rg);
         rg.rcode?temRecord.rcode = rg.rcode:'';
         rg.hasOwnProperty("customQuantity")?temRecord.customQuantity = rg.customQuantity:'';
@@ -170,16 +173,16 @@ async function pasteRationsAndRationGLJ (rations,ration_gljs) {
     rations.length>0?await insertMany(rations,ration_Model):'';
     new_ration_gljs.length>0?await insertMany(new_ration_gljs,ration_glj_Model):'';
 
-    return{rations:rations,new_ration_gljs:new_ration_gljs};
+    return {rations:rations,new_ration_gljs:new_ration_gljs};
 
-    async function getProjectGLJ (glj,keyArray,gljMap) {
+    async function getProjectGLJ (glj,keyArray,gljMap,unitFileId,ext) {
         let keyIndex = projectGljModel.getIndex(glj,keyArray);
         let pgljResult = null;
         if(gljMap[keyIndex]){
             pgljResult = gljMap[keyIndex]
         }else {
             let p_glj = ration_glj_facade.getGLJSearchInfo(glj);
-            pgljResult = await projectGljModel.addList(p_glj);//逐条添加到项目工料机
+            pgljResult = await projectGljModel.addList(p_glj,unitFileId,ext);//逐条添加到项目工料机
             gljMap[keyIndex] = pgljResult;
         }
         setResult(glj,pgljResult);

+ 24 - 0
modules/main/facade/contractor_facade.js

@@ -0,0 +1,24 @@
+/**
+ * Created by zhang on 2019/9/11.
+ */
+
+
+module.exports={
+    getData:getData
+};
+
+const mongoose = require('mongoose');
+let contractorListModel = mongoose.model("contractor_list");
+let consts = require('../../main/models/project_consts');
+
+
+
+function getData(projectID, callback) {
+    contractorListModel.find({'projectID': projectID}).lean().exec((err, datas) => {
+        if (err) {
+            callback(1, '', null);
+        } else {
+            callback(0, consts.projectConst.CONTRACTOR_LIST, datas);
+        }
+    })
+}

+ 24 - 0
modules/main/facade/evaluate_facade.js

@@ -0,0 +1,24 @@
+/**
+ * Created by zhang on 2019/9/11.
+ */
+
+
+module.exports={
+    getData:getData
+};
+
+const mongoose = require('mongoose');
+let evaluateListModel = mongoose.model("evaluate_list");
+let consts = require('../../main/models/project_consts');
+
+
+
+function getData(projectID, callback) {
+    evaluateListModel.find({'projectID': projectID}).lean().exec((err, datas) => {
+        if (err) {
+            callback(1, '', null);
+        } else {
+            callback(0, consts.projectConst.EVALUATE_LIST, datas);
+        }
+    })
+}

+ 239 - 10
modules/main/facade/project_facade.js

@@ -1,6 +1,21 @@
 /**
  * Created by zhang on 2018/1/26.
  */
+
+module.exports = {
+    markUpdateProject:markUpdateProject,
+    removeProjectMark:removeProjectMark,
+    updateNodes:updateNodes,
+    calcInstallationFee:calcInstallationFee,
+    saveProperty: saveProperty,
+    getDefaultColSetting: getDefaultColSetting,
+    markProjectsToChange:markProjectsToChange,
+    getSEIProjects:getSEIProjects,
+    loadSEIProjectData:loadSEIProjectData,
+    setSEILibData:setSEILibData,
+    getIndexReportData:getIndexReportData
+};
+
 let mongoose = require('mongoose');
 let logger = require("../../../logs/log_helper").logger;
 let  projectsModel = mongoose.model('projects');
@@ -17,16 +32,14 @@ let ration_glj_facade = require("../../ration_glj/facade/ration_glj_facade");
 const uuidV1 = require('uuid/v1');
 const gljUtil = require('../../../public/gljUtil');
 let stdColSettingModel = mongoose.model('std_main_col_lib');
-
-module.exports = {
-    markUpdateProject:markUpdateProject,
-    removeProjectMark:removeProjectMark,
-    updateNodes:updateNodes,
-    calcInstallationFee:calcInstallationFee,
-    saveProperty: saveProperty,
-    getDefaultColSetting: getDefaultColSetting,
-    markProjectsToChange:markProjectsToChange
-};
+import GLJListModel from "../../glj/models/glj_list_model";
+let economicLib = mongoose.model('std_economic_lib');
+let engineerFeatureLib = mongoose.model('std_engineer_feature_lib');
+let engineerInfoLib = mongoose.model('std_engineer_info_lib');
+let mainQuantityLib = mongoose.model('std_main_quantity_lib');
+let materialLib = mongoose.model('std_material_lib');
+import fixedFlag from '../../common/const/bills_fixed';
+const scMathUtil = require('../../../public/scMathUtil').getUtil();
 
 async function calcInstallationFee(data) {
     let result={};
@@ -163,6 +176,23 @@ async function updateNodes(datas){
 
         }
     }
+    //test------------------------
+    if (billTasks.length) {
+        console.log(`billTasks===============================`);
+        billTasks.forEach(task => {
+            if (task.updateOne.update.name === '分部分项工程') {
+                console.log(`task.updateOne.filter`);
+                console.log(task.updateOne.filter);
+                console.log(`task.updateOne.update`);
+                console.log(task.updateOne.update);
+            }
+            /*console.log(`task.updateOne.filter`);
+            console.log(task.updateOne.filter);
+            console.log(`task.updateOne.update`);
+            console.log(task.updateOne.update);*/
+        });
+    }
+    //test------------------------
     rationTasks.length>0?asyncTasks.push(ration_model.model.bulkWrite(rationTasks)):'';
     billTasks.length>0?asyncTasks.push(bill_model.model.bulkWrite(billTasks)):"";
     rationGLJTasks.length>0?asyncTasks.push(ration_glj_model.bulkWrite(rationGLJTasks)):"";
@@ -271,4 +301,203 @@ function saveProperty(data, callback){
 
 async function getDefaultColSetting(libID){
     return await stdColSettingModel.findOne({ID: libID, deleted: false}, '-_id main_tree_col');
+}
+
+function sortChildren(lists) {
+    let IDMap ={},nextMap = {}, firstNode = null,newList=[];
+    for(let l of lists){
+        if(l.children&&l.children.length > 0) l.children = sortChildren(l.children);//递规排序
+        IDMap[l.ID] = l;
+        if(l.NextSiblingID!=-1) nextMap[l.NextSiblingID] = l;
+    }
+    for(let t of lists){
+        if(!nextMap[t.ID]){ //如果在下一节点映射没找到,则是第一个节点
+            firstNode = t;
+            break;
+        }
+    }
+    if(firstNode){
+        newList.push(firstNode);
+        delete IDMap[firstNode.ID];
+        setNext(firstNode,newList);
+    }
+    //容错处理,如果链断了的情况,直接添加到后面
+    for(let key in IDMap){
+        if(IDMap[key]) newList.push(IDMap[key])
+    }
+    return newList;
+
+    function setNext(node,array) {
+        if(node.NextSiblingID != -1){
+            let next = IDMap[node.NextSiblingID];
+            if(next){
+                array.push(next);
+                delete IDMap[next.ID];
+                setNext(next,array);
+            }
+        }
+    }
+
+}
+
+async  function getSEIProjects(projectID) {
+    let project = await  projectsModel.findOne({ID:projectID});
+    if(!project) throw new Error(`找不到建设项目:${projectID}`);
+    let tem_e = await projectsModel.find({ParentID:project.ID});
+    let engineers = [];
+    for(let e of tem_e){
+        if(!e.deleteInfo || !e.deleteInfo.deleted){
+            let tenders =  await projectsModel.find({ParentID:e.ID});
+            let children = [];
+            for(let t of tenders){
+                if(!t.deleteInfo || !t.deleteInfo.deleted){
+                    children.push(t);
+                }
+            }
+            e._doc.children = children;
+            engineers.push(e);
+        }
+    }
+    engineers = sortChildren(engineers);
+    project._doc.children = engineers;
+    return project;
+}
+
+async function loadSEIProjectData(projectID) {
+    let gljListModel = new GLJListModel();
+    let [gljList, mixRatioConnectData,mixRatioMap,unitPriceMap] = await gljListModel.getListByProjectId(projectID);
+    let rations = await ration_model.getDataSync(projectID);
+    let bills = await bill_model.getDataSync(projectID);
+    let ration_gljs = await  ration_glj_model.find({projectID:projectID});
+    let projectGLJs = {
+        gljList : gljList,
+        mixRatioConnectData:mixRatioConnectData,
+        mixRatioMap:mixRatioMap,
+        unitPriceMap:unitPriceMap
+    };
+    return{bills:bills,rations:rations,ration_gljs:ration_gljs,projectGLJs:projectGLJs}
+}
+
+async function setSEILibData(property){
+    if(property.engineerInfoLibID){//工程信息指标
+        let engineerInfo = await engineerInfoLib.findOne({'ID':property.engineerInfoLibID});
+        if(engineerInfo) property.engineerInfos = engineerInfo.info;
+    }
+    if(property.engineerFeatureLibID){//工程特征指标
+        let engineerFeature = await engineerFeatureLib.findOne({'ID':property.engineerFeatureLibID});
+        if(engineerFeature) property.engineerFeatures = engineerFeature.features;
+    }
+    if(property.materialLibID){//主要工料指标
+        let material = await materialLib.findOne({'ID':property.materialLibID});
+        if(material) property.materials = material.materials;
+    }
+    if(property.mainQuantityLibID){//主要工程量指标
+        let mainQuantity = await mainQuantityLib.findOne({'ID':property.mainQuantityLibID});
+        if(mainQuantity) property.mainQuantities = mainQuantity.index;
+    }
+    if(property.economicLibID){//主要工程量指标
+        let economic = await economicLib.findOne({'ID':property.economicLibID});
+        if(economic) property.economics = economic.index;
+    }
+}
+
+async function getIndexReportData(projectID,keyArr) {
+    let project = await projectsModel.findOne({ID:projectID});
+    let bills = await bill_model.getDataSync(projectID);
+    let result = {};
+
+     for(let key of keyArr){
+         switch (key) {
+             case 'ProjectCostFields':
+                 result[key] =getEngineerCostData(project.property,bills);
+                 break;
+             case 'ProjectEcoFields':
+                 result[key] = getEconomicDatas(project.property,bills);
+                 break;
+             case 'ProjectLabMaterialFields':
+                 result[key] = await getMainMaterialDatas(projectID,project.property);
+                 break;
+             case 'ProjectQtyFields':
+                 result[key] = await getQuantityDatas(project.property,bills);
+                 break;
+             case 'ProjectInfoFields':
+                 result[key] = getEngineerInfoData(project.property.engineerInfos);
+                 break;
+             case 'ProjectFeatureFields':
+                 result[key] = getEngineerFeaturesDatas(project.property.engineerFeatures);
+                 break;
+         }
+     }
+    return result
+}
+
+
+
+
+function getEngineerInfoData(engineerInfos) {
+    let datas = [];
+    for(let info of engineerInfos){
+        let d = {
+            name:info.dispName,
+            value:info.value
+        };
+        datas.push(d);
+    }
+    return datas;
+}
+
+function getEngineerFeaturesDatas(engineerFeatures) {
+    let datas = [];
+    if (engineerFeatures !== null && engineerFeatures !== undefined) {
+        for(let f of engineerFeatures){
+            let tem = {
+                ID:f.ID,
+                name:f.name,
+                value:f.value,
+                ParentID:f.ParentID
+            }
+            datas.push(tem);
+        }
+    }
+    return datas;
+}
+
+function getQuantityDatas(property,bills) {
+     return gljUtil.getQuantityDatas(property.engineerFeatures,property.mainQuantities,bills,fixedFlag,_,scMathUtil,property.decimal)
+}
+
+function getEconomicDatas(property,bills) {
+   return gljUtil.getEconomicDatas(property.engineerFeatures,property.economics,bills,fixedFlag,_,scMathUtil,property.decimal)
+}
+
+
+function getEngineerCostData(property,bills){
+    let datas = [];
+    let priceIndex = gljUtil.getEngineerCostData(property,bills,fixedFlag,scMathUtil);
+    for(let c of priceIndex.children){
+        let tem = {
+            name:c.name,
+            cost:parseFloat(c.attrs[0].value),
+            unitCost:parseFloat(c.attrs[1].value),
+            per:parseFloat(c.attrs[2].value)
+        };
+        datas.push(tem);
+    }
+    return datas;
+}
+
+async function getMainMaterialDatas(projectID,property) {
+    let gljListModel = new GLJListModel();
+    let [gljList, mixRatioConnectData,mixRatioMap,unitPriceMap] = await gljListModel.getListByProjectId(projectID, property.unitPriceFile?property.unitPriceFile.id:null);
+    gljList = JSON.parse(JSON.stringify(gljList));
+    await calcProjectGLJQuantity(projectID,{gljList:gljList,mixRatioMap:mixRatioMap},property);
+    return gljUtil.getMainMaterialDatas(property.engineerFeatures,property.materials,{gljList:gljList},property.calcOptions,property.decimal,false,_,scMathUtil)
+
+}
+
+async function calcProjectGLJQuantity(projectID,projectGLJDatas,property){
+    let q_decimal = property.decimal.glj.quantity;
+    let rationGLJDatas = await ration_glj_model.find({'projectID':projectID});
+    let rationDatas = await ration_model.model.find({'projectID':projectID});
+    gljUtil.calcProjectGLJQuantity(projectGLJDatas,rationGLJDatas,rationDatas,[],q_decimal)
 }

+ 76 - 49
modules/main/facade/ration_facade.js

@@ -1,6 +1,20 @@
 /**
  * Created by zhang on 2018/2/9.
  */
+//先导出后require可以解决循环引用问题
+module.exports = {
+    replaceRations: replaceRations,
+    addNewRation:addNewRation,
+    addMultiRation: addMultiRation,
+    deleteMultiRation:deleteMultiRation,
+    getSameSectionRations:getSameSectionRations,
+    getExtendData:getExtendData,
+    getDefaultProgramID:getDefaultProgramID,
+    deleteSubListByQuery:deleteSubListByQuery,
+    updateCoeAdjust:updateCoeAdjust
+};
+
+
 let mongoose = require('mongoose');
 import SearchDao from '../../complementary_ration_lib/models/searchModel';
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
@@ -29,16 +43,6 @@ const projectDao = require('../../pm/models/project_model').project;
 let projectModel = mongoose.model('projects');
 const fs = require('fs');
 
-module.exports = {
-    replaceRations: replaceRations,
-    addNewRation:addNewRation,
-    addMultiRation: addMultiRation,
-    getSameSectionRations:getSameSectionRations,
-    getExtendData:getExtendData,
-    getDefaultProgramID:getDefaultProgramID,
-    deleteSubListByQuery:deleteSubListByQuery,
-    updateCoeAdjust:updateCoeAdjust
-};
 async function addNewRation(data,compilation) {
     let query = data.itemQuery;
     let stdRation = null;
@@ -72,6 +76,14 @@ async function addMultiRation(datas,compilation) {
     return rst;
 }
 
+async function deleteMultiRation(rations) {//这里是只有删除的情况,删除定额的同时删除定额下挂的其它子项目
+    if(rations.length > 0){//删除定额下的
+        let rationIDS = _.map(rations,'ID');
+        await deleteSubListByQuery({projectID:rations[0].projectID,rationID:{"$in": rationIDS}});
+        await ration_model.model.deleteMany({ID:{"$in": rationIDS}});
+    }
+}
+
 async function getSameSectionRations(data,userId,compilationId){
     //let userId
     //要先根据定额获取所属章节的ID
@@ -134,11 +146,9 @@ async function insertNewRation(newData,defaultLibID,std,calQuantity) {//插入
         }
         newData.prefix = '';
         newData.from = std.type === 'complementary' ? 'cpt' : 'std';
-        if(defaultLibID !== std.rationRepId){//借
-            newData.prefix = '借';
-        }
-        else if(std.rationRepId === defaultLibID && newData.from === 'cpt') {
-            newData.prefix = '补';
+        if(defaultLibID !== std.rationRepId){//定额是默认定额库中的时,只显示编号;
+            newData.prefix = '借';//定额不是默认定额库中的、且不是补充定额库中的时, 在定额编号前显示“借”。
+            if(newData.from === 'cpt') newData.prefix = '补';//定额是补充定额库中的时,在定额编号前显示“补”;
         }
         if(std.feeType == undefined || std.feeType == null || std.feeType ==''){//定额取费专业为空的情况下,取项目属性中的定额取费专业ID
             newData.programID = await getProgramForProject(newData.projectID);
@@ -164,7 +174,7 @@ async function replaceRations(userID,data,compilation) {
     let recodes = [];
     for(let recode of data.nodeInfo){
         let stdRation = await searchDao.getRationItem(userID,compilation._id,data.libIDs,recode.newCode, null);
-        let newRecode = await replaceRation(recode,stdRation,data.defaultLibID,data.projectID,data.calQuantity,compilation);
+        let newRecode = await replaceRation(recode,stdRation,data.defaultLibID,data.projectID,data.calQuantity,compilation,data.cleanzmhs);
         if(newRecode){
             recodes.push(newRecode);
         }else {
@@ -186,20 +196,20 @@ async function getDefaultProgramID(data) {
     return programID;
 }
 
-async function replaceRation(nodeInfo,stdRation,defaultLibID,projectID,calQuantity,compilation) {
+async function replaceRation(nodeInfo,stdRation,defaultLibID,projectID,calQuantity,compilation,cleanzmhs) {
     if(nodeInfo.newCode == null||nodeInfo.newCode ==""){//说明是删除编号,则要变成一条空定额
         await deleRationSubRecode(projectID,nodeInfo.ID);//删除定额下挂的各种数据,如定额工料机等
         return await setEmptyRation(projectID,nodeInfo.ID);
     }else if(stdRation){
-        await deleRationSubRecode(projectID,nodeInfo.ID);//删除定额下挂的各种数据,如定额工料机等
-        let newRation = await updateRation(stdRation,defaultLibID,nodeInfo.ID,nodeInfo.billsItemID,projectID,calQuantity);//生成并插入新的定额
-        return await addRationSubList(stdRation,newRation,nodeInfo.needInstall,compilation);
+        await deleRationSubRecode(projectID,nodeInfo.ID,cleanzmhs);//删除定额下挂的各种数据,如定额工料机等
+        let newRation = await updateRation(stdRation,defaultLibID,nodeInfo.ID,nodeInfo.billsItemID,projectID,calQuantity,cleanzmhs);//生成并插入新的定额
+        return await addRationSubList(stdRation,newRation,nodeInfo.needInstall,compilation,cleanzmhs);
     }else {
         return null;
     }
 }
 
-async function addRationSubList(stdRation,newRation,needInstall,compilation) {
+async function addRationSubList(stdRation,newRation,needInstall,compilation,cleanzmhs=false) {
     let startTime = +new Date();
     let ration_gljs = await addRationGLJ(stdRation,newRation,compilation);
     let addRationGLJTime = +new Date();
@@ -208,13 +218,16 @@ async function addRationSubList(stdRation,newRation,needInstall,compilation) {
     let addRationCoeTime = +new Date();
     console.log("添加定额coe时间-----"+(addRationCoeTime - addRationGLJTime));
     let ration_installations = [];
-    if(needInstall && stdRation.type == 'std'){//只有标准的定额才有安装增加费,补充的定额没有安装增加费
-        ration_installations =  await addRationInstallFee(stdRation,newRation);
+    let ration_template = [];
+    if(cleanzmhs == false){//清除子目换算即cleanzmh==true时 模板子目、安装增加费不用恢复成标准的
+        if(needInstall && stdRation.type == 'std'){//只有标准的定额才有安装增加费,补充的定额没有安装增加费
+            ration_installations =  await addRationInstallFee(stdRation,newRation);
+        }
+        let addRationInstallFeeTime = +new Date();
+        console.log("添加定额install时间-----"+(addRationInstallFeeTime - addRationCoeTime));
+        //添加定额模板子目
+        ration_template = await addRationTemplate(stdRation,newRation);
     }
-    let addRationInstallFeeTime = +new Date();
-    console.log("添加定额install时间-----"+(addRationInstallFeeTime - addRationCoeTime));
-    //添加定额模板子目
-    let ration_template = await addRationTemplate(stdRation,newRation);
     return {ration:newRation,ration_gljs:ration_gljs,ration_coes:ration_coes,ration_installations:ration_installations,ration_templates:ration_template?[ration_template]:[]};
 }
 
@@ -443,6 +456,9 @@ async function addRationGLJ(std,newRation,compilation) {
                 newGLJ.adjCoe = std_glj.adjCoe;
                 newGLJ.materialType = std_glj.materialType;
                 newGLJ.materialCoe = std_glj.materialCoe;
+                newGLJ.materialIndexType = std_glj.materialIndexType;
+                newGLJ.materialIndexUnit = std_glj.materialIndexUnit;
+                newGLJ.materialIndexCoe = std_glj.materialIndexCoe;
                 newGLJ.createType = 'normal';
                 let info =  await ration_glj_facade.getInfoFromProjectGLJ(newGLJ,unitPriceFileId,ext);
                 newGLJ = ration_glj_facade.createNewRecord(info);
@@ -464,18 +480,22 @@ async function addRationGLJ(std,newRation,compilation) {
     return rationGLJShowList;
 }
 
-async function deleRationSubRecode(projectID,rationID) {//删除挂在定额下的数据,如工程量明细,定额工料机等
+async function deleRationSubRecode(projectID,rationID,cleanzmhs=false) {//删除挂在定额下的数据,如工程量明细,定额工料机等
     let delete_query={projectID: projectID, rationID: rationID};
     //删除工程量明细
-    await deleteSubListByQuery(delete_query) ;
+    await deleteSubListByQuery(delete_query,cleanzmhs) ;
 }
 
-async function deleteSubListByQuery(delete_query) {
-    await quantity_detail.deleteByQuery(delete_query) ;//删除工程量明细
+async function deleteSubListByQuery(delete_query,cleanzmhs=false) {
+    if(cleanzmhs == false){//清空子目换算即cleanzmh==true时不需要清空工程量明细、模板关联子目、安装增加费
+        await quantity_detail.deleteByQuery(delete_query) ;//删除工程量明细
+        await rationInstallationModel.deleteMany(delete_query);//删除安装增加费
+        await rationTemplateModel.deleteMany(delete_query);//删除模板关联子目
+    }
+    //to do稳定土也要删除
+
     await ration_coe.deleteMany(delete_query);//删除附注条件
     await ration_glj.deleteMany(delete_query);//删除定额工料机
-    await rationInstallationModel.deleteMany(delete_query);//删除安装增加费
-    await rationTemplateModel.deleteMany(delete_query);//删除模板关联子目
 }
 
 async function updateCoeAdjust(data,compilation) {
@@ -512,7 +532,7 @@ async function updateCoeAdjust(data,compilation) {
 
 
 
-async function  updateRation(std,defaultLibID,rationID,billsItemID,projectID,calQuantity) {
+async function  updateRation(std,defaultLibID,rationID,billsItemID,projectID,calQuantity,cleanzmh=false) {
     // insertNewRation
     let ration ={};
     ration.code = std.code;
@@ -535,21 +555,20 @@ async function  updateRation(std,defaultLibID,rationID,billsItemID,projectID,cal
     ration.from = std.type === 'complementary' ? 'cpt' : 'std';
     //定额前缀 none:0, complementary:1, borrow: 2
     ration.prefix = '';
-    //借用优先级比补充高
-    if(std.rationRepId !== parseInt(defaultLibID)){//借用
-        ration.prefix = '借';
-    }
-    else if(std.rationRepId === defaultLibID && ration.from === 'cpt') {
-        ration.prefix = '补';
-    }
-    if(std.feeType == undefined || std.feeType == null || std.feeType ==''){//定额取费专业为空的情况下,取项目属性中的定额取费专业ID
-        ration.programID = await getProgramForProject(projectID);
-    }else {
-        ration.programID = std.feeType;
+    if(parseInt(defaultLibID) !== std.rationRepId){//定额是默认定额库中的时,只显示编号;
+        ration.prefix = '借';//定额不是默认定额库中的、且不是补充定额库中的时, 在定额编号前显示“借”。
+        if(ration.from === 'cpt') ration.prefix = '补';//定额是补充定额库中的时,在定额编号前显示“补”;
     }
     ration.rationAssList = createRationAss(std);//生成辅助定额
-    if(calQuantity){
-       await CalculateQuantity(ration,billsItemID,projectID);
+    if(cleanzmh==false){//如果是清空子目换算,即cleanzmh==true 保留定额工程量、工程量表达式、含量(分解系数)、取费专业(取费类别)
+        if(std.feeType == undefined || std.feeType == null || std.feeType ==''){//定额取费专业为空的情况下,取项目属性中的定额取费专业ID
+            ration.programID = await getProgramForProject(projectID);
+        }else {
+            ration.programID = std.feeType;
+        }
+        if(calQuantity){
+            await CalculateQuantity(ration,billsItemID,projectID);
+        }
     }
 
      let unsetObject = {
@@ -599,10 +618,18 @@ async function setEmptyRation(projectID,rationID){
 function createRationAss(std) {
     let  rationAssList = [];//生成辅助定额
     if(std.hasOwnProperty('rationAssList')&&std.rationAssList.length>0){
-        for(let i=0;i<std.rationAssList.length;i++){
-            let ass = std.rationAssList[i];
+        let assGroup =  _.groupBy(std.rationAssList,'name');
+        for(let key in assGroup){
+            let assList = assGroup[key];
+            let ass = assList[0];
             ass._doc.actualValue = ass.stdValue;
+            ass._doc.isAdjust = 0;
             if(_.isString(ass._doc.assistCode)) ass._doc.assistCode = ass._doc.assistCode.replace("\n","");
+            if(_.isString(ass._doc.thirdRationCode)) ass._doc.thirdRationCode = ass._doc.thirdRationCode.replace("\n","");
+            if(assList.length > 1){
+                ass._doc.groupList = JSON.parse(JSON.stringify(assList))  ;
+                ass._doc.maxValue = assList[assList.length-1]._doc.maxValue;
+            }
             rationAssList.push(ass);
         }
     }

+ 9 - 3
modules/main/models/bills.js

@@ -27,13 +27,16 @@ const billType ={
     BX:5//补项
 };
 
+
 class billsModel extends baseModel {
     constructor () {
         super(bills);
     };
-
-    getData (projectID, callback) {
-        this.model.find({'$or': [{projectID: projectID, deleteInfo: null}, {projectID: projectID, 'deleteInfo.deleted': {$in: [null, false]}}]}, '-_id', function(err, datas){
+    getQuery(projectID){
+        return {'$or': [{projectID: projectID, deleteInfo: null}, {projectID: projectID, 'deleteInfo.deleted': {$in: [null, false]}}]}
+    }
+     getData (projectID, callback) {
+        this.model.find(this.getQuery(projectID), '-_id', function(err, datas){
             if (!err) {
                 callback(0, projectConsts.BILLS, datas);
             } else {
@@ -41,6 +44,9 @@ class billsModel extends baseModel {
             };
         });
     };
+    async getDataSync(projectID){
+        return await this.model.find(this.getQuery(projectID),'-_id');
+    }
 
     save (user_id, datas, callback) {
         let functions = [];

+ 8 - 0
modules/main/models/project.js

@@ -17,6 +17,10 @@ var labour_coe_facade = require('../facade/labour_coe_facade');
 var calc_program_facade = require('../facade/calc_program_facade');
 import GLJController from "../../glj/controllers/glj_controller";
 let installation_facade = require('../facade/installation_facade');
+let evaluate_facade = require('../facade/evaluate_facade');
+let bid_facade = require('../facade/bid_facade');
+let contractor_facade = require('../facade/contractor_facade');
+let pmController = require('../../pm/controllers/pm_controller');
 
 
 const ProjectModel = require('../../pm/models/project_model').project;
@@ -43,6 +47,10 @@ moduleMap[projectConsts.CALC_PROGRAM] = calc_program_facade;
 moduleMap[projectConsts.PROJECTGLJ] = new GLJController();
 moduleMap[projectConsts.INSTALLATION_FEE] = installation_facade;
 moduleMap[projectConsts.RATION_TEMPLATE] = ration_template;
+moduleMap[projectConsts.PROJECT_INFO] = pmController;
+moduleMap[projectConsts.EVALUATE_LIST] = evaluate_facade;
+moduleMap[projectConsts.BID_EVALUATION_LIST] = bid_facade;
+moduleMap[projectConsts.contractor_list] = contractor_facade;
 
 var Project = function (){};
 

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

@@ -20,7 +20,11 @@ let projectConst = {
     FEERATE:'feeRate',
     LABOUR_COE:'labour_coe',
     CALC_PROGRAM:'calc_program',
-    INSTALLATION_FEE:'installation_fee'
+    INSTALLATION_FEE:'installation_fee',
+    PROJECT_INFO: 'project_info',
+    EVALUATE_LIST:'evaluate_list',
+    CONTRACTOR_LIST:'contractor_list',
+    BID_EVALUATION_LIST:'bid_evaluation_list'
 };
 
 let projectConstList = [
@@ -48,6 +52,15 @@ let summaryConstList = [
     `SegmentDetail`
 ];
 
+let projectFieldConstList = [
+    `ProjectInfoFields`,        //工程信息指标
+    `ProjectFeatureFields`,     //工程特征指标
+    `ProjectCostFields`,        //工程造价指标
+    `ProjectEcoFields`,         //工程(主要)经济指标
+    `ProjectLabMaterialFields`, //主要工料指标
+    `ProjectQtyFields`          //主要工程量指标
+];
+
 let commonConst = {
     UT_UPDATE: 'ut_update',
     UT_CREATE: 'ut_create',
@@ -62,4 +75,13 @@ const rationType = {
     gljRation: 3,
     install:4
 };
-module.exports = {projectConst: projectConst, commonConst: commonConst, projectConstList: projectConstList,gljKeyArray:gljKeyArray,rationKeyArray:rationKeyArray,rationType:rationType, summaryConstList: summaryConstList};
+module.exports = {
+    projectConst: projectConst,
+    commonConst: commonConst,
+    projectConstList: projectConstList,
+    gljKeyArray:gljKeyArray,
+    rationKeyArray:rationKeyArray,
+    rationType:rationType,
+    summaryConstList: summaryConstList,
+    projectFieldConstList: projectFieldConstList
+};

+ 7 - 3
modules/main/models/ration.js

@@ -16,9 +16,11 @@ class rationModel extends baseModel {
     constructor () {
         super(ration);
     }
-
+    getQuery(projectID){
+        return {'$or': [{projectID: projectID, deleteInfo: null}, {projectID: projectID, 'deleteInfo.deleted': {$in: [null, false]}}]}
+    }
     getData (projectID, callback) {
-        ration.find({'$or': [{projectID: projectID, deleteInfo: null}, {projectID: projectID, 'deleteInfo.deleted': {$in: [null, false]}}]}, '-_id', function(err, datas){
+        ration.find(this.getQuery(projectID), '-_id', function(err, datas){
             if (!err) {
                 callback(0, projectConsts.RATION, datas);
             } else {
@@ -26,7 +28,9 @@ class rationModel extends baseModel {
             }
         });
     };
-
+    async getDataSync(projectID){
+        return await  ration.find(this.getQuery(projectID), '-_id');
+    }
     save (user_id, datas, callback) {
         let functions = [];
         let data;

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

@@ -5,7 +5,10 @@
 
 import BaseController from "../../common/base/base_controller";
 const projectModel = require("../../pm/models/project_model");
+const pmFacade = require('../../pm/facade/pm_facade');
 let config = require("../../../config/config.js");
+import OptionsDao from '../../options/models/optionsModel';
+import optionSetting from '../../options/models/optionTypes';
 module.exports =function (app) {
     const baseController = new BaseController();
     app.get('/main', baseController.init, function(req, res) {
@@ -22,17 +25,29 @@ module.exports =function (app) {
                     //允许协作的项目允许编辑,非只读
                     projectReadOnly = !projectCooperate;
                 }
+                let fileKind = '1'; //默认投标文件
+                let constructProject = await pmFacade.getConstructionProject(req.query.project);
+                if (constructProject && constructProject.property && constructProject.property.fileKind) {
+                    fileKind = constructProject.property.fileKind;
+                }
+                let optionsDao = new OptionsDao();
+                let options = await optionsDao.getOptions(req.session.sessionUser.id, req.session.sessionCompilation._id);
+                if(options){
+                    options = await optionsDao.saveOptions(req.session.sessionUser.id, req.session.sessionCompilation._id, optionSetting);
+                }
                 res.render('building_saas/main/html/main.html',
                     {
                         userAccount: req.session.userAccount,
                         userID: req.session.sessionUser.id,
                         projectData: projectData,
                         compilationName: req.session.sessionCompilation.name,
-                        versionName: `纵横建筑云计价(${req.session.compilationVersion})`,
+                        versionName: req.session.compilationVersion,
                         projectReadOnly: projectReadOnly,
                         projectCooperate: projectCooperate,
                         LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
-                        overWriteUrl:req.session.sessionCompilation.overWriteUrl
+                        overWriteUrl:req.session.sessionCompilation.overWriteUrl,
+                        fileKind: fileKind,
+                        options:JSON.stringify(options)
                     });
             } else {
                 res.redirect('/pm');

+ 2 - 1
modules/main/routes/project_route.js

@@ -15,7 +15,8 @@ module.exports = function (app) {
     projectRouter.post('/calcInstallationFee', projectController.calcInstallationFee);
     projectRouter.post('/saveProperty', projectController.saveProperty);
     projectRouter.post('/getDefaultColSetting', projectController.getDefaultColSetting);
-
+    projectRouter.post('/getSEIProjects', projectController.getSEIProjects);
+    projectRouter.post('/loadSEIProjectData', projectController.loadSEIProjectData);
     app.use('/project',projectRouter);
 };
 

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

@@ -9,6 +9,7 @@ let async = require('async');
 const uuidV1 = require('uuid/v1');
 const mongoose = require('mongoose');
 let mainColLibModel = mongoose.model('std_main_col_lib');
+const billsUtil = require('../../../public/billsUtil');
 
 import BillsTemplateModel from "../models/templates/bills_template_model";
 import EngineeringLibModel from "../../users/models/engineering_lib_model";
@@ -27,11 +28,16 @@ module.exports = {
                 for(let bill of billsDatas){
                     uuidMaping[bill.ID] = uuidV1();
                 }
+                const reg = /@\d+/;
                 billsDatas.forEach(function (template) {
                     template.projectID = newProjID;
                     template.ID = uuidMaping[template.ID] ? uuidMaping[template.ID] : -1;
                     template.ParentID = uuidMaping[template.ParentID] ? uuidMaping[template.ParentID] : -1;
                     template.NextSiblingID = uuidMaping[template.NextSiblingID] ? uuidMaping[template.NextSiblingID] : -1;
+                    const needToParseCalcBase = template.calcBase && reg.test(template.calcBase);
+                    if (needToParseCalcBase) {
+                        template.calcBase = billsUtil.parseCalcBase(template.calcBase, uuidMaping);
+                    }
                 });
                 billsData.insertData(billsDatas, callback);
             },

+ 214 - 24
modules/pm/controllers/pm_controller.js

@@ -27,7 +27,11 @@ const fs = require('fs');
 const _ = require('lodash');
 import SectionTreeDao from '../../complementary_ration_lib/models/sectionTreeModel';
 let sectionTreeDao = new SectionTreeDao();
-
+let consts = require('../../main/models/project_consts');
+const rationLibModel = mongoose.model('std_ration_lib_map');
+const multiparty = require("multiparty");
+let logger = require("../../../logs/log_helper").logger;
+let rp = require('request-promise');
 //统一回调函数
 let callback = function(req, res, err, message, data){
     res.json({error: err, message: message, data: data});
@@ -77,7 +81,7 @@ module.exports = {
     },
     updateProjects: async function (req, res) {
         let data = JSON.parse(req.body.data);
-        await ProjectsData.updateUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, req.session.sessionCompilation.name, data.updateData, function (err, message, data) {
+        await ProjectsData.updateUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, req.session.sessionCompilation.name,req.session.sessionCompilation.overWriteUrl, data.updateData, function (err, message, data) {
             if (err === 0) {
                 callback(req, res, err, message, data);
             } else {
@@ -113,6 +117,14 @@ module.exports = {
                 }
                 delete datas.properties['property.basicInformation'];
             }
+            //建设项目-编制说明,更新建设项目
+            if (datas.properties['property.compilationIllustrationProject']) {
+                let constructionProject = await pm_facade.getConstructionProject(datas.projectID);
+                if (constructionProject) {
+                    functions.push(updateFunc(projectModel, {ID: constructionProject.ID}, {'property.compilationIllustration': datas.properties['property.compilationIllustrationProject']}));
+                }
+                delete datas.properties['property.compilationIllustrationProject'];
+            }
             functions.push(updateFunc(projectModel, {ID: datas.projectID}, datas.properties));
         };
 
@@ -127,14 +139,14 @@ module.exports = {
         };
 
         // 清单:每文档doc只存储一条清单,每条清单都必须定位一次文档,无法合并处理
-        if (datas.bills.length > 0){
+        if (datas.bills && datas.bills.length > 0){
             for (let bill of datas.bills){
                 functions.push(updateFunc(billsModel, {projectID: datas.projectID, ID: bill.ID, deleteInfo: null}, bill));
             };
         };
 
         // 定额:每文档doc只存储一条定额,每条定额都必须定位一次文档,无法合并处理
-        if (datas.rations.length > 0){
+        if (datas.rations&& datas.rations.length > 0){
             for (let ration of datas.rations){
                 functions.push(updateFunc(rationsModel, {projectID: datas.projectID, ID: ration.ID, deleteInfo: null}, ration));
             };
@@ -188,6 +200,50 @@ module.exports = {
             callback(req, res, err, message, null);
         });
     },
+    //project getData接口
+    getData: function(projectID, callback) {
+        projectModel.findOne({$or: [{deleteInfo: null}, {'deleteInfo.deleted': false}], ID: projectID}, '-_id').then(async function (project) {
+            if (!project) {
+                callback('', consts.projectConst.PROJECT_INFO, {});
+            }
+            let engineeringLibModel = new EngineeringLibModel();
+            let engineeringInfo = project !== null && project.property.engineering_id !== undefined ?
+                await engineeringLibModel.getEngineering(project.property.engineering_id) : null;
+            //查找定额库的定额库编码
+            if (Array.isArray(engineeringInfo.ration_lib)) {
+                let rationLibIDs = engineeringInfo.ration_lib.map(data => data.id);
+                let rationLibs = await rationLibModel.find({ID: {$in: rationLibIDs}}, 'ID libCode');
+                for (let rationLib of rationLibs) {
+                    let lib = engineeringInfo.ration_lib.find(data => data.id == rationLib.ID);
+                    lib.libCode = rationLib.libCode;
+                }
+            }
+            let projInfo = project._doc;
+            if (engineeringInfo !== null) {
+                if(engineeringInfo.billsGuidance_lib){
+                    for(let billsGuidanceLib of engineeringInfo.billsGuidance_lib){
+                        let stdBillsGuidanceLib = await stdBillsGuidanceLibModel.findOne({ID: billsGuidanceLib.id});
+                        if(stdBillsGuidanceLib){
+                            billsGuidanceLib.type = stdBillsGuidanceLib.type ? stdBillsGuidanceLib.type : 1;
+                        }
+                    }
+                }
+                projInfo.engineeringInfo = engineeringInfo;
+            }
+            //读取建设项目的项目属性
+            let constructionProperty = await ProjectsData.getConstructionProperty(projectID);
+            //基本信息
+            projInfo.property.basicInformation = constructionProperty && constructionProperty.basicInformation ? constructionProperty.basicInformation : [];
+            //编制说明
+            projInfo.property.compilationIllustrationProject = constructionProperty && constructionProperty.compilationIllustration ? constructionProperty.compilationIllustration : '';
+            //获取单位工程完整目录结构
+            let fullPath = await pm_facade.getFullPath(projectID);
+            projInfo.fullPath = fullPath;
+            callback('', consts.projectConst.PROJECT_INFO, project);
+        }, function (err) {
+            callback(err, consts.projectConst.PROJECT_INFO, {});
+        });
+    },
     getProject: function(req, res){
         let data = JSON.parse(req.body.data);
         let projectID = data.proj_id;
@@ -209,11 +265,14 @@ module.exports = {
                     }
                     projInfo.engineeringInfo = engineeringInfo;
                 }
-                //读取建设项目的基本信息
-                let basicInfo = await ProjectsData.getBasicInfo(projectID);
-                if(basicInfo !== null){
-                    projInfo.property.basicInformation = basicInfo;
-                }
+                //读取建设项目的项目属性
+                let constructionProperty = await ProjectsData.getConstructionProperty(projectID);
+                console.log(projectID);
+                console.log(constructionProperty);
+                //基本信息
+                projInfo.property.basicInformation = constructionProperty && constructionProperty.basicInformation ? constructionProperty.basicInformation : [];
+                //编制说明
+                projInfo.property.compilationIllustrationProject = constructionProperty && constructionProperty.compilationIllustration ? constructionProperty.compilationIllustration : '';
                 //获取单位工程完整目录结构
                 let fullPath = await pm_facade.getFullPath(projectID);
                 projInfo.fullPath = fullPath;
@@ -248,14 +307,7 @@ module.exports = {
         request.session.sessionCompilation = compilationData;
         sessionCompilation = request.session.sessionCompilation;
         //更新用户的使用过的费用定额列表
-        let userData = await userModel.findOne({_id: mongoose.Types.ObjectId(request.session.sessionUser.id)}, '-_id used_list');
-        //是否第一次进入该费用定额
-        let isFirst = false;
-        if (userData) {
-            isFirst = !_.find(userData.used_list, function (o) {
-                return o.compilationId === compilationData._id.toString();
-            });;
-        }
+        let isFirst = await pm_facade.isFirst(request.session.sessionUser.id, compilationData._id.toString());
         // 清单计价
         let billValuation = sessionCompilation.bill_valuation !== undefined ?
             sessionCompilation.bill_valuation : [];
@@ -270,17 +322,18 @@ module.exports = {
         rationValuation = await engineeringLibModel.getLib(rationValuation);
         let absoluteUrl = compilationData.overWriteUrl ? request.app.locals.rootDir + compilationData.overWriteUrl : request.app.locals.rootDir;
         let overWriteUrl = fs.existsSync(absoluteUrl) && fs.statSync(absoluteUrl).isFile()? compilationData.overWriteUrl : null;
+        //let valuationOpts = billValuation.map(data => {return {name: data.name, id: data.id}}).reverse();
         let renderData = {
             isFirst: isFirst,
             userAccount: request.session.userAccount,
             userID: request.session.sessionUser.id,
             compilationData: JSON.stringify(sessionCompilation),
             overWriteUrl: overWriteUrl,
-            billValuation: JSON.stringify(billValuation),
+            billValuation: JSON.stringify(billValuation.reverse()), // 按最新排序
             rationValuation: JSON.stringify(rationValuation),
             engineeringList: JSON.stringify(engineering.List),
             compilationName: sessionCompilation.name,
-            versionName: `纵横建筑云计价(${request.session.compilationVersion})`,
+            versionName: request.session.compilationVersion,
             LicenseKey:config.getLicenseKey(process.env.NODE_ENV)
         };
 
@@ -442,10 +495,11 @@ module.exports = {
     },
 
     recGC: function(request, response){
-        let userID = request.session.sessionUser.id;
+        let userID = request.session.sessionUser.id,
+            compilationId = request.session.sessionCompilation._id;
         let data = JSON.parse(request.body.data);
         let nodes = data.nodes;
-        ProjectsData.recGC(userID, nodes, function (err, msg, data) {
+        ProjectsData.recGC(userID, compilationId, nodes, function (err, msg, data) {
             callback(request, response, err, msg, data);
         });
    },
@@ -500,8 +554,8 @@ module.exports = {
             error:0
         };
         try {
-            let data = JSON.parse(req.body.data);
-            result.data = await pm_facade.copyProject(req.session.sessionUser.id, req.session.sessionCompilation._id,data);
+            let data = {dataString:req.body.data,userID:req.session.sessionUser.id,compilationID:req.session.sessionCompilation._id};
+            result = await redirectToImportServer(data,"copyProject",req);
         }catch (err){
             console.log(err);
             result.error=1;
@@ -561,6 +615,8 @@ module.exports = {
                     if (proj.projType === projType.tender) {
                         //设置工程专业
                         proj._doc.feeStandardName = proj.property.feeStandardName || '';
+                        //设置计税方法
+                        proj._doc.taxType = proj.property.taxType;
                     }
                     delete proj._doc.property;
                 }
@@ -586,6 +642,8 @@ module.exports = {
                             } else if (projC.projType === projType.tender) {
                                 //设置工程专业
                                 projC._doc.feeStandardName = projC.property.feeStandardName || '';
+                                //设置计税方法
+                                projC._doc.taxType = projC.property.taxType;
                                 if (proj.projType === projType.engineering) {
                                     ungroupedTenders.push(projC._doc);
                                 }
@@ -657,5 +715,137 @@ module.exports = {
             console.log(err);
             callback(req, res, 1, err, null);
         }
+    },
+    exportProject:async function(req,res){
+        let result={
+            error:0
+        };
+        try {
+            let data = {dataString:req.body.data,userID:req.session.sessionUser.id};
+            result =  await redirectToImportServer(data,"exportProject",req);
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
+
+    },
+    importProject:async function(req,res){
+        let form = new multiparty.Form({uploadDir: './tmp'});
+        let path = "";
+        form.parse(req, async function (err, fields, files) {
+            try {
+                console.log(fields);
+                const file = typeof files.file !== 'undefined' ? files.file[0] : null;
+                if (err || !file) {
+                    throw '上传失败。';
+                };
+                path = file.path;
+                let data = fs.readFileSync(file.path,'utf-8');
+                let body = {data: data, fields:fields, session:req.session};
+                let result = await redirectToImportServer(body,"importProject",req);
+                res.json(result);
+            }catch (e){
+                console.log(e);
+                res.json({error:1,msg:"导入失败请检查文件!"})
+            }finally {
+                fs.unlinkSync(path);
+            }
+
+        })
+    },
+    getBasicInfo: async function(req, res) {
+        try {
+            let data = JSON.parse(req.body.data);
+            let infoLib = await pm_facade.getBasicInfo(req.session.sessionCompilation._id, data.fileKind);
+            callback(req, res, 0, 'success', infoLib ? infoLib.info : []);
+        } catch (err) {
+            console.log(err);
+            callback(req, res, 1, err, []);
+        }
+    },
+    getProjectFeature: async function(req, res) {
+        try {
+            let data = JSON.parse(req.body.data);
+            let featureLib = await pm_facade.getProjectFeature(data.valuationID, data.engineeringName, data.feeName);
+            //工程专业设置为费用标准名称
+            if (featureLib) {
+                let engData = featureLib.feature.find(function (d) {
+                    return d.key === 'engineering';
+                });
+                if (engData) {
+                    engData.value = data.feeName;
+                }
+            }
+            callback(req, res, 0, 'success', featureLib ? featureLib.feature : []);
+        } catch (err) {
+            console.log(err);
+            callback(req, res, 1, err, []);
+        }
+    },
+    getProjectByGranularity: async function(req, res) {
+        try {
+            let data = JSON.parse(req.body.data);
+            let projData = await pm_facade.getProjectByGranularity(data.tenderID, data.granularity, req.session.sessionUser.id, req.session.compilationVersion);
+            callback(req, res, 0, 'success', projData);
+        } catch (err) {
+            callback(req, res, 1, err, null);
+        }
+    },
+    getProjectPlaceholder: async function(req, res) {
+        let data = JSON.parse(req.body.data);
+        try {
+            let countRst = await pm_facade.getProjectPlaceholder(data);
+            callback(req, res, 0, 'succes', countRst);
+        } catch (err) {
+            console.log(err);
+            callback(req, res, 1, err, null);
+        }
+    },
+    importInterface: async function(req, res) {
+        logger.info(`${req.ip} importInterface`);
+        const uploadOption = {
+            uploadDir: './public'
+        };
+        let uploadFullName = '';
+        const form = new multiparty.Form(uploadOption);
+        form.parse(req, async function(err, fields, files) {
+            try {
+                let file = files.file && files.file.length ? files.file[0] : null;
+                if (!file) {
+                    throw '无有效数据';
+                }
+                uploadFullName = file.path;
+                //获取源数据
+                let srcStr = fs.readFileSync(file.path, 'utf8');
+                if (!srcStr) {
+                    throw '无有效数据'
+                }
+                let importData = JSON.parse(srcStr);
+                fs.unlinkSync(file.path);
+                let projectData = await pm_facade.importProject(importData, req.session.sessionUser.id, req.session.sessionCompilation._id);
+                callback(req, res, 0, '', projectData);
+            } catch (err) {
+                console.log(err);
+                if(uploadFullName && fs.existsSync(uploadFullName)){
+                    fs.unlinkSync(uploadFullName);
+                }
+                callback(req, res, 1, err, null);
+            }
+        });
     }
-};
+};
+
+async function redirectToImportServer(data,action,req) {
+    let importURL = config.getImportURL(process.env.NODE_ENV,req.headers.origin);
+    let options = {
+        method: 'POST',
+        uri: `http://${importURL}/import/${action}`,
+        body: data,
+        timeout:220000,
+        json: true
+    };
+    console.log("post import data to:"+options.uri);
+    return await rp.post(options);
+}

File diff suppressed because it is too large
+ 1092 - 69
modules/pm/facade/pm_facade.js


+ 83 - 49
modules/pm/models/project_model.js

@@ -28,7 +28,8 @@ import {
     projectFeature,
     displaySetting,
     calcOptions,
-    tenderSetting
+    tenderSetting,
+    G_FILE_VER
 } from './project_property_template';
 import optionSetting from '../../options/models/optionTypes';
 import fixedFlag from '../../common/const/bills_fixed';
@@ -41,6 +42,8 @@ let feeRateFacade = require('../../fee_rates/facade/fee_rates_facade');
 let labourCoeFacade = require('../../main/facade/labour_coe_facade');
 let calcProgramFacade = require('../../main/facade/calc_program_facade');
 let installationFacade = require('../../main/facade/installation_facade');
+let projectFacade = require('../../main/facade/project_facade');
+
 let pmFacade = require('../facade/pm_facade');
 let logger = require("../../../logs/log_helper").logger;
 let BillsModel = require("../../main/models/bills").model;
@@ -54,7 +57,7 @@ let optionModel = mongoose.model('options');
 function ProjectsDAO() {
 }
 
-let G_FILE_VER = '1.0.1';
+//let G_FILE_VER = '1.0.1';
 
 ProjectsDAO.prototype.getUserProjects = async function (userId, compilation, callback) {
     try {//
@@ -64,7 +67,7 @@ ProjectsDAO.prototype.getUserProjects = async function (userId, compilation, cal
                 'compilation': compilation,
                 'deleteInfo': null
             }, {'userID': userId, 'compilation': compilation, 'deleteInfo.deleted': {'$in': [null, false]}}]
-        }, '-_id');
+        }, '-_id', {lean: true});
         let projIDs= [];
         for(let project of projects){
             if(project.projType === projectType.project){
@@ -76,16 +79,16 @@ ProjectsDAO.prototype.getUserProjects = async function (userId, compilation, cal
         for(let proj of projects){
             let summaryProj = summaryInfo[proj.ID];
             if(summaryProj){
-                proj._doc.engineeringCost = summaryProj.engineeringCost;
-                proj._doc.subEngineering = summaryProj.subEngineering;
-                proj._doc.measure = summaryProj.measure;
-                proj._doc.safetyConstruction = summaryProj.safetyConstruction;
-                proj._doc.other = summaryProj.other;
-                proj._doc.charge = summaryProj.charge;
-                proj._doc.tax = summaryProj.tax;
-                proj._doc.rate = summaryProj.rate;
-                proj._doc.buildingArea = summaryProj.buildingArea;
-                proj._doc.perCost = summaryProj.perCost;
+                proj.engineeringCost = summaryProj.engineeringCost;
+                proj.subEngineering = summaryProj.subEngineering;
+                proj.measure = summaryProj.measure;
+                proj.safetyConstruction = summaryProj.safetyConstruction;
+                proj.other = summaryProj.other;
+                proj.charge = summaryProj.charge;
+                proj.tax = summaryProj.tax;
+                proj.rate = summaryProj.rate;
+                proj.buildingArea = summaryProj.buildingArea;
+                proj.perCost = summaryProj.perCost;
             }
         }
         callback(0, '', projects);
@@ -109,7 +112,7 @@ ProjectsDAO.prototype.getUserProject = function (userId, ProjId, callback) {
     });
 };
 
-ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId, compilationName, datas, callback) {
+ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId, compilationName,overWriteUrl, datas, callback) {
     let data, project, updateLength = 0, hasError = false, deleteInfo = null, i, newProject;
     let updateAll = function (err) {
         if (!err) {
@@ -138,16 +141,15 @@ ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId
                 data.updateData['compilation'] = compilationId;
                 data.updateData['createDateTime'] = new Date();
                 data.updateData['fileVer'] = G_FILE_VER;
-                if(data.updateData.projType === projectType.project){
+                /*if(data.updateData.projType === projectType.project){
                     //设置建设项目基本信息,多个单位工程共用
                     data.updateData.property = {basicInformation: basicInformation};
-                }
+                }*/
                 // 如果没有选中单价文件则新增单价文件
                 if (data.updateData.projType === projectType.tender && data.updateData.property !== null &&
                     Object.keys(data.updateData.property.unitPriceFile).length > 0 &&
                     data.updateData.property.unitPriceFile.id === '') {
                     let unitPriceFileModel = new UnitPriceFileModel();
-
                     let insertData = {
                         name: data.updateData.name,
                         project_id: data.updateData.ID,
@@ -168,21 +170,14 @@ ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId
                     data.updateData.property.decimal = defaultDecimal;
                     //清单工程量精度
                     data.updateData.property.billsQuantityDecimal = billsQuantityDecimal;
-                    //基本信息-挪到建设项目下,多个单位工程共用
-                    //basicInformation[0]['items'][1]['value'] = data.updateData.property.engineeringName || '';
-                    //data.updateData.property.basicInformation = basicInformation;
-                    //工程特征
-                    if(data.updateData.property.featureLibID){
-                        //工程专业显示费用定额的名称
-                        data.updateData.property.projectFeature = await pmFacade.getProjectFeature(data.updateData.property.featureLibID, data.updateData.property.feeStandardName);
-                    }
-                    /*projectFeature[0]['value'] = data.updateData.property.engineeringName || '';
-                    data.updateData.property.projectFeature = projectFeature;*/
-
                     //呈现选项
                     data.updateData.property.displaySetting = displaySetting;
 
                     data.updateData.property.billsCalcMode = 0;
+                    if(overWriteUrl && overWriteUrl !=""){
+                        let overWrite = require("../../.."+overWriteUrl);
+                        if(overWrite.getBillsCalcMode) data.updateData.property.billsCalcMode = overWrite.getBillsCalcMode();//重写清单计费取费方式
+                    }
                     data.updateData.property.zanguCalcMode = 0;
                     //计算选项
                     data.updateData.property.calcOptions = calcOptions;
@@ -194,15 +189,29 @@ ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId
                     data.updateData.property.lockBills = false;
                     //工料机单价调整系数
                     data.updateData.property.tenderSetting = tenderSetting;
+                     //添加重庆经济指标数据
+                    await projectFacade.setSEILibData(data.updateData.property);
+                    data.updateData.property.overHeight = await pmFacade.initOverHeightItems(data.updateData.property.engineering_id);
+                    if (data.updateData.property.overHeight.length) {
+                        // 对应清单或分部下
+                        data.updateData.property.overHeightOption = 1;
+                    }
+                }else if (data.updateData.projType === projectType.project) {
+                    //更新基本信息
+                    data.updateData.property.basicInformation.forEach(function (pData) {
+                        pData.items.forEach(function (item) {
+                            if (item.key === 'engineeringName') {
+                                item.value = data.updateData.name;
+                            } else if (item.key === 'fileKind') {
+                                item.value = {'1': '投标', '2': '招标'}[data.updateData.property.fileKind]
+                            } else if (item.key === 'taxModel') {
+                                item.value = {'1': '一般计税法', '2': '简易计税法'}[data.updateData.property.taxType]
+                            }
+                        });
+                    });
                 }
-
                 newProject = new Projects(data.updateData);
-               /* // 查找同级是否存在同名数据
-                let exist = await this.isExist(userId, compilationId, data.updateData.name, data.updateData.ParentID);
-                if (exist) {
-                    callback(1, '同级目录已存在相同名称数据.', null);
-                    return;
-                }*/
+
                 if (data.updateData.projType === 'Tender') {
                     let feeRateFileID = await feeRateFacade.newFeeRateFile(userId, data.updateData);
                     newProject.property.feeFile = feeRateFileID ? feeRateFileID : -1;
@@ -308,6 +317,11 @@ ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId
     }
 };
 
+//导入接口数据
+ProjectsDAO.prototype.importInterface = async function (srcData) {
+
+};
+
 ProjectsDAO.prototype.udpateUserFiles = async function (userId, datas, callback) {
     let updateType = {update: 'update', delete: 'delete'};
     let deleteInfo = Object.create(null);
@@ -322,14 +336,14 @@ ProjectsDAO.prototype.udpateUserFiles = async function (userId, datas, callback)
                 await Projects.update({
                     userID: userId,
                     'property.unitPriceFile.id': data.updateData.id
-                }, {$set: {'property.unitPriceFile.name': data.updateData.name}});
+                }, {$set: {'property.unitPriceFile.name': data.updateData.name}}, {multi: true});
             }
             else if (data.updateType === updateType.update && data.fileType === fileType.feeRateFile) {
                 await FeeRateFiles.update({ID: data.updateData.ID}, data.updateData);
                 await Projects.update({
                     userID: userId,
                     'property.feeFile.id': data.updateData.ID
-                }, {$set: {'property.feeFile.name': data.updateData.name}});
+                }, {$set: {'property.feeFile.name': data.updateData.name}}, {multi: true});
             }
             else if (data.updateType === updateType.delete && data.fileType === fileType.unitPriceFile) {
                 data.updateData.deleteInfo = deleteInfo;
@@ -398,13 +412,23 @@ ProjectsDAO.prototype.rename = async function (userId, compilationId, data, call
             throw '请填写名称!';
         }
         data.newName = data.newName.trim();
-        //重名前端控制
-       /* // 查找同级是否存在同名数据
-        let exist = await this.isExist(userId, compilationId, data.newName, data.parentID);
-        if (exist) {
-            throw '同级目录已存在相同名称数据';
-        }*/
-
+        let project = await Projects.findOne({ID: data.id});
+        if (!project) {
+            throw '项目不存在';
+        }
+        if (project.projType === 'Project' && project.property && Array.isArray(project.property.basicInformation)) {
+            //更新基本信息
+            project.property.basicInformation.forEach(function (pData) {
+                pData.items.forEach(function (item) {
+                    if (item.key === 'engineeringName') {
+                        item.value = data.newName;
+                    }
+                });
+            });
+            await Projects.update({ID: data.id}, {$set: {name: data.newName, 'property.basicInformation': project.property.basicInformation}});
+        } else {
+            await Projects.update({ID: data.id}, {$set: {name: data.newName}});
+        }
         Projects.update({userID: userId, ID: data.id}, {name: data.newName}, function (err) {
             if (err) {
                 throw '项目不存在';
@@ -482,8 +506,8 @@ ProjectsDAO.prototype.getGCFiles = async function (fileType, userID) {
     return rst;
 };
 
-ProjectsDAO.prototype.getFirstNodeID = async function (userID, pid) {
-    let nodes = await Projects.find({userID: userID, ParentID: pid, deleteInfo: null});
+ProjectsDAO.prototype.getFirstNodeID = async function (userID, compilationId, pid) {
+    let nodes = await Projects.find({userID: userID, compilation: compilationId, ParentID: pid, deleteInfo: null});
     if (nodes.length === 0) {
         return -1;
     }
@@ -514,7 +538,7 @@ ProjectsDAO.prototype.getFirstNodeID = async function (userID, pid) {
     }
 };
 
-ProjectsDAO.prototype.recGC = async function (userID, datas, callback) {
+ProjectsDAO.prototype.recGC = async function (userID, compilationId, datas, callback) {
     let functions = [];
     let updateDatas = [];
     for (let i = 0, len = datas.length; i < len; i++) {
@@ -537,7 +561,7 @@ ProjectsDAO.prototype.recGC = async function (userID, datas, callback) {
                         datas[i].updateData.ParentID = -1;
                     }
                 }
-                let firstNodeID = await this.getFirstNodeID(userID, projPid);
+                let firstNodeID = await this.getFirstNodeID(userID, compilationId, projPid);
                 datas[i].updateData.NextSiblingID = firstNodeID;
             }
             updateDatas.push(datas[i])
@@ -814,7 +838,17 @@ ProjectsDAO.prototype.getSummaryFees = async function (ID) {
 ProjectsDAO.prototype.updateUnitFileToProject=async function(projectID,unitFile){
     return await Projects.findOneAndUpdate({'ID':projectID},{'property.unitPriceFile':unitFile});
 }
+//获取建设项目项目属性
+ProjectsDAO.prototype.getConstructionProperty = async function (projectID) {
+    //获取建设项目
+    let constructionProject = await pmFacade.getConstructionProject(projectID);
+    if(!constructionProject || !constructionProject.property){
+        return null;
+    }
+    return constructionProject.property;
+};
 
+//获取建设项目的基本信息
 ProjectsDAO.prototype.getBasicInfo = async function (projectID) {
     //获取建设项目
     let constructionProject = await pmFacade.getConstructionProject(projectID);
@@ -850,4 +884,4 @@ ProjectsDAO.prototype.defaultSettings = async function (userID, compilationId, p
     await optionModel.update({user_id: userID, compilation_id: compilationId}, {$set: {options: optionSetting}});
     await Projects.update({ID: projectID}, {$set: {property: cloneProperty}});
     return true;
-};
+};

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

@@ -80,7 +80,6 @@ const billsQuantityDecimal = [
 const basicInformation = [
     {dispName: '基本信息', key: 'basicInfo', items: [
         {dispName: '合同号', key: 'contractNum', value: ''},
-       // {dispName: '工程专业', key: 'engineering', value: ''},//只读,用户新建单位工程时选择的值
         {dispName: '建设地点', key: 'constructionPlace', value: ''},
         {dispName: '质量标准', key: 'qualityStandard', value: ''},
         {dispName: '工程类别', key: 'projectCategory', value: ''},
@@ -139,4 +138,6 @@ const projectFeature = [
     {dispName: '门窗材料及装饰', key: 'doorsWindowsMaterial', value: ''}
 ];
 
-export {defaultDecimal, billsQuantityDecimal, basicInformation, projectFeature,displaySetting,calcOptions,tenderSetting};
+let G_FILE_VER = '1.0.1';
+
+export {defaultDecimal, billsQuantityDecimal, basicInformation, projectFeature,displaySetting,calcOptions,tenderSetting, G_FILE_VER};

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

@@ -14,7 +14,7 @@ module.exports = function (app) {
     let pmRouter = express.Router();
 
     pmRouter.use(function (req, res, next) {
-        if (/\/getNewProjectID/.test(req.originalUrl) || pmController.checkRight(req, res)) {
+        if (/\/getNewProjectID/.test(req.originalUrl) ||/\/importProject/.test(req.originalUrl)|| pmController.checkRight(req, res)) {
             next();
         } else {
             res.json({error: 1, message: '对不起,您无权限操作。', data: null});
@@ -61,8 +61,18 @@ module.exports = function (app) {
     pmRouter.post('/share', pmController.share);
     pmRouter.post('/receiveProjects', pmController.receiveProjects);
     pmRouter.post('/changeFile', pmController.changeFile);
+    pmRouter.post('/exportProject', pmController.exportProject);
+    pmRouter.post('/importProject', pmController.importProject);
+    pmRouter.post('/getBasicInfo', pmController.getBasicInfo);
+    pmRouter.post('/getProjectFeature', pmController.getProjectFeature);
+    pmRouter.post('/getProjectByGranularity', pmController.getProjectByGranularity);
 
     app.use('/pm/api', pmRouter);
+
+    let importRouter = express.Router();
+    importRouter.post('/getProjectPlaceholder', pmController.getProjectPlaceholder);
+    importRouter.post('/importInterface', pmController.importInterface);
+    app.use('/pm/import', importRouter);
 };
 
 

+ 25 - 0
modules/ration_glj/controllers/ration_glj_controller.js

@@ -10,6 +10,7 @@ module.exports={
     createRationGLJ:createRationGLJ,
     testGetQuantify:testGetQuantify,
     getGLJData:getGLJData,
+    getGLJDataPaging: getGLJDataPaging,
     getGLJDataByCodes:getGLJDataByCodes,
     addGLJ:addGLJ,
     replaceGLJ:replaceGLJ,
@@ -56,6 +57,30 @@ async function getGLJDataByCodes(req,res){
     res.json(result);
 }
 
+async function getGLJDataPaging(req, res) {
+    let result={
+        error:0
+    };
+    let data = JSON.parse(req.body.data);
+    try {
+        let gljLibId = await ration_glj_facade.getGLJLibByEngineerID(data.engineerID);
+        let info = {
+            gljLibId,
+            userID: req.session.sessionUser.id,
+            compilationId: req.session.sessionCompilation._id
+        };
+        result.data = await ration_glj_facade.getGLJDataPaging(info, data.condition);
+        if (req.session.sessionCompilation.priceProperties) {
+            result.data.priceProperties = req.session.sessionCompilation.priceProperties
+        }
+        res.json(result);
+    } catch (err) {
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+        res.json(result);
+    }
+}
 
 async function getGLJData(req, res) {
     let result={

+ 69 - 11
modules/ration_glj/facade/glj_calculate_facade.js

@@ -46,9 +46,9 @@ async function calculateQuantity(query,noNeedCal,refreshRationName = false){
              glj_result:[],
              rationID:query.rationID
          };
-         let impactRation = await ration.findOne({ID:query.rationID,projectID:query.projectID});
+         let impactRation = await ration.findOne({ID:query.rationID});
          let gljList = await ration_glj.find(query);//{projectID:query.projectID,rationID:query.rationID}
-         let coeList = await ration_coe.find({projectID:query.projectID,rationID:query.rationID}).sort('seq').exec();
+         let coeList = await ration_coe.find({rationID:query.rationID}).sort('seq').exec();
          let assList=[];
          let assRation = null;
          let adjustState=[];
@@ -56,22 +56,37 @@ async function calculateQuantity(query,noNeedCal,refreshRationName = false){
              return null;
          }
          if(impactRation._doc.hasOwnProperty("rationAssList")&&impactRation.rationAssList.length>0){
+             let temTimes = [];
+             let thirdRationCodes=[];
              for(let i=0;i<impactRation.rationAssList.length;i++){
                  let times = calculateTimes(impactRation.rationAssList[i]);
                  if(times!=0){
+                     let thirdRationCode = impactRation.rationAssList[i].thirdRationCode;
+                     if(thirdRationCode && thirdRationCode !=''){
+                         temTimes.push(times);
+                         thirdRationCodes.push(thirdRationCode)
+                     }
                      assRation = await  std_ration_lib_ration_items.findOne({rationRepId:impactRation.libID,code:impactRation.rationAssList[i].assistCode});
                      assList.push({times:times,assRation:assRation});
                      adjustState.push({index:stateSeq.ass,content:impactRation.rationAssList[i].name+" "+impactRation.rationAssList[i].actualValue+" : +"+impactRation.rationAssList[i].assistCode+"x"+times});
                  }
              }
+             if(temTimes.length == 2 &&thirdRationCodes[0] == thirdRationCodes[1] ){ //说明有第三定额
+                 let times_t = temTimes[0] * temTimes[1];
+                 let tration =  await  std_ration_lib_ration_items.findOne({rationRepId:impactRation.libID,code:thirdRationCodes[0]});
+                 if(tration){
+                     assList.push({times:times_t,assRation:tration});
+                     adjustState.push({index:stateSeq.ass,content:"+"+thirdRationCodes[0]+"x"+times_t});
+                 }
+             }
          }
          gljList = sortRationGLJ(gljList);
          for(let i =0;i<gljList.length;i++ ){
              let r = await calculateQuantityPerGLJ(gljList[i],gljList,coeList,assList,adjustState,noNeedCal);
-             result.glj_result.push(r);
+             if(quantityUpdateCheck(gljList[i],r) == true) result.glj_result.push(r);
          }
 
-         if(noNeedCal==null){
+         if(noNeedCal==null && result.glj_result.length > 0){
             await ration_glj.bulkWrite(generateUpdateTasks(result.glj_result));
          }
          adjustState= _.sortByOrder(adjustState, ['index'], ['asc']);
@@ -83,7 +98,7 @@ async function calculateQuantity(query,noNeedCal,refreshRationName = false){
              setData.name = newName;
              result.rationName = newName;
          }
-         await ration.update({projectID:query.projectID,ID:query.rationID,deleteInfo: null},setData);
+         await ration.update({ID:query.rationID},setData);
          result.adjustState=adjustStateString;
          return result;
     }catch (err){
@@ -92,12 +107,36 @@ async function calculateQuantity(query,noNeedCal,refreshRationName = false){
     }
 }
 
+function quantityUpdateCheck(glj,r) {//检查,有改变的才更新
+    for(let key in r.doc){
+        if(glj._doc[key] != r.doc[key]) return true
+    }
+    return false
+}
+
+
 function generateRationName(ration,gljList) {
     let caption = ration.caption ? ration.caption:ration.name;
-    if(ration.rationAssList && ration.rationAssList.length > 0){
-        let ass = ration.rationAssList[0];
-        if(ass.actualValue != null && ass.actualValue != undefined ){
-            caption =  caption.replace('%s',ass.actualValue);
+    let replaceList = [];
+    if(ration.rationAssList && ration.rationAssList.length > 0){//这里要处理第三定额和多辅助定额的情况
+        let isThird = ration.rationAssList.length == 2 && ration.rationAssList[0].thirdRationCode&&ration.rationAssList[0].thirdRationCode!='';//说明有第三定额的情况
+        let adjustMatch = "",notAdjust="";
+        for(let ass of ration.rationAssList){
+            let tem = "";
+            if( ass.isAdjust == 1 && ass.actualValue != null && ass.actualValue != undefined ){
+                tem = ass.actualValue;
+                adjustMatch = tem;
+            }else {
+                tem = ass.stdValue;
+                notAdjust = tem;
+            }
+            if(isThird) replaceList.push(tem);
+        }
+        if(replaceList.length == 0){
+            adjustMatch!=""?replaceList.push(adjustMatch):replaceList.push(notAdjust);
+        }
+        for(let r of replaceList){
+            caption =  caption.replace('%s',r);
         }
     }
     let reNameList = [];
@@ -283,7 +322,24 @@ function getContent(coes) {
 
 }
 
+function prepareAss(assList) {//处理辅助定额,支持多个辅助定额的情况
+    for(let a of assList){
+        if(a.groupList && a.groupList.length > 1){//组里有多个定额的情况
+            let newList = _.sortByAll(a.groupList,['param']);//先按参数排序
+            for(let n of newList){
+                if(a.actualValue > n.stdValue && a.actualValue <= parseFloat(n.param)){//落在中间,则用组里的这条定额
+                    a._doc.param = n.param;
+                    a._doc.paramName = n.paramName;
+                    a._doc.assistCode = n.assistCode;
+                    break;
+                }
+            }
+        }
+    }
+}
+
 function calculateTimes(ass){
+    if(ass.isAdjust == 0) return 0;//打勾辅助定额才计算
     let times =(ass.actualValue-ass.stdValue)/ass.stepValue;
     let r = false;
     if(times<0){
@@ -294,6 +350,8 @@ function calculateTimes(ass){
         times = _.round(times,ass.decimal);
     }else if (ass.carryBit=='进一'){
         times =_.ceil(times,ass.decimal);
+    }else if(ass.carryBit == '舍一'){
+        times = _.floor(times,ass.decimal);
     }
     if(r){
         times=times*-1;
@@ -373,13 +431,13 @@ function getCalculateResult(quantify,c,coe,gljList,decimal) {
         case '+*' :
             o_glj = getCoeSelectedGLJ(gljList,coe.original_code,coe.select_code);
             if(o_glj){
-              q = q +  c.amount * scMathUtil.roundForObj(o_glj.quantity,decimal);
+              q = q +  c.amount * scMathUtil.roundForObj(o_glj.rationItemQuantity,decimal);
             }
             break;
         case '-*' :
             o_glj = getCoeSelectedGLJ(gljList,coe.original_code,coe.select_code);
             if(o_glj){
-                q = q -  c.amount * scMathUtil.roundForObj(o_glj.quantity,decimal);
+                q = q -  c.amount * scMathUtil.roundForObj(o_glj.rationItemQuantity,decimal);
             }
             break;
         case '=' :

+ 50 - 30
modules/ration_glj/facade/ration_glj_facade.js

@@ -1,6 +1,32 @@
 /**
  * Created by chen on 2017/6/29.
  */
+module.exports = {
+    save: save,
+    getData: getData,
+    deleteByRation: deleteByRation,
+    getQuantityByProjectGLJ: getQuantityByProjectGLJ,
+    getLibInfo: getLibInfo,
+    getGLJData: getGLJData,
+    getGLJDataPaging: getGLJDataPaging,
+    getGLJDataByCodes:getGLJDataByCodes,
+    addGLJ: addGLJ,
+    deleteGLJ:deleteGLJ,
+    insertAddTypeGLJ:insertAddTypeGLJ,
+    replaceGLJ: replaceGLJ,
+    replaceGLJByData:replaceGLJByData,
+    mReplaceGLJ: mReplaceGLJ,
+    updateRationGLJByEdit: updateRationGLJByEdit,
+    getGLJClass: getGLJClass,
+    insertGLJAsRation: insertGLJAsRation,
+    getRationTypeGLJQuantity:getRationTypeGLJQuantity,
+    getInfoFromProjectGLJ:getInfoFromProjectGLJ,
+    createNewRecord:createNewRecord,
+    getGLJSearchInfo:getGLJSearchInfo,
+    updateRationGLJFromDoc:updateRationGLJFromDoc,
+    getGLJLibByEngineerID:getGLJLibByEngineerID,
+    prepareExtData:prepareExtData
+}
 
 let mongoose = require('mongoose');
 const uuidV1 = require('uuid/v1');
@@ -29,30 +55,6 @@ const gljClassModel = mongoose.model('std_glj_lib_gljClass');
 const projectDao = require('../../pm/models/project_model').project;
 const compleClassModel = mongoose.model('complementary_glj_section');
 
-module.exports = {
-    save: save,
-    getData: getData,
-    deleteByRation: deleteByRation,
-    getQuantityByProjectGLJ: getQuantityByProjectGLJ,
-    getLibInfo: getLibInfo,
-    getGLJData: getGLJData,
-    getGLJDataByCodes:getGLJDataByCodes,
-    addGLJ: addGLJ,
-    deleteGLJ:deleteGLJ,
-    insertAddTypeGLJ:insertAddTypeGLJ,
-    replaceGLJ: replaceGLJ,
-    replaceGLJByData:replaceGLJByData,
-    mReplaceGLJ: mReplaceGLJ,
-    updateRationGLJByEdit: updateRationGLJByEdit,
-    getGLJClass: getGLJClass,
-    insertGLJAsRation: insertGLJAsRation,
-    getRationTypeGLJQuantity:getRationTypeGLJQuantity,
-    getInfoFromProjectGLJ:getInfoFromProjectGLJ,
-    createNewRecord:createNewRecord,
-    getGLJSearchInfo:getGLJSearchInfo,
-    updateRationGLJFromDoc:updateRationGLJFromDoc,
-    getGLJLibByEngineerID:getGLJLibByEngineerID
-}
 
 let operationMap = {
     'ut_create': create_ration_glj,
@@ -538,6 +540,18 @@ async function getGLJLibByEngineerID  (engineerID) {
     return gljLibId
 }
 
+async function getGLJDataPaging(info, condition) {
+    let gljDao = new GljDao();
+    let rst = {};
+    // 初始化,需要生成分类树和类型数据
+    if (condition.init) {
+        rst.treeData = await gljDao.getMixedTree(info.gljLibId, info.userID, info.compilationId);
+        rst.distTypeTree = stdgljutil.getStdGljTypeCacheObj().toArray();
+    }
+    let gljData = await gljDao.getGLJPaging({...info, ...condition});
+    Object.assign(rst, gljData);
+    return rst;
+}
 
 function getGLJData(info, callback) {
     let gljDao = new GljDao();
@@ -554,7 +568,7 @@ function getGLJData(info, callback) {
             }
         },
         function (cb) {
-            gljDao.getGljItems(info.gljLibId, info.userID, info.compilationId, function (err, data) {
+            gljDao.getGljItems(info.gljLibId, info.userID, info.compilationId, {_id: 0}, function (err, data) {
                 if (err) {
                     cb(err);
                 } else {
@@ -594,6 +608,9 @@ function getGLJSearchInfo(ration_glj) {
         adjCoe: ration_glj.adjCoe,
         materialType:ration_glj.materialType,
         materialCoe:ration_glj.materialCoe,
+        materialIndexType:ration_glj.materialIndexType,
+        materialIndexUnit:ration_glj.materialIndexUnit,
+        materialIndexCoe:ration_glj.materialIndexCoe,
         from: ration_glj.from ? ration_glj.from : 'std'//std:标准工料机库, cpt:补充工料机库
     };
     let glj_type_object = glj_type_util.getStdGljTypeCacheObj();
@@ -746,13 +763,16 @@ async function updateRationGLJFromDoc(rg,doc,priceInfo) {
     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 {
+    if(rg.createType == 'replace'&& rg.rcode == projcetGLJ_n.code){//如果原数据已经是替换过的,这次替换又恢复成原数据,则把类型改回来
         doc.createType = 'normal';
-        doc.rcode = '';
     }
+    if (rg.createType===undefined || rg.createType == 'normal'){// createTypel 默认是normal 只有是定额下带的工料机才需把类型改成替换,其它的保持不变
+        if(rg.code != projcetGLJ_n.code){
+            doc.createType = 'replace';
+            doc.rcode = rg.code;
+        }
+    }
+
     await ration_glj.findOneAndUpdate({ID:rg.ID}, doc);
     return [projcetGLJ_n,doc]
 }

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

@@ -9,6 +9,7 @@ module.exports = function (app) {
 
     var rgRouter = express.Router();
     rgRouter.get('/getGLJData/:engineerID', rgController.getGLJData);
+    rgRouter.post('/getGLJDataPaging', rgController.getGLJDataPaging);
     rgRouter.post('/getGLJDataByCodes',rgController.getGLJDataByCodes);
     rgRouter.post('/addGLJ',rgController.addGLJ);
     rgRouter.post('/replaceGLJ',rgController.replaceGLJ);

+ 65 - 22
modules/reports/controllers/rpt_controller.js

@@ -23,7 +23,6 @@ import rpt_svg_util from "../util/rpt_svg_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){
     if(err){
@@ -223,6 +222,7 @@ function setupCustomizeCfg(customizeCfg, rptTpl, defProperties) {
         }
     }
 }
+
 function getAllPagesCommon(user_id, prj_id, rpt_id, pageSize, orientation, customizeCfg, option, outputType, cb) {
     let rptTpl = null;
     rptTplFacade.getRptTemplate(rpt_id).then(function(rst) {
@@ -230,9 +230,9 @@ function getAllPagesCommon(user_id, prj_id, rpt_id, pageSize, orientation, custo
         if (rptTpl) {
             let rptDataUtil = new rptDataExtractor();
             rptDataUtil.initialize((rptTpl._doc)?rptTpl._doc:rptTpl);
-            let summaryRst = [];
-            let filter = rptDataUtil.getDataRequestFilter(summaryRst);
-            let promiseArr = [null, null];
+            let summaryRst = [], economicRst = [];
+            let filter = rptDataUtil.getDataRequestFilter(summaryRst, economicRst);
+            let promiseArr = [null, null, null];
             if (summaryRst.length > 0) {
                 if (summaryRst.indexOf(`Construct`) >= 0 || summaryRst.indexOf(`ConstructDetail`) >= 0) {
                     promiseArr[0] = pm_facade.getSummaryInfoByTender(prj_id, pm_facade.projectType.project);
@@ -241,26 +241,42 @@ function getAllPagesCommon(user_id, prj_id, rpt_id, pageSize, orientation, custo
                     promiseArr[1] = pm_facade.getSummaryInfoByTender(prj_id, pm_facade.projectType.engineering);
                 }
             }
+            if (economicRst.length > 0) {
+                // economicRst.push('ProjectInfoFields');
+                // economicRst.push('ProjectFeatureFields');
+                promiseArr[2] = pm_facade.getIndexReportData(prj_id, economicRst);
+            }
             rptTplDataFacade.prepareProjectData(user_id, prj_id, filter, function (err, msg, rawDataObj) {
                 if (!err) {
                     let buildPageData = function() {
-                        let tplData = rptDataUtil.assembleData(rawDataObj);
-                        let printCom = JpcEx.createNew();
-                        if (pageSize) rptTpl[JV.NODE_MAIN_INFO][JV.NODE_PAGE_INFO][JV.PROP_PAGE_SIZE] = pageSize;
-                        //console.log("orientation: " + (orientation === 'null'));
-                        if (orientation && (orientation !== 'null')) rptTpl[JV.NODE_MAIN_INFO][JV.NODE_PAGE_INFO][JV.PROP_ORIENTATION] = orientation;
-                        let defProperties = rptUtil.getReportDefaultCache();
-                        if (customizeCfg) setupCustomizeCfg(customizeCfg, rptTpl, defProperties);
-                        let dftOption = option||JV.PAGING_OPTION_NORMAL;
-                        printCom.initialize(rptTpl);
-                        printCom.analyzeData(rptTpl, tplData, defProperties, dftOption, outputType);
-                        let maxPages = printCom.totalPages;
-                        let pageRst = printCom.outputAsSimpleJSONPageArray(rptTpl, tplData, 1, maxPages, defProperties, customizeCfg);
-                        if (pageRst) {
-                            // fsUtil.writeObjToFile(pageRst, "D:/GitHome/ConstructionCost/tmp/testBuiltPageResult.jsp");
-                            cb(null, pageRst);
-                        } else {
-                            cb('Have errors while on going...', null);
+                        try {
+                            let tplData = rptDataUtil.assembleData(rawDataObj);
+                            let printCom = JpcEx.createNew();
+                            if (pageSize) rptTpl[JV.NODE_MAIN_INFO][JV.NODE_PAGE_INFO][JV.PROP_PAGE_SIZE] = pageSize;
+                            //console.log("orientation: " + (orientation === 'null'));
+                            if (orientation && (orientation !== 'null')) rptTpl[JV.NODE_MAIN_INFO][JV.NODE_PAGE_INFO][JV.PROP_ORIENTATION] = orientation;
+                            let defProperties = rptUtil.getReportDefaultCache();
+                            if (customizeCfg) setupCustomizeCfg(customizeCfg, rptTpl, defProperties);
+                            let dftOption = option||JV.PAGING_OPTION_NORMAL;
+                            printCom.initialize(rptTpl);
+                            printCom.analyzeData(rptTpl, tplData, defProperties, dftOption, outputType);
+                            let maxPages = printCom.totalPages;
+                            let pageRst = null;
+                            if (maxPages > 0) {
+                                pageRst = printCom.outputAsSimpleJSONPageArray(rptTpl, tplData, 1, maxPages, defProperties, customizeCfg);
+                            } else {
+                                pageRst = printCom.outputAsPreviewPage(rptTpl, defProperties);
+                            }
+                            if (pageRst) {
+                                // fsUtil.writeObjToFile(pageRst, "D:/GitHome/ConstructionCost/tmp/testBuiltPageResult.jsp");
+                                cb(null, pageRst);
+                            } else {
+                                cb('Have errors while on going...', null);
+                            }
+                        } catch (ex) {
+                            console.log("报表数据异常: userId " + user_id + ", project id: " + prj_id);
+                            console.log(ex);
+                            cb('Exception occurs while on going...', null);
                         }
                     };
                     //*/
@@ -286,7 +302,21 @@ function getAllPagesCommon(user_id, prj_id, rpt_id, pageSize, orientation, custo
                             buildPageData(rawDataObj, rptDataUtil, rptTpl);
                         });
                     } else {
-                        buildPageData(rawDataObj, rptDataUtil, rptTpl);
+                        if (promiseArr[2] !== null) {
+                            promiseArr[2].then(function (rst) {
+                                let ecoFieldsRst = (rst._doc)?rst._doc:rst;
+                                // fsUtil.writeObjToFile(ecoFieldsRst, "D:/GitHome/ConstructionCost/tmp/testEcoFieldsResult.jsp");
+                                rawDataObj.prjData.push({moduleName: 'ProjectInfoFields', data: ecoFieldsRst.ProjectInfoFields});
+                                rawDataObj.prjData.push({moduleName: 'ProjectFeatureFields', data: ecoFieldsRst.ProjectFeatureFields});
+                                rawDataObj.prjData.push({moduleName: 'ProjectCostFields', data: ecoFieldsRst.ProjectCostFields});
+                                rawDataObj.prjData.push({moduleName: 'ProjectEcoFields', data: ecoFieldsRst.ProjectEcoFields});
+                                rawDataObj.prjData.push({moduleName: 'ProjectLabMaterialFields', data: ecoFieldsRst.ProjectLabMaterialFields});
+                                rawDataObj.prjData.push({moduleName: 'ProjectQtyFields', data: ecoFieldsRst.ProjectQtyFields});
+                                buildPageData(rawDataObj, rptDataUtil, rptTpl);
+                            });
+                        } else {
+                            buildPageData(rawDataObj, rptDataUtil, rptTpl);
+                        }
                     }
                     /*/
                     let tplData = rptDataUtil.assembleData(rawDataObj);
@@ -367,6 +397,18 @@ module.exports = {
             }
         })
     },
+    getMultiReportsEx: function (req, res) {
+        //原则说明:把所有报表模板集中获取,统一filter,只读一次数据!(有空再整)
+        let params = JSON.parse(req.body.params),
+            prj_id = params.prj_id,
+            rpt_ids = params.rpt_ids.split(','),
+            pageSize = params.pageSize,
+            orientation = params.orientation,
+            customizeCfg = params.custCfg,
+            option = params.option;
+        let user_id = req.session.sessionUser.id;
+        let dftOption = option||JV.PAGING_OPTION_NORMAL;
+    },
 
     getReportAllPagesSvg: function (req, res) {
         let params = JSON.parse(req.body.params),
@@ -723,3 +765,4 @@ module.exports = {
 
     }
 };
+

+ 2 - 2
modules/reports/rpt_component/helper/jpc_helper_common.js

@@ -133,7 +133,7 @@ let JpcCommonHelper = {
         }
         return rst;
     },
-    getStringLinesInArea: function(area, strVal, pdfDoc, chnW, otherW) {
+    getStringLinesInArea: function(area, strVal, chnW, otherW) {
         //备注: 因后台的pdf kit判断字符串长度与前端的不一样,需要做些调整,不一次性地判断字符串长度。
         //      分2种字符:中文与非中文,按照各种字符的数量分别乘以相关一个字符的宽度再累计。
         //      另判断行数还不能直接用总长度除以宽度来计算,因每一行都会有不同的余量,所以得一行行走过来判断。
@@ -157,7 +157,7 @@ let JpcCommonHelper = {
         return rst;
         //备注: 其实是想用canvas的,但node canvas装起来麻烦,暂时用PDF Kit来顶用一下,以后有更好的再换
     },
-    splitString: function (area, strVal, pdfDoc, chnW, otherW) {
+    splitString: function (area, strVal, chnW, otherW) {
         let rst = [];
         if (strVal) {
             let areaWidth = area[JV.PROP_RIGHT] - area[JV.PROP_LEFT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT] - 1;

+ 5 - 1
modules/reports/rpt_component/helper/jpc_helper_common_output.js

@@ -2,7 +2,7 @@ let JV = require('../jpc_value_define');
 let JpcFieldHelper = require('./jpc_helper_field');
 
 let JpcCommonOutputHelper = {
-    createCommonOutputWithoutDecorate: function (node, value) {
+    createCommonOutputWithoutDecorate: function (node, value, forceCombine) {
         let me = this, rst = {};
         //1. font/style/control
         rst[JV.PROP_FONT] = node[[JV.PROP_FONT]];
@@ -14,9 +14,13 @@ let JpcCommonOutputHelper = {
         // innerFormat(node[JV.PROP_FORMAT], rst);
         if (node[JV.PROP_PREFIX] && rst[JV.PROP_VALUE] !== null) {
             rst[JV.PROP_VALUE] = node[JV.PROP_PREFIX] + rst[JV.PROP_VALUE];
+        } else if (node[JV.PROP_PREFIX] && forceCombine) {
+            rst[JV.PROP_VALUE] = node[JV.PROP_PREFIX];
         }
         if (node[JV.PROP_SUFFIX] && rst[JV.PROP_VALUE] !== null) {
             rst[JV.PROP_VALUE] = rst[JV.PROP_VALUE] + node[JV.PROP_SUFFIX];
+        } else if (node[JV.PROP_SUFFIX] && forceCombine) {
+            rst[JV.PROP_VALUE] = node[JV.PROP_SUFFIX];
         }
         return rst;
     },

+ 39 - 7
modules/reports/rpt_component/helper/jpc_helper_cross_tab.js

@@ -1,4 +1,5 @@
 let JV = require('../jpc_value_define');
+let JE = require('../jpc_rte');
 let JpcCommonHelper = require('./jpc_helper_common');
 
 let JpcCrossTabHelper = {
@@ -27,7 +28,7 @@ let JpcCrossTabHelper = {
     },
     sortFieldValue: function(sIDX, eIDX, sortOrder, dataField, dataValSeq) {
         let tmpSeq = [];
-        if ((sortOrder) && (sortOrder != JV.TAB_FIELD_PROP_SORT_VAL_NOSORT)) {
+        if ((sortOrder) && (sortOrder !== JV.TAB_FIELD_PROP_SORT_VAL_NOSORT)) {
             if (sIDX >= 0 && eIDX >= sIDX && dataValSeq.length > eIDX) {
                 let reversed = 1;
                 if (sortOrder === JV.TAB_FIELD_PROP_SORT_VAL_DESC) {
@@ -38,7 +39,8 @@ let JpcCrossTabHelper = {
                 }
                 tmpSeq.sort(function(idx1, idx2) {
                     let rst = 0;
-                    if (isNaN(parseFloat(dataField[idx1])) || isNaN(parseFloat(dataField[idx1]))) {
+                    // if (isNaN(parseFloat(dataField[idx1])) || isNaN(parseFloat(dataField[idx1]))) {
+                    if (typeof (dataField[idx1]) === 'string' || typeof (dataField[idx1]) === 'string' ) {
                         if (dataField[idx1] > dataField[idx2]) {
                             rst = reversed;
                         } else if (dataField[idx1] < dataField[idx2]) {
@@ -72,12 +74,12 @@ let JpcCrossTabHelper = {
         }
         return rst;
     },
-    sortTabFields: function(tabFields, fieldSeqs, data_details, dataSeq) {
+    sortTabFields: function(tabFields, fieldSeqs, data_details, dataSeq, $CURRENT_RPT) {
         let me = this;
         let sIDX = 0, eIDX = -1, isFirstSort = true;
         for (let i = 0; i < tabFields.length; i++) {
             let tabField = tabFields[i];
-            if (tabField[JV.TAB_FIELD_PROP_SORT] != JV.TAB_FIELD_PROP_SORT_VAL_NOSORT) {
+            if (tabField[JV.TAB_FIELD_PROP_SORT] !== JV.TAB_FIELD_PROP_SORT_VAL_NOSORT) {
                 if (isFirstSort) {
                     isFirstSort = false;
                     //first field, should sort all data items
@@ -85,14 +87,34 @@ let JpcCrossTabHelper = {
                         sIDX = 0;
                         eIDX = dataSeq[j].length - 1;
                         //sort the field value here
-                        me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT],data_details[fieldSeqs[i]], dataSeq[j]);
+                        if (typeof(fieldSeqs[i]) === "object") {
+                            let exFirstField = JE.F(fieldSeqs[i][JV.PROP_ID], $CURRENT_RPT);
+                            if (exFirstField) {
+                                me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT], exFirstField[JV.PROP_AD_HOC_DATA], dataSeq[j]);
+                            } else {
+                                //不排序(健壮性)
+                            }
+                        } else {
+                            me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT],data_details[fieldSeqs[i]], dataSeq[j]);
+                        }
+                        // me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT],data_details[fieldSeqs[i]], dataSeq[j]);
                     }
                 } else {
                     //then sort the rest fields one by one
                     for (let j = 0; j < dataSeq.length; j++) {
                         let chkFields = [];
                         for (let k = 0; k < i; k++) {
-                            chkFields.push(data_details[fieldSeqs[k]]);
+                            if (typeof(fieldSeqs[k]) === "object") {
+                                let exField = JE.F(fieldSeqs[k][JV.PROP_ID], $CURRENT_RPT);
+                                if (exField) {
+                                    chkFields.push(exField[JV.PROP_AD_HOC_DATA]);
+                                } else {
+                                    chkFields.push(null);
+                                }
+                            } else {
+                                chkFields.push(data_details[fieldSeqs[k]]);
+                            }
+                            // chkFields.push(data_details[fieldSeqs[k]]);
                         }
                         sIDX = 0, eIDX = -1;
                         for (let m = 1; m < dataSeq[j].length; m++) {
@@ -103,7 +125,17 @@ let JpcCrossTabHelper = {
                             };
                             if (eIDX >= sIDX) {
                                 if (eIDX != sIDX) {
-                                    me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT],data_details[fieldSeqs[i]], dataSeq[j]);
+                                    if (typeof(fieldSeqs[i]) === "object") {
+                                        let exOtherField = JE.F(fieldSeqs[i][JV.PROP_ID], $CURRENT_RPT);
+                                        if (exOtherField) {
+                                            me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT], exOtherField[JV.PROP_AD_HOC_DATA], dataSeq[j]);
+                                        } else {
+                                            //不排序(健壮性)
+                                        }
+                                    } else {
+                                        me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT],data_details[fieldSeqs[i]], dataSeq[j]);
+                                    }
+                                    // me.sortFieldValue(sIDX, eIDX, tabField[JV.TAB_FIELD_PROP_SORT],data_details[fieldSeqs[i]], dataSeq[j]);
                                 }
                                 sIDX = m;
                                 eIDX = m - 1; //for protection purpose

+ 1 - 1
modules/reports/rpt_component/helper/jpc_helper_discrete.js

@@ -60,7 +60,7 @@ let JpcDiscreteHelper = {
                                 let param = JE.P(df[JV.PROP_PARAM_ID], $CURRENT_RPT);
                                 value = param[JV.PROP_DFT_VALUE];
                             }
-                            let item = JpcCommonOutputHelper.createCommonOutputWithoutDecorate(df, value, null);
+                            let item = JpcCommonOutputHelper.createCommonOutputWithoutDecorate(df, value, true);
                             //position
                             item[JV.PROP_AREA] = JpcAreaHelper.outputArea(df[JV.PROP_AREA], band, unitFactor, 1, 0, 1, 0, multiCols, multiColIdx, false, false);
                             rst.push(item);

+ 4 - 1
modules/reports/rpt_component/helper/jpc_helper_field.js

@@ -31,8 +31,11 @@ let JpcFieldHelper = {
     resetFormat: function (tab_field, map_field, customizeCfg) {
         let rst = false;
         if (map_field && map_field[JV.PROP_PRECISION] && map_field[JV.PROP_PRECISION].type === "fixed") {
-            let formatStrs = ["#."], ffStr = (customizeCfg && customizeCfg.fillZero)?"0":"#";
+            let formatStrs = ["#"], ffStr = (customizeCfg && customizeCfg.fillZero)?"0":"#";
             for (let idx = 0; idx < parseInt(map_field[JV.PROP_FIXED_PRECISION_AMT]); idx++) {
+                if (idx === 0) {
+                    formatStrs.push(".");
+                }
                 formatStrs.push(ffStr);
             }
             if (tab_field[JV.PROP_FORMAT] && tab_field[JV.PROP_FORMAT].indexOf(",") >= 0) {

+ 513 - 0
modules/reports/rpt_component/helper/jpc_helper_font_width.js

@@ -0,0 +1,513 @@
+/**
+ * Created by Tony on 2019/12/12.
+ */
+
+const fontWidthMap = {
+    "宋体": {
+        "宽": {
+            "_6": 6,
+            "_7": 7,
+            "_8": 8,
+            "_9": 9,
+            "_10": 10,
+            "_11": 11,
+            "_12": 12,
+            "_13": 13,
+            "_14": 14,
+            "_15": 15,
+            "_16": 16,
+            "_17": 17,
+            "_18": 18,
+            "_19": 19,
+            "_20": 20,
+            "_21": 21,
+            "_22": 22,
+            "_23": 23,
+            "_24": 24,
+            "_25": 25,
+            "_26": 26,
+            "_27": 27,
+            "_28": 28,
+            "_29": 29,
+            "_30": 30,
+            "_31": 31,
+            "_32": 32,
+            "_33": 33,
+            "_34": 34,
+            "_35": 35,
+            "_36": 36,
+            "_37": 37,
+            "_38": 38,
+            "_39": 39,
+            "_40": 40,
+            "_41": 41,
+            "_42": 42,
+            "_43": 43,
+            "_44": 44,
+            "_45": 45,
+            "_46": 46,
+            "_47": 47,
+            "_48": 48,
+            "_49": 49,
+            "_50": 50,
+            "_51": 51,
+            "_52": 52,
+            "_53": 53,
+            "_54": 54,
+            "_55": 55,
+            "_56": 56,
+            "_57": 57,
+            "_58": 58,
+            "_59": 59,
+            "_60": 60,
+            "_61": 61,
+            "_62": 62,
+            "_63": 63,
+            "_64": 64
+        },
+        "窄": {
+            "_6": 3,
+            "_7": 3.5,
+            "_8": 4,
+            "_9": 4.5,
+            "_10": 5,
+            "_11": 5.5,
+            "_12": 6,
+            "_13": 6.5,
+            "_14": 7,
+            "_15": 7.5,
+            "_16": 8,
+            "_17": 8.5,
+            "_18": 9,
+            "_19": 9.5,
+            "_20": 10,
+            "_21": 10.5,
+            "_22": 11,
+            "_23": 11.5,
+            "_24": 12,
+            "_25": 12.5,
+            "_26": 13,
+            "_27": 13.5,
+            "_28": 14,
+            "_29": 14.5,
+            "_30": 15,
+            "_31": 15.5,
+            "_32": 16,
+            "_33": 16.5,
+            "_34": 17,
+            "_35": 17.5,
+            "_36": 18,
+            "_37": 18.5,
+            "_38": 19,
+            "_39": 19.5,
+            "_40": 20,
+            "_41": 20.5,
+            "_42": 21,
+            "_43": 21.5,
+            "_44": 22,
+            "_45": 22.5,
+            "_46": 23,
+            "_47": 23.5,
+            "_48": 24,
+            "_49": 24.5,
+            "_50": 25,
+            "_51": 25.5,
+            "_52": 26,
+            "_53": 26.5,
+            "_54": 27,
+            "_55": 27.5,
+            "_56": 28,
+            "_57": 28.5,
+            "_58": 29,
+            "_59": 29.5,
+            "_60": 30,
+            "_61": 30.5,
+            "_62": 31,
+            "_63": 31.5,
+            "_64": 32
+        }
+    },
+    "黑体": {
+        "宽": {
+            "_6": 6,
+            "_7": 7,
+            "_8": 8,
+            "_9": 9,
+            "_10": 10,
+            "_11": 11,
+            "_12": 12,
+            "_13": 13,
+            "_14": 14,
+            "_15": 15,
+            "_16": 16,
+            "_17": 17,
+            "_18": 18,
+            "_19": 19,
+            "_20": 20,
+            "_21": 21,
+            "_22": 22,
+            "_23": 23,
+            "_24": 24,
+            "_25": 25,
+            "_26": 26,
+            "_27": 27,
+            "_28": 28,
+            "_29": 29,
+            "_30": 30,
+            "_31": 31,
+            "_32": 32,
+            "_33": 33,
+            "_34": 34,
+            "_35": 35,
+            "_36": 36,
+            "_37": 37,
+            "_38": 38,
+            "_39": 39,
+            "_40": 40,
+            "_41": 41,
+            "_42": 42,
+            "_43": 43,
+            "_44": 44,
+            "_45": 45,
+            "_46": 46,
+            "_47": 47,
+            "_48": 48,
+            "_49": 49,
+            "_50": 50,
+            "_51": 51,
+            "_52": 52,
+            "_53": 53,
+            "_54": 54,
+            "_55": 55,
+            "_56": 56,
+            "_57": 57,
+            "_58": 58,
+            "_59": 59,
+            "_60": 60,
+            "_61": 61,
+            "_62": 62,
+            "_63": 63,
+            "_64": 64
+        },
+        "窄": {
+            "_6": 3,
+            "_7": 3.5,
+            "_8": 4,
+            "_9": 4.5,
+            "_10": 5,
+            "_11": 5.5,
+            "_12": 6,
+            "_13": 6.5,
+            "_14": 7,
+            "_15": 7.5,
+            "_16": 8,
+            "_17": 8.5,
+            "_18": 9,
+            "_19": 9.5,
+            "_20": 10,
+            "_21": 10.5,
+            "_22": 11,
+            "_23": 11.5,
+            "_24": 12,
+            "_25": 12.5,
+            "_26": 13,
+            "_27": 13.5,
+            "_28": 14,
+            "_29": 14.5,
+            "_30": 15,
+            "_31": 15.5,
+            "_32": 16,
+            "_33": 16.5,
+            "_34": 17,
+            "_35": 17.5,
+            "_36": 18,
+            "_37": 18.5,
+            "_38": 19,
+            "_39": 19.5,
+            "_40": 20,
+            "_41": 20.5,
+            "_42": 21,
+            "_43": 21.5,
+            "_44": 22,
+            "_45": 22.5,
+            "_46": 23,
+            "_47": 23.5,
+            "_48": 24,
+            "_49": 24.5,
+            "_50": 25,
+            "_51": 25.5,
+            "_52": 26,
+            "_53": 26.5,
+            "_54": 27,
+            "_55": 27.5,
+            "_56": 28,
+            "_57": 28.5,
+            "_58": 29,
+            "_59": 29.5,
+            "_60": 30,
+            "_61": 30.5,
+            "_62": 31,
+            "_63": 31.5,
+            "_64": 32
+        }
+    },
+    "楷体": {
+        "宽": {
+            "_6": 6,
+            "_7": 7,
+            "_8": 8,
+            "_9": 9,
+            "_10": 10,
+            "_11": 11,
+            "_12": 12,
+            "_13": 13,
+            "_14": 14,
+            "_15": 15,
+            "_16": 16,
+            "_17": 17,
+            "_18": 18,
+            "_19": 19,
+            "_20": 20,
+            "_21": 21,
+            "_22": 22,
+            "_23": 23,
+            "_24": 24,
+            "_25": 25,
+            "_26": 26,
+            "_27": 27,
+            "_28": 28,
+            "_29": 29,
+            "_30": 30,
+            "_31": 31,
+            "_32": 32,
+            "_33": 33,
+            "_34": 34,
+            "_35": 35,
+            "_36": 36,
+            "_37": 37,
+            "_38": 38,
+            "_39": 39,
+            "_40": 40,
+            "_41": 41,
+            "_42": 42,
+            "_43": 43,
+            "_44": 44,
+            "_45": 45,
+            "_46": 46,
+            "_47": 47,
+            "_48": 48,
+            "_49": 49,
+            "_50": 50,
+            "_51": 51,
+            "_52": 52,
+            "_53": 53,
+            "_54": 54,
+            "_55": 55,
+            "_56": 56,
+            "_57": 57,
+            "_58": 58,
+            "_59": 59,
+            "_60": 60,
+            "_61": 61,
+            "_62": 62,
+            "_63": 63,
+            "_64": 64
+        },
+        "窄": {
+            "_6": 3,
+            "_7": 3.5,
+            "_8": 4,
+            "_9": 4.5,
+            "_10": 5,
+            "_11": 5.5,
+            "_12": 6,
+            "_13": 6.5,
+            "_14": 7,
+            "_15": 7.5,
+            "_16": 8,
+            "_17": 8.5,
+            "_18": 9,
+            "_19": 9.5,
+            "_20": 10,
+            "_21": 10.5,
+            "_22": 11,
+            "_23": 11.5,
+            "_24": 12,
+            "_25": 12.5,
+            "_26": 13,
+            "_27": 13.5,
+            "_28": 14,
+            "_29": 14.5,
+            "_30": 15,
+            "_31": 15.5,
+            "_32": 16,
+            "_33": 16.5,
+            "_34": 17,
+            "_35": 17.5,
+            "_36": 18,
+            "_37": 18.5,
+            "_38": 19,
+            "_39": 19.5,
+            "_40": 20,
+            "_41": 20.5,
+            "_42": 21,
+            "_43": 21.5,
+            "_44": 22,
+            "_45": 22.5,
+            "_46": 23,
+            "_47": 23.5,
+            "_48": 24,
+            "_49": 24.5,
+            "_50": 25,
+            "_51": 25.5,
+            "_52": 26,
+            "_53": 26.5,
+            "_54": 27,
+            "_55": 27.5,
+            "_56": 28,
+            "_57": 28.5,
+            "_58": 29,
+            "_59": 29.5,
+            "_60": 30,
+            "_61": 30.5,
+            "_62": 31,
+            "_63": 31.5,
+            "_64": 32
+        }
+    },
+    "Arial": {
+        "宽": {
+            "_6": 6,
+            "_7": 7,
+            "_8": 8,
+            "_9": 9,
+            "_10": 10,
+            "_11": 11,
+            "_12": 12,
+            "_13": 13,
+            "_14": 14,
+            "_15": 15,
+            "_16": 16,
+            "_17": 17,
+            "_18": 18,
+            "_19": 19,
+            "_20": 20,
+            "_21": 21,
+            "_22": 22,
+            "_23": 23,
+            "_24": 24,
+            "_25": 25,
+            "_26": 26,
+            "_27": 27,
+            "_28": 28,
+            "_29": 29,
+            "_30": 30,
+            "_31": 31,
+            "_32": 32,
+            "_33": 33,
+            "_34": 34,
+            "_35": 35,
+            "_36": 36,
+            "_37": 37,
+            "_38": 38,
+            "_39": 39,
+            "_40": 40,
+            "_41": 41,
+            "_42": 42,
+            "_43": 43,
+            "_44": 44,
+            "_45": 45,
+            "_46": 46,
+            "_47": 47,
+            "_48": 48,
+            "_49": 49,
+            "_50": 50,
+            "_51": 51,
+            "_52": 52,
+            "_53": 53,
+            "_54": 54,
+            "_55": 55,
+            "_56": 56,
+            "_57": 57,
+            "_58": 58,
+            "_59": 59,
+            "_60": 60,
+            "_61": 61,
+            "_62": 62,
+            "_63": 63,
+            "_64": 64
+        },
+        "窄": {
+            "_6": 3.3399999141693115,
+            "_7": 3.8899998664855957,
+            "_8": 4.449999809265137,
+            "_9": 5.009999752044678,
+            "_10": 5.559999942779541,
+            "_11": 6.119999885559082,
+            "_12": 6.670000076293945,
+            "_13": 7.230000019073486,
+            "_14": 7.789999961853027,
+            "_15": 8.34000015258789,
+            "_16": 8.899999618530273,
+            "_17": 9.449999809265136,
+            "_18": 10.010000228881836,
+            "_19": 10.569999694824218,
+            "_20": 11.119999885559082,
+            "_21": 11.679999351501464,
+            "_22": 12.239999771118164,
+            "_23": 12.789999961853027,
+            "_24": 13.34999942779541,
+            "_25": 13.899999618530273,
+            "_26": 14.460000038146972,
+            "_27": 15.019999504089355,
+            "_28": 15.569999694824218,
+            "_29": 16.1299991607666,
+            "_30": 16.68000030517578,
+            "_31": 17.239999771118164,
+            "_32": 17.799999237060547,
+            "_33": 18.350000381469726,
+            "_34": 18.90999984741211,
+            "_35": 19.469999313354492,
+            "_36": 20.020000457763672,
+            "_37": 20.579999923706054,
+            "_38": 21.1299991607666,
+            "_39": 21.689998626708984,
+            "_40": 22.25,
+            "_41": 22.799999237060547,
+            "_42": 23.35999870300293,
+            "_43": 23.90999984741211,
+            "_44": 24.469999313354492,
+            "_45": 25.029998779296875,
+            "_46": 25.579999923706054,
+            "_47": 26.139999389648437,
+            "_48": 26.69999885559082,
+            "_49": 27.25,
+            "_50": 27.809999465942383,
+            "_51": 28.35999870300293,
+            "_52": 28.920000076293945,
+            "_53": 29.479999542236328,
+            "_54": 30.029998779296875,
+            "_55": 30.59000015258789,
+            "_56": 31.139999389648437,
+            "_57": 31.69999885559082,
+            "_58": 32.2599983215332,
+            "_59": 32.80999755859375,
+            "_60": 33.369998931884766,
+            "_61": 33.93000030517578,
+            "_62": 34.47999954223633,
+            "_63": 35.040000915527344,
+            "_64": 35.59000015258789
+        }
+    },
+    getFontWidth: function(fontName, fontHeight, typeStr) {
+        let rst = 12;
+        if (fontWidthMap[fontName]) {
+            rst = fontWidthMap[fontName][typeStr]['_' + fontHeight];
+        } else {
+            rst = fontWidthMap['宋体'][typeStr]['_' + fontHeight];
+        }
+        return rst;
+    }
+};
+
+module.exports = fontWidthMap;

+ 224 - 85
modules/reports/rpt_component/jpc_cross_tab.js

@@ -102,16 +102,16 @@ JpcCrossTabSrv.prototype.createNew = function(){
             }
         }
     }
-    function private_SortAndOptimize(rptTpl, dataObj, dataSeq, sortTab, rstFieldsIdx) {
+    function private_SortAndOptimize(rptTpl, dataObj, dataSeq, sortTab, rstFieldsIdx, $CURRENT_RPT) {
         let result = [];
         let tab = rptTpl[JV.NODE_CROSS_INFO][sortTab];
         if (tab) {
             let sIDX = 0;
             //1. prepare and sort by tab-field
             let fields = [];
-            JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, tab[JV.TAB_CROSS_FIELDS], fields, rstFieldsIdx);
+            JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, tab[JV.PROP_CROSS_FIELDS], fields, rstFieldsIdx);
             let data_details = dataObj[JV.DATA_DETAIL_DATA];
-            JpcCrossTabHelper.sortTabFields(fields, rstFieldsIdx, data_details, dataSeq);
+            JpcCrossTabHelper.sortTabFields(fields, rstFieldsIdx, data_details, dataSeq, $CURRENT_RPT);
             //2. distinguish sort tab fields value
             let b1 = false;
             for (let i = 0; i < dataSeq.length; i++) {
@@ -123,9 +123,17 @@ JpcCrossTabSrv.prototype.createNew = function(){
                     for (let j = 1; j < dataSeq[i].length; j++) {
                         b1 = false;
                         for (let k = 0; k < rstFieldsIdx.length; k++) {
-                            if (data_details[rstFieldsIdx[k]][dataSeq[i][j - 1]] !== data_details[rstFieldsIdx[k]][dataSeq[i][j]]) {
-                                b1 = true;
-                                break;
+                            if (typeof(rstFieldsIdx[k]) === 'object') {
+                                let map_data_field = JE.F(rstFieldsIdx[k][JV.PROP_ID], $CURRENT_RPT);
+                                if (map_data_field[JV.PROP_AD_HOC_DATA][dataSeq[i][j - 1]] !== map_data_field[JV.PROP_AD_HOC_DATA][dataSeq[i][j]]) {
+                                    b1 = true;
+                                    break;
+                                }
+                            } else {
+                                if (data_details[rstFieldsIdx[k]][dataSeq[i][j - 1]] !== data_details[rstFieldsIdx[k]][dataSeq[i][j]]) {
+                                    b1 = true;
+                                    break;
+                                }
                             }
                         }
                         if (b1) {
@@ -148,7 +156,7 @@ JpcCrossTabSrv.prototype.createNew = function(){
         let result = [];
         let tab = rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_CONTENT];
         if (tab) {
-            JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, tab[JV.TAB_CROSS_FIELDS], null, rstFieldsIdx);
+            JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, tab[JV.PROP_CROSS_FIELDS], null, rstFieldsIdx);
         }
         for (let i = 0; i < rowSeq.length; i++) {
             let rl = rowSeq[i], cl = colSeq[i];
@@ -187,6 +195,7 @@ JpcCrossTabSrv.prototype.createNew = function(){
         me.dispSumValueLst_Col = [];
         me.page_seg_map = [];
         me.row_fields_idx = [];
+        me.row_fields_adhoc_idx = [];
         me.col_fields_idx = [];
         me.content_fields_idx = [];
         me.row_extension_fields_idx = [];
@@ -195,18 +204,27 @@ JpcCrossTabSrv.prototype.createNew = function(){
         me.pageStatusLst = [];
         me.paging_option = JV.PAGING_OPTION_NORMAL;
     };
-    JpcCrossTabResult.sorting = function(rptTpl, dataObj, dataSeq) {
+    JpcCrossTabResult.sorting = function(rptTpl, dataObj, dataSeq, $CURRENT_RPT) {
         let me = this;
         //IMPORTANT: the data should be sorted in SQL/NoSQL level!
-        me.sortedRowSequence = private_SortAndOptimize(rptTpl, dataObj, dataSeq, JV.NODE_CROSS_ROW, me.row_fields_idx);
-        me.sortedColSequence = private_SortAndOptimize(rptTpl, dataObj, dataSeq, JV.NODE_CROSS_COL, me.col_fields_idx);
+        me.sortedRowSequence = private_SortAndOptimize(rptTpl, dataObj, dataSeq, JV.NODE_CROSS_ROW, me.row_fields_idx, $CURRENT_RPT);
+        private_SortAndOptimize(rptTpl, dataObj, dataSeq, JV.NODE_CROSS_ROW_AD_HOC, me.row_fields_adhoc_idx, $CURRENT_RPT);
+        me.sortedColSequence = private_SortAndOptimize(rptTpl, dataObj, dataSeq, JV.NODE_CROSS_COL, me.col_fields_idx, $CURRENT_RPT);
         me.sortedContentSequence = private_SortForDisplayContent(rptTpl, me.sortedRowSequence, me.sortedColSequence, me.content_fields_idx);
-        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_COL_SUM][JV.TAB_CROSS_FIELDS], null, me.col_sum_fields_idx);
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_COL_SUM][JV.PROP_CROSS_FIELDS], null, me.col_sum_fields_idx);
         //pre-sum the data(for col sum display)
         let data_details = dataObj[JV.DATA_DETAIL_DATA],
             data_fields = [];
         for (let i = 0; i < me.col_sum_fields_idx.length; i++) {
-            let data_field = data_details[me.col_sum_fields_idx[i]];
+            let data_field = null;
+            if (typeof me.col_sum_fields_idx[i] === 'object') {
+                let exField = JE.F(me.col_sum_fields_idx[i][JV.PROP_ID], $CURRENT_RPT);
+                if (exField) {
+                    data_field = exField[JV.PROP_AD_HOC_DATA];
+                }
+            } else {
+                data_field = data_details[me.col_sum_fields_idx[i]];
+            }
             data_fields.push(data_field);
         }
         for (let i = 0; i < me.sortedRowSequence.length; i++) { //seg level
@@ -240,29 +258,29 @@ JpcCrossTabSrv.prototype.createNew = function(){
             maxRowRec = JpcCrossTabHelper.getMaxRowsPerPage(bands, rptTpl);
             maxColRec = JpcCrossTabHelper.getMaxColsPerPage(bands, rptTpl);
         }
-        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_EXT][JV.TAB_CROSS_FIELDS], null, me.row_extension_fields_idx);
-        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_SUM_EXT][JV.TAB_CROSS_FIELDS], null, me.row_sum_extension_fields_idx);
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_EXT][JV.PROP_CROSS_FIELDS], null, me.row_extension_fields_idx);
+        JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_SUM_EXT][JV.PROP_CROSS_FIELDS], null, me.row_sum_extension_fields_idx);
         if (me.paging_option === JV.PAGING_OPTION_INFINITY) {
             /*
-            rst = segCnt;
-            pageStatus[JV.STATUS_SEGMENT_START] = true;
-            pageStatus[JV.STATUS_SEGMENT_END] = true;
-            pageStatus[JV.STATUS_CROSS_ROW_END] = true;
-            pageStatus[JV.STATUS_CROSS_COL_END] = true;
-            for (let segIdx = 0; segIdx < segCnt; segIdx++) {
-                if (segIdx === segCnt - 1) {
-                    pageStatus[JV.STATUS_REPORT_END] = true;
-                }
-                if (segIdx > 0) {
-                    pageStatus[JV.STATUS_REPORT_START] = false;
-                }
-                me.pageStatusLst.push(pageStatus.slice(0));
-                pageIdx++;
-                private_addTabValue(me.dispValueIdxLst_Row, me.sortedRowSequence, segIdx, 0, me.sortedRowSequence[segIdx].length, me.dispSerialIdxLst_Row, me.col_sum_fields_value_total, me.dispSumValueLst_Col);
-                private_addTabValue(me.dispValueIdxLst_Col, me.sortedColSequence, segIdx, 0, me.sortedColSequence[segIdx].length, null, null, null);
-                private_addContentValue(me.dispValueIdxLst_Content, me.sortedContentSequence, segIdx, 0, me.sortedRowSequence[segIdx].length, 0, me.sortedColSequence[segIdx].length, me.page_seg_map, pageIdx);
-            }
-            //*/
+             rst = segCnt;
+             pageStatus[JV.STATUS_SEGMENT_START] = true;
+             pageStatus[JV.STATUS_SEGMENT_END] = true;
+             pageStatus[JV.STATUS_CROSS_ROW_END] = true;
+             pageStatus[JV.STATUS_CROSS_COL_END] = true;
+             for (let segIdx = 0; segIdx < segCnt; segIdx++) {
+             if (segIdx === segCnt - 1) {
+             pageStatus[JV.STATUS_REPORT_END] = true;
+             }
+             if (segIdx > 0) {
+             pageStatus[JV.STATUS_REPORT_START] = false;
+             }
+             me.pageStatusLst.push(pageStatus.slice(0));
+             pageIdx++;
+             private_addTabValue(me.dispValueIdxLst_Row, me.sortedRowSequence, segIdx, 0, me.sortedRowSequence[segIdx].length, me.dispSerialIdxLst_Row, me.col_sum_fields_value_total, me.dispSumValueLst_Col);
+             private_addTabValue(me.dispValueIdxLst_Col, me.sortedColSequence, segIdx, 0, me.sortedColSequence[segIdx].length, null, null, null);
+             private_addContentValue(me.dispValueIdxLst_Content, me.sortedContentSequence, segIdx, 0, me.sortedRowSequence[segIdx].length, 0, me.sortedColSequence[segIdx].length, me.page_seg_map, pageIdx);
+             }
+             //*/
         } else {
             for (let segIdx = 0; segIdx < segCnt; segIdx++) {
                 //2.1. seg level initialize
@@ -321,9 +339,80 @@ JpcCrossTabSrv.prototype.createNew = function(){
         bands = null;
         return rst;
     };
-    JpcCrossTabResult.outputAsPreviewPage = function (rptTpl, bands, controls, $CURRENT_RPT) {
+    JpcCrossTabResult.outputAsPreviewPage = function (rptTpl, bands, controls, $CURRENT_RPT, customizeCfg) {
+        let me = this, rst = [];
+        let pageStatus = [true, true, true, true, true, true, true, true];
+        me.pageStatusLst.push(pageStatus);
+        // JpcFieldHelper.findAndPutDataFieldIdx(rptTpl, rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_CONTENT][JV.PROP_FLOW_FIELDS], null, me.disp_fields_idx, false);
+        JpcBandHelper.setBandArea(bands, rptTpl, pageStatus, true, false);
+        let maxRowRec = JpcCrossTabHelper.getMaxRowsPerPage(bands, rptTpl);
+        let maxColRec = JpcCrossTabHelper.getMaxColsPerPage(bands, rptTpl);
+        let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
+        //1. 交叉行
+        rst = rst.concat(me.outputPreviewRowTab(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, unitFactor));
+        //2. 交叉列
+        rst = rst.concat(me.outputPreviewColTab(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxColRec, unitFactor));
+        //3. 交叉数据
+        rst = rst.concat(me.outputPreviewContent(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, maxColRec, unitFactor));
+        //4. 交叉行拓展
+        rst = rst.concat(me.outputPreviewTabExt(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxColRec, unitFactor));
+        //5. 交叉行拓展合计
+        rst = rst.concat(me.outputPreviewSumTabExt(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, unitFactor));
+        //6. 交叉列合计
+        rst = rst.concat(me.outputPreviewTabSum(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, JV.NODE_CROSS_COL_SUM, unitFactor));
+        //7. 离散
+        rst = rst.concat(JpcDiscreteHelper.outputPreviewDiscreteInfo(rptTpl[JV.NODE_CROSS_INFO][JV.NODE_DISCRETE_INFO], bands, unitFactor, pageStatus));
+        return rst;
+    };
+    JpcCrossTabResult.outputPreviewRowTab = function(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, unitFactor) {
+        let rst = this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, 1, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW], unitFactor);
+        rst = rst.concat(this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, 1, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_AD_HOC], unitFactor));
+        return rst;
+    };
+    JpcCrossTabResult.outputPreviewColTab = function(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxColRec, unitFactor) {
+        return this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, 1, maxColRec, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_COL], unitFactor);
+    };
+    JpcCrossTabResult.outputPreviewContent = function(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, maxColRec, unitFactor) {
+        return this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, maxColRec, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_CONTENT], unitFactor);
+    };
+    JpcCrossTabResult.outputPreviewTabSum = function(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, tabNodeName, unitFactor) {
+        return this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, 1, rptTpl[JV.NODE_CROSS_INFO][tabNodeName], unitFactor);
+    };
+    JpcCrossTabResult.outputPreviewTabExt = function(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxColRec, unitFactor) {
+        //交叉行拓展
+        return this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, 1, maxColRec, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_EXT], unitFactor);
+    };
+    JpcCrossTabResult.outputPreviewSumTabExt = function(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, unitFactor) {
+        //交叉行拓展合计
+        return this.private_OutputPreviewCommon(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, 1, 1, rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_SUM_EXT], unitFactor);
+    };
+    JpcCrossTabResult.private_OutputPreviewCommon = function(rptTpl, bands, controls, $CURRENT_RPT, customizeCfg, maxRowRec, maxColRec, tab, unitFactor) {
         let me = this, rst = [];
-        //...
+        let band = (tab)?bands[tab[JV.PROP_BAND_NAME]]:null;
+        if (band) {
+            let tab_fields = tab[JV.PROP_CROSS_FIELDS];
+            for (let rowIdx = 0; rowIdx < maxRowRec; rowIdx++) {
+                for (let i = 0; i < tab_fields.length; i++) {
+                    let tab_field = tab_fields[i];
+                    if (!(tab_field[JV.PROP_HIDDEN])) {
+                        for (let colIdx = 0; colIdx < maxColRec; colIdx++) {
+                            if (tab_field[JV.PROP_IS_SERIAL]) {
+                                rst.push(me.outputTabField(band, tab_field, [rowIdx + 1], 0, -1, maxRowRec, rowIdx, maxColRec, colIdx, unitFactor, false, controls));
+                            } else {
+                                rst.push(me.outputTabField(band, tab_field, null, -1, -1, maxRowRec, rowIdx, maxColRec, colIdx, unitFactor, false, controls));
+                            }
+                        }
+                    }
+                }
+                if (tab[JV.PROP_TEXTS]) {
+                    for (let j = 0; j < tab[JV.PROP_TEXTS].length; j++) {
+                        for (let colIdx = 0; colIdx < maxColRec; colIdx++) {
+                            rst.push(JpcTextHelper.outputText(tab[JV.PROP_TEXTS][j], band, unitFactor, maxRowRec, rowIdx, maxColRec, colIdx, 1, 0));
+                        }
+                    }
+                }
+            }
+        }
         return rst;
     };
     JpcCrossTabResult.outputAsSimpleJSONPage = function(rptTpl, dataObj, page, bands, controls, $CURRENT_RPT, customizeCfg) {
@@ -331,50 +420,52 @@ JpcCrossTabSrv.prototype.createNew = function(){
         let unitFactor = JpcCommonHelper.getUnitFactor(rptTpl);
         if (me.paging_option === JV.PAGING_OPTION_INFINITY) {
             /*
-            let segIdx = page - 1;
-            //1 calculate the band position
-            JpcBandHelper.setBandArea(bands, rptTpl, me.pageStatusLst[page - 1]);
-            //2. then reset the band height
-            let tab = rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_CONTENT];
-            let crossContentBand = bands[tab[JV.PROP_BAND_NAME]];
-            let actH = JpcCrossTabHelper.getActualRowsHeight(bands, rptTpl, me.sortedRowSequence, page);
-            let actW = JpcCrossTabHelper.getActualColsWidth(bands, rptTpl, me.sortedColSequence, page);
-            let offsetY = actH - (crossContentBand.Bottom - crossContentBand.Top);
-            let offsetX = actW - (crossContentBand.Right - crossContentBand.Left);
-            JpcBandHelper.resetBandPos(rptTpl[JV.NODE_BAND_COLLECTION], bands, crossContentBand, offsetX, offsetY);
+             let segIdx = page - 1;
+             //1 calculate the band position
+             JpcBandHelper.setBandArea(bands, rptTpl, me.pageStatusLst[page - 1]);
+             //2. then reset the band height
+             let tab = rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_CONTENT];
+             let crossContentBand = bands[tab[JV.PROP_BAND_NAME]];
+             let actH = JpcCrossTabHelper.getActualRowsHeight(bands, rptTpl, me.sortedRowSequence, page);
+             let actW = JpcCrossTabHelper.getActualColsWidth(bands, rptTpl, me.sortedColSequence, page);
+             let offsetY = actH - (crossContentBand.Bottom - crossContentBand.Top);
+             let offsetX = actW - (crossContentBand.Right - crossContentBand.Left);
+             JpcBandHelper.resetBandPos(rptTpl[JV.NODE_BAND_COLLECTION], bands, crossContentBand, offsetX, offsetY);
 
-            //2.1 Row-Tab
-            tabRstLst.push(me.outputRowTab(rptTpl, dataObj, page, bands, unitFactor, controls));
-            //2.2 Col-Tab
-            tabRstLst.push(me.outputColTab(rptTpl, dataObj, page, bands, unitFactor, controls));
-            //2.3 Content-Tab
-            tabRstLst.push(me.outputContent(rptTpl, dataObj, page, bands, unitFactor, controls));
-            //2.4 Sum-Tab Row
-            //2.4 Sum-tab Col
-            tabRstLst.push(me.outputTabSum(rptTpl, dataObj, page, bands, unitFactor, JV.NODE_CROSS_COL_SUM, controls));
-            //2.x row tab ext
-            tabRstLst.push(me.outputTabExt(rptTpl, dataObj, page, bands, unitFactor, controls));
-            tabRstLst.push(me.outputSumTabExt(rptTpl, dataObj, page, bands, unitFactor, segIdx, controls));
-            //2.5 Discrete
-            tabRstLst.push(JpcDiscreteHelper.outputDiscreteInfo(rptTpl[JV.NODE_CROSS_INFO][JV.NODE_DISCRETE_INFO], bands, dataObj, unitFactor, me.pageStatusLst[page - 1], segIdx, 1, 0, $CURRENT_RPT, customizeCfg));
-            //*/
+             //2.1 Row-Tab
+             tabRstLst.push(me.outputRowTab(rptTpl, dataObj, page, bands, unitFactor, controls));
+             //2.2 Col-Tab
+             tabRstLst.push(me.outputColTab(rptTpl, dataObj, page, bands, unitFactor, controls));
+             //2.3 Content-Tab
+             tabRstLst.push(me.outputContent(rptTpl, dataObj, page, bands, unitFactor, controls));
+             //2.4 Sum-Tab Row
+             //2.4 Sum-tab Col
+             tabRstLst.push(me.outputTabSum(rptTpl, dataObj, page, bands, unitFactor, JV.NODE_CROSS_COL_SUM, controls));
+             //2.x row tab ext
+             tabRstLst.push(me.outputTabExt(rptTpl, dataObj, page, bands, unitFactor, controls));
+             tabRstLst.push(me.outputSumTabExt(rptTpl, dataObj, page, bands, unitFactor, segIdx, controls));
+             //2.5 Discrete
+             tabRstLst.push(JpcDiscreteHelper.outputDiscreteInfo(rptTpl[JV.NODE_CROSS_INFO][JV.NODE_DISCRETE_INFO], bands, dataObj, unitFactor, me.pageStatusLst[page - 1], segIdx, 1, 0, $CURRENT_RPT, customizeCfg));
+             //*/
         } else {
             let segIdx = JpcCommonHelper.getSegIdxByPageIdx(page, me.page_seg_map);
             //1 calculate the band position
             JpcBandHelper.setBandArea(bands, rptTpl, me.pageStatusLst[page - 1]);
             //2. start to output detail-part
             //2.1 Row-Tab
-            tabRstLst.push(me.outputRowTab(rptTpl, dataObj, page, bands, unitFactor, controls, $CURRENT_RPT, customizeCfg));
+            //tabRstLst.push(me.outputRowTab(rptTpl, dataObj, page, bands, unitFactor, controls, $CURRENT_RPT, customizeCfg));
+            tabRstLst.push(me.outputRowTabCommon(rptTpl, dataObj, page, bands, JV.NODE_CROSS_ROW, me.row_fields_idx, unitFactor, controls, $CURRENT_RPT, customizeCfg));
+            tabRstLst.push(me.outputRowTabCommon(rptTpl, dataObj, page, bands, JV.NODE_CROSS_ROW_AD_HOC, me.row_fields_adhoc_idx, unitFactor, controls, $CURRENT_RPT, customizeCfg));
             //2.2 Col-Tab
             tabRstLst.push(me.outputColTab(rptTpl, dataObj, page, bands, unitFactor, controls, $CURRENT_RPT, customizeCfg));
             //2.3 Content-Tab
             tabRstLst.push(me.outputContent(rptTpl, dataObj, page, bands, unitFactor, controls, $CURRENT_RPT, customizeCfg));
             //2.4 Sum-Tab Row
             //2.4 Sum-tab Col
-            tabRstLst.push(me.outputTabSum(rptTpl, dataObj, page, bands, unitFactor, JV.NODE_CROSS_COL_SUM, controls));
+            tabRstLst.push(me.outputTabSum(rptTpl, dataObj, page, bands, unitFactor, JV.NODE_CROSS_COL_SUM, controls, $CURRENT_RPT, customizeCfg));
             //2.x row tab ext
-            tabRstLst.push(me.outputTabExt(rptTpl, dataObj, page, bands, unitFactor, controls));
-            tabRstLst.push(me.outputSumTabExt(rptTpl, dataObj, page, bands, unitFactor, segIdx, controls));
+            tabRstLst.push(me.outputTabExt(rptTpl, dataObj, page, bands, unitFactor, controls, $CURRENT_RPT, customizeCfg));
+            tabRstLst.push(me.outputSumTabExt(rptTpl, dataObj, page, bands, unitFactor, segIdx, controls, $CURRENT_RPT, customizeCfg));
             //2.5 Discrete
             tabRstLst.push(JpcDiscreteHelper.outputDiscreteInfo(rptTpl[JV.NODE_CROSS_INFO][JV.NODE_DISCRETE_INFO], bands, dataObj, unitFactor, me.pageStatusLst[page - 1], segIdx, 1, 0, $CURRENT_RPT, customizeCfg));
         }
@@ -384,23 +475,30 @@ JpcCrossTabSrv.prototype.createNew = function(){
         }
         return rst;
     };
-    JpcCrossTabResult.outputRowTab = function(rptTpl, dataObj, page, bands, unitFactor, controls, $CURRENT_RPT, customizeCfg) {
+    JpcCrossTabResult.outputRowTabCommon = function(rptTpl, dataObj, page, bands, tabStr, rowFieldsIdxArr, unitFactor, controls, $CURRENT_RPT, customizeCfg) {
         let me = this, rst = [];
-        let tab = rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW];
-        let band = bands[tab[JV.PROP_BAND_NAME]];
+        let tab = rptTpl[JV.NODE_CROSS_INFO][tabStr];
+        let band = (tab)?bands[tab[JV.PROP_BAND_NAME]]:null;
         if (band) {
             let pageStatus = me.pageStatusLst[page - 1];
             if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
-                let tab_fields = tab[JV.TAB_CROSS_FIELDS];
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS];
                 let data_details = dataObj[JV.DATA_DETAIL_DATA];
                 let valuesIdx = me.dispValueIdxLst_Row[page - 1];
                 let serialsIdx = me.dispSerialIdxLst_Row[page - 1];
                 let flexiblePrecisionRefObj = null, flexibleRefField = null, precision_ref_data = null;
-                for (let i = 0; i < me.row_fields_idx.length; i++) {
+                for (let i = 0; i < rowFieldsIdxArr.length; i++) {
                     let tab_field = tab_fields[i];
-                    let data_field = data_details[me.row_fields_idx[i]];
-                    let map_data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
                     if (!(tab_field[JV.PROP_HIDDEN])) {
+                        let data_field = null;
+                        let map_data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
+                        if (typeof rowFieldsIdxArr[i] !== 'object') {
+                            data_field = data_details[rowFieldsIdxArr[i]];
+                        } else {
+                            if (map_data_field) {
+                                data_field = map_data_field[JV.PROP_AD_HOC_DATA];
+                            }
+                        }
                         let rows = valuesIdx.length;
                         for (let rowIdx = 0; rowIdx < rows; rowIdx++) {
                             if (map_data_field && map_data_field[JV.PROP_PRECISION] && map_data_field.flexiblePrecisionRefObj) {
@@ -431,15 +529,22 @@ JpcCrossTabSrv.prototype.createNew = function(){
         if (band) {
             let pageStatus = me.pageStatusLst[page - 1];
             if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
-                let tab_fields = tab[JV.TAB_CROSS_FIELDS];
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS];
                 let data_details = dataObj[JV.DATA_DETAIL_DATA];
                 let valuesIdx = me.dispValueIdxLst_Col[page - 1];
                 let flexiblePrecisionRefObj = null, flexibleRefField = null, precision_ref_data = null;
                 for (let i = 0; i < me.col_fields_idx.length; i++) {
                     let tab_field = tab_fields[i];
-                    let data_field = data_details[me.col_fields_idx[i]];
-                    let map_data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
                     if (!(tab_field[JV.PROP_HIDDEN])) {
+                        let data_field = null;
+                        let map_data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
+                        if (typeof me.col_fields_idx[i] !== 'object') {
+                            data_field = data_details[me.col_fields_idx[i]];
+                        } else {
+                            if (map_data_field) {
+                                data_field = map_data_field[JV.PROP_AD_HOC_DATA];
+                            }
+                        }
                         let cols = valuesIdx.length;
                         for (let colIdx = 0; colIdx < cols; colIdx++) {
                             if (map_data_field && map_data_field[JV.PROP_PRECISION] && map_data_field.flexiblePrecisionRefObj) {
@@ -482,14 +587,21 @@ JpcCrossTabSrv.prototype.createNew = function(){
         if (band) {
             let pageStatus = me.pageStatusLst[page - 1];
             if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
-                let tab_fields = tab[JV.TAB_CROSS_FIELDS];
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS];
                 let data_details = dataObj[JV.DATA_DETAIL_DATA];
                 let contentValuesIdx = me.dispValueIdxLst_Content[page - 1];
                 let flexiblePrecisionRefObj = null, flexibleRefField = null, precision_ref_data = null;
                 for (let i = 0; i < tab_fields.length; i++) {
                     let tab_field = tab_fields[i];
-                    let data_field = data_details[me.content_fields_idx[i]];
+                    let data_field = null;
                     let map_data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
+                    if (typeof me.content_fields_idx[i] !== 'object') {
+                        data_field = data_details[me.content_fields_idx[i]];
+                    } else {
+                        if (map_data_field) {
+                            data_field = map_data_field[JV.PROP_AD_HOC_DATA];
+                        }
+                    }
                     if (!(tab_field[JV.PROP_HIDDEN])) {
                         let rows = contentValuesIdx.length;
                         for (let rowIdx = 0; rowIdx < rows; rowIdx++) {
@@ -517,15 +629,21 @@ JpcCrossTabSrv.prototype.createNew = function(){
         }
         return rst;
     };
-    JpcCrossTabResult.outputTabSum = function (rptTpl, dataObj, page, bands, unitFactor, tabNodeName, controls) {
+    JpcCrossTabResult.outputTabSum = function (rptTpl, dataObj, page, bands, unitFactor, tabNodeName, controls, $CURRENT_RPT, customizeCfg) {
         let me = this, rst = [],
             tab = rptTpl[JV.NODE_CROSS_INFO][tabNodeName],
             band = bands[tab[JV.PROP_BAND_NAME]];
         if (band) {
             let pageStatus = me.pageStatusLst[page - 1];
             if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
-                let tab_fields = tab[JV.TAB_CROSS_FIELDS];
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS];
                 for (let i = 0; i < me.dispSumValueLst_Col[page - 1].length; i++) {
+                    if (i === 0) {
+                        for (let tfIdx = 0; tfIdx < tab_fields.length; tfIdx++) {
+                            let map_data_field = JE.F(tab_fields[tfIdx][JV.PROP_FIELD_ID], $CURRENT_RPT);
+                            JpcFieldHelper.resetFormat(tab_fields[tfIdx], map_data_field, customizeCfg);
+                        }
+                    }
                     if (me.dispSumValueLst_Col[page - 1][i] !== null) {
                         for (let j = 0; j < me.dispSumValueLst_Col[page - 1][i].length; j++) {
                             let tab_field = tab_fields[j];
@@ -555,22 +673,32 @@ JpcCrossTabSrv.prototype.createNew = function(){
         }
         return rst;
     };
-    JpcCrossTabResult.outputTabExt = function (rptTpl, dataObj, page, bands, unitFactor, controls) {
+    JpcCrossTabResult.outputTabExt = function (rptTpl, dataObj, page, bands, unitFactor, controls, $CURRENT_RPT, customizeCfg) {
         let me = this, rst = [], firstTextOutput = true,
             tab = rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_EXT];
         let band = bands[tab[JV.PROP_BAND_NAME]];
         if (band) {
             let pageStatus = me.pageStatusLst[page - 1];
             if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true) {
-                let tab_fields = tab[JV.TAB_CROSS_FIELDS],
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS],
                     data_details = dataObj[JV.DATA_DETAIL_DATA],
                     valuesIdx = me.dispValueIdxLst_Col[page - 1];
                 for (let i = 0; i < me.row_extension_fields_idx.length; i++) {
                     let tab_field = tab_fields[i];
-                    let data_field = data_details[me.row_extension_fields_idx[i]];
+                    let data_field = null;
+                    let map_data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
+                    if (typeof me.row_extension_fields_idx[i] !== 'object') {
+                        data_field = data_details[me.row_extension_fields_idx[i]];
+                    } else {
+                        if (map_data_field) {
+                            data_field = map_data_field[JV.PROP_AD_HOC_DATA];
+                        }
+                    }
+
                     if (!(tab_field[JV.PROP_HIDDEN])) {
                         let cols = valuesIdx.length;
                         for (let colIdx = 0; colIdx < cols; colIdx++) {
+                            if (colIdx === 0) JpcFieldHelper.resetFormat(tab_field, map_data_field, customizeCfg);
                             rst.push(me.outputTabField(band, tab_field, data_field, valuesIdx[colIdx], -1, 1, 0, cols, colIdx, unitFactor, false, controls));
                             //2. output texts if has
                             if (firstTextOutput) {
@@ -591,18 +719,29 @@ JpcCrossTabSrv.prototype.createNew = function(){
         }
         return rst;
     };
-    JpcCrossTabResult.outputSumTabExt = function (rptTpl, dataObj, page, bands, unitFactor, segIdx, controls) {
+    JpcCrossTabResult.outputSumTabExt = function (rptTpl, dataObj, page, bands, unitFactor, segIdx, controls, $CURRENT_RPT, customizeCfg) {
         let me = this, rst = [],
             tab = rptTpl[JV.NODE_CROSS_INFO][JV.NODE_CROSS_ROW_SUM_EXT];
         let band = bands[tab[JV.PROP_BAND_NAME]];
         if (band) {
             let pageStatus = me.pageStatusLst[page - 1];
             if (pageStatus[band[JV.BAND_PROP_DISPLAY_TYPE]] === true && pageStatus[JV.STATUS_CROSS_ROW_END] === true) {
-                let tab_fields = tab[JV.TAB_CROSS_FIELDS],
+                let tab_fields = tab[JV.PROP_CROSS_FIELDS],
                     data_details = dataObj[JV.DATA_DETAIL_DATA],
                     data_fields = [];
                 for (let i = 0; i < me.row_sum_extension_fields_idx.length; i++) {
-                    let data_field = data_details[me.row_sum_extension_fields_idx[i]];
+                    // let data_field = data_details[me.row_sum_extension_fields_idx[i]];
+                    let tab_field = tab_fields[i];
+                    let data_field = null;
+                    let map_data_field = JE.F(tab_field[JV.PROP_FIELD_ID], $CURRENT_RPT);
+                    if (typeof me.row_sum_extension_fields_idx[i] !== 'object') {
+                        data_field = data_details[me.row_sum_extension_fields_idx[i]];
+                    } else {
+                        if (map_data_field) {
+                            data_field = map_data_field[JV.PROP_AD_HOC_DATA];
+                        }
+                    }
+                    JpcFieldHelper.resetFormat(tab_field, map_data_field, customizeCfg);
                     data_fields.push(data_field);
                 }
                 //2. initialize grand total value

+ 0 - 0
modules/reports/rpt_component/jpc_ex.js


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