Просмотр исходного кода

Merge branch '1.0.0_online' of http://192.168.1.41:3000/SmartCost/ConstructionCost into 1.0.0_online

TonyKang 6 лет назад
Родитель
Сommit
ffbbc61970
100 измененных файлов с 5078 добавлено и 1227 удалено
  1. 0 0
      .tgitconfig
  2. 2 4
      Dockerfile
  3. 16 4
      config/config.js
  4. 4 1
      config/gulpConfig.js
  5. 70 0
      importserver.js
  6. 16 0
      lib/jquery-contextmenu/jquery.contextMenu.js
  7. 1 1
      modules/all_models/calc_program_model.js
  8. 74 0
      modules/all_models/config_material_list.js
  9. 0 25
      modules/all_models/evaluate_list.js
  10. 5 4
      modules/all_models/project_glj.js
  11. 1 1
      modules/all_models/ration_glj.js
  12. 1 1
      modules/all_models/stdRation_ration.js
  13. 1 1
      modules/all_models/unit_price.js
  14. 2 1
      modules/common/base/base_model.js
  15. 3 1
      modules/common/const/bills_fixed.js
  16. 21 3
      modules/complementary_glj_lib/models/gljModel.js
  17. 14 1
      modules/complementary_ration_lib/controllers/compleViewController.js
  18. 34 42
      modules/complementary_ration_lib/models/compleRationModel.js
  19. 7 7
      modules/complementary_ration_lib/models/searchModel.js
  20. 6 0
      modules/complementary_ration_lib/models/sectionTreeModel.js
  21. 1 0
      modules/complementary_ration_lib/routes/routes.js
  22. 13 0
      modules/glj/controllers/glj_controller.js
  23. 37 2
      modules/glj/facade/glj_facade.js
  24. 143 77
      modules/glj/models/glj_list_model.js
  25. 0 1
      modules/glj/models/mix_ratio_model.js
  26. 2 2
      modules/glj/models/unit_price_file_model.js
  27. 4 0
      modules/glj/routes/glj_router.js
  28. 59 0
      modules/import/controllers/import_controller.js
  29. 16 0
      modules/import/routes/import_route.js
  30. 24 0
      modules/main/facade/bid_facade.js
  31. 24 0
      modules/main/facade/contractor_facade.js
  32. 24 0
      modules/main/facade/evaluate_facade.js
  33. 17 0
      modules/main/facade/project_facade.js
  34. 6 0
      modules/main/models/project.js
  35. 4 1
      modules/main/models/project_consts.js
  36. 0 1
      modules/main/routes/main_route.js
  37. 6 0
      modules/pm/controllers/new_proj_controller.js
  38. 25 8
      modules/pm/controllers/pm_controller.js
  39. 71 8
      modules/pm/facade/pm_facade.js
  40. 9 1
      modules/templates/models/bills_template.js
  41. 2 2
      modules/users/models/user_model.js
  42. 1 0
      package.json
  43. 60 0
      public/billsUtil.js
  44. 41 0
      public/common_util.js
  45. 20 3
      public/counter/counter.js
  46. 14 33
      public/web/PerfectLoad.js
  47. 28 21
      public/web/gljUtil.js
  48. 3 3
      public/web/headerOpr.js
  49. 5 3
      public/web/sheet/sheet_common.js
  50. 4 2
      public/web/slideResize.js
  51. 1 1
      server.js
  52. 1 1
      web/building_saas/complementary_glj_lib/js/glj.js
  53. 3 61
      web/building_saas/complementary_ration_lib/html/dinge.html
  54. 24 37
      web/building_saas/complementary_ration_lib/js/gljSelect.js
  55. 79 0
      web/building_saas/complementary_ration_lib/js/init.js
  56. 1 23
      web/building_saas/complementary_ration_lib/js/ration.js
  57. 9 16
      web/building_saas/complementary_ration_lib/js/ration_glj.js
  58. 11 17
      web/building_saas/complementary_ration_lib/js/ration_installation.js
  59. 18 50
      web/building_saas/complementary_ration_lib/js/section_tree.js
  60. 84 1
      web/building_saas/css/custom.css
  61. 265 222
      web/building_saas/css/main.css
  62. 89 0
      web/building_saas/glj/html/project_glj.html
  63. BIN
      web/building_saas/img/building2.png
  64. BIN
      web/building_saas/img/logo.png
  65. BIN
      web/building_saas/img/qqgroupqrcode.png
  66. 14 5
      web/building_saas/js/global.js
  67. 11 5
      web/building_saas/main/html/main.html
  68. 59 12
      web/building_saas/main/js/controllers/block_controller.js
  69. 2 0
      web/building_saas/main/js/models/bills.js
  70. 0 1
      web/building_saas/main/js/models/calc_base.js
  71. 35 29
      web/building_saas/main/js/models/calc_program.js
  72. 1 1
      web/building_saas/main/js/models/exportSEIInterface.js
  73. 63 39
      web/building_saas/main/js/models/exportStandardInterface.js
  74. 132 64
      web/building_saas/main/js/models/exportStdInterfaceBase.js
  75. 1030 0
      web/building_saas/main/js/models/exportStdInterface_gd18.js
  76. 6 3
      web/building_saas/main/js/models/fee_rate.js
  77. 204 92
      web/building_saas/main/js/models/importStandardInterface.js
  78. 207 0
      web/building_saas/main/js/models/importStdInterfaceBase.js
  79. 10 3
      web/building_saas/main/js/models/main_consts.js
  80. 8 1
      web/building_saas/main/js/models/project.js
  81. 48 7
      web/building_saas/main/js/models/project_glj.js
  82. 1 0
      web/building_saas/main/js/views/calc_program_view.js
  83. 1038 0
      web/building_saas/main/js/views/config_material_view.js
  84. 6 2
      web/building_saas/main/js/views/fee_rate_view.js
  85. 1 1
      web/building_saas/main/js/views/glj_col.js
  86. 14 5
      web/building_saas/main/js/views/glj_view.js
  87. 1 1
      web/building_saas/main/js/views/importBills.js
  88. 18 10
      web/building_saas/main/js/views/locate_view.js
  89. 258 148
      web/building_saas/main/js/views/material_adjust_view.js
  90. 64 5
      web/building_saas/main/js/views/project_glj_view.js
  91. 107 25
      web/building_saas/main/js/views/project_view.js
  92. 1 0
      web/building_saas/main/js/views/tender_price_view.js
  93. 2 1
      web/building_saas/main/js/views/zmhs_view.js
  94. 29 15
      web/building_saas/pm/html/project-management.html
  95. 5 1
      web/building_saas/pm/js/pm_import.js
  96. 129 45
      web/building_saas/pm/js/pm_newMain.js
  97. 4 3
      web/building_saas/pm/js/pm_share.js
  98. 31 8
      web/building_saas/pm/js/pm_tree.js
  99. 13 1
      web/common/html/header.html
  100. 0 0
      web/dest/css/img/edit.png

+ 0 - 0
.tgitconfig


+ 2 - 4
Dockerfile

@@ -8,10 +8,8 @@ RUN cnpm install
 
 RUN gulp build
 
-EXPOSE 6060
-
 ENV NODE_ENV=prod
 
-ENTRYPOINT babel-node server.js
-
+ENTRYPOINT ["babel-node", "--max-old-space-size=2048"]
 
+CMD ["server.js"]

Разница между файлами не показана из-за своего большого размера
+ 16 - 4
config/config.js


+ 4 - 1
config/gulpConfig.js

@@ -49,6 +49,7 @@ 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',
@@ -149,6 +150,7 @@ module.exports = {
         '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',
@@ -236,7 +238,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!');
+});
+
+

+ 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,

+ 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,

+ 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"}));

+ 0 - 25
modules/all_models/evaluate_list.js

@@ -1,25 +0,0 @@
-/**
- * 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:String,
-    projectID: {type:Number,index: true},
-    projectGLJID:Number,//关联工料机ID
-    seq:String,//序号
-    code:String,
-    name:String,
-    specs:String,
-    unit:String,
-    market_price: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"}));

+ 5 - 4
modules/all_models/project_glj.js

@@ -25,12 +25,12 @@ let modelSchema = {
     // 编码
     code: {
         type: String,
-        index: false
+        index: true
     },
     //原始的编码
     original_code: {
         type: String,
-        index: false
+        index: true
     },
     // 名称
     name: {
@@ -118,12 +118,13 @@ let modelSchema = {
         type: Number,
         default: 0
     },
-    is_info_adjust:{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,//现行价格指数
+    FI:String,//现行价格指数*/
     ratio_data: Schema.Types.Mixed,
     remark:String,
     originPlace:String,//产地

+ 1 - 1
modules/all_models/ration_glj.js

@@ -12,7 +12,7 @@ var ration_glj = new Schema({
     GLJID:Number,
     repositoryId:Number,
     projectID: Number,
-    rationID:String,
+    rationID:{type: String, index: true},
     projectGLJID:Number,
     name:String,
     code:String,

+ 1 - 1
modules/all_models/stdRation_ration.js

@@ -40,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,

+ 1 - 1
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,
     // 市场单价

+ 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){

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

@@ -64,7 +64,9 @@ const fixedFlag = {
     //住宅工程质量分户验收费
     HOUSE_QUALITY_ACCEPT_FEE:31,
     //组织措施费
-    ORGANIZATION:32
+    ORGANIZATION:32,
+    //其他措施费
+    OTHER_MEASURE_FEE:33
 };
 
 export default fixedFlag;

+ 21 - 3
modules/complementary_glj_lib/models/gljModel.js

@@ -46,9 +46,10 @@ class GljDao {
         }
     }
 
-    async getQueryByType ({userID, compilationId, gljLibId, type, replace, location, classList, search}) {
+    async getQueryByType ({userID, compilationId, gljLibId, code, type, replace, location, classList, search}) {
         let model = null,
             query = {},
+            countQuery = {},
             matchLocation = false;
         if (type === libType.stdGLJ) {
             model = stdGljModel;
@@ -82,9 +83,15 @@ class GljDao {
         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
         };
     }
@@ -103,13 +110,24 @@ class GljDao {
             // 显示的数据不能太少,否则数据没有占满整个表格区域(实际上数据条目应该超过表格显示区域),让人误以为数据只有那么点,不再滚动触发分页
             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);
-        const total = await queryData.model.find(queryData.query).count();
+        //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, projection, callback){
         let me = this;

+ 14 - 1
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});
 };
@@ -53,6 +54,18 @@ class CompleViewController extends BaseController{
         });
     }
 
+    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`;

+ 34 - 42
modules/complementary_ration_lib/models/compleRationModel.js

@@ -8,8 +8,14 @@ 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');
@@ -153,26 +159,16 @@ 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 = gljUtil.sortRationGLJ(stdGljs.concat(comGljs),true);
-          /*  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;
-            });*/
             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("</label>");
                 hintsArr.push("");
             }
             for(let glj of gljDatas){
@@ -244,20 +240,12 @@ class CompleRatoinDao {
                 stdGljIds.push(rationGlj.gljId);
             }
             if(stdGljIds.length > 0) {
-                stdGljs = await stdGljModel.find({ID: {$in: stdGljIds}});
+                stdGljs = await stdGljModel.find({ID: {$in: stdGljIds}}).lean();
+                /*stdGljs.forEach(function (glj) {
+                    glj.type = glj.gljType;
+                });*/
             }
             let gljDatas =  gljUtil.sortRationGLJ(stdGljs,true);
-        /*    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;
-            });*/
-            gljDatas = gljUtil.sortRationGLJ(gljDatas,true);
             if(ration.jobContent && ration.jobContent.toString().trim() !== ''){
                 hintsArr.push(`<label class="nomargin font_blue">工作内容:`);
                 hintsArr = hintsArr.concat(ration.jobContent.split('\n'));
@@ -528,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
         }
     }
 }

+ 7 - 7
modules/complementary_ration_lib/models/searchModel.js

@@ -15,13 +15,13 @@ const compleRationLib = 'compleRationLib';
 class SearchDao{
     async getRationItem(userId, compilationId, rationRepIds, code, ID, callback){
         let ration = null;
+        let otherLibs=[];
         try{
-            let firstLib = rationRepIds.shift();//优先取第一个
-            if(rationRepIds.includes(firstLib)) {//去掉重复的库ID
-                rationRepIds.splice(rationRepIds.indexOf(firstLib), 1);
-            }
-            if(rationRepIds.includes(compleRationLib)) {
-                rationRepIds.splice(rationRepIds.indexOf(compleRationLib), 1);
+            let firstLib = rationRepIds[0];//优先取第一个
+            for (let l of rationRepIds){
+                if(l != firstLib && l != compleRationLib){
+                    otherLibs.push(l);
+                }
             }
             if(firstLib == compleRationLib){//说明选中的是补充定额库
                 ration = await this.getCompleRation(userId,compilationId,code,ID);
@@ -34,7 +34,7 @@ class SearchDao{
                 ration = await this.getStdRation(firstQuery);
             }
             if(ration == null){//选中的定额库或者默认的定额库中没有找到定额,才走常规的流程查找其它定额库
-                let stdQuery = {rationRepId: {$in: rationRepIds}, code: code, $or: [{isDeleted: null}, {isDeleted: false}]};
+                let stdQuery = {rationRepId: {$in: otherLibs}, code: code, $or: [{isDeleted: null}, {isDeleted: false}]};
                 if(ID){
                     stdQuery = {ID: ID, $or: [{isDeleted: null}, {isDeleted: false}]};
                 }

+ 6 - 0
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 = {

+ 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);

+ 13 - 0
modules/glj/controllers/glj_controller.js

@@ -699,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

+ 37 - 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;
@@ -77,4 +84,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;
+        }
+    }
+}

+ 143 - 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 {
 
@@ -556,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查找单价文件数据,用来判断单价文件信息是否已存在
 
@@ -604,22 +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,
-                    materialIndexType: tmp.materialIndexType,
-                    materialIndexUnit: tmp.materialIndexUnit,
-                    materialIndexCoe: tmp.materialIndexCoe
-                };
+                let gljData = this.getProjectGLJNewData(tmp,projectId);
                 gljInsertData.push(gljData);
             }
             let basePrice = tmp.basePrice;
@@ -655,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;
+    }
+
     /**
      * 获取组成物具体数据
      *
@@ -718,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){
@@ -763,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];
 
     }
 
@@ -825,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);
 

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

@@ -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);
+};

+ 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);
+        }
+    })
+}

+ 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);
+        }
+    })
+}

+ 17 - 0
modules/main/facade/project_facade.js

@@ -176,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)):"";

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

@@ -17,6 +17,9 @@ 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');
 
 
@@ -45,6 +48,9 @@ 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 (){};
 

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

@@ -21,7 +21,10 @@ let projectConst = {
     LABOUR_COE:'labour_coe',
     CALC_PROGRAM:'calc_program',
     INSTALLATION_FEE:'installation_fee',
-    PROJECT_INFO: 'project_info'
+    PROJECT_INFO: 'project_info',
+    EVALUATE_LIST:'evaluate_list',
+    CONTRACTOR_LIST:'contractor_list',
+    BID_EVALUATION_LIST:'bid_evaluation_list'
 };
 
 let projectConstList = [

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

@@ -35,7 +35,6 @@ module.exports =function (app) {
                 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,

+ 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);
             },

+ 25 - 8
modules/pm/controllers/pm_controller.js

@@ -31,6 +31,7 @@ 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});
@@ -553,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;
@@ -720,8 +721,8 @@ module.exports = {
             error:0
         };
         try {
-            let data = JSON.parse(req.body.data);
-            result.data = await pm_facade.exportProject(req.session.sessionUser.id, data);
+            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;
@@ -732,7 +733,7 @@ module.exports = {
     },
     importProject:async function(req,res){
         let form = new multiparty.Form({uploadDir: './tmp'});
-        let uploadFullName;
+        let path = "";
         form.parse(req, async function (err, fields, files) {
             try {
                 console.log(fields);
@@ -740,13 +741,16 @@ module.exports = {
                 if (err || !file) {
                     throw '上传失败。';
                 };
+                path = file.path;
                 let data = fs.readFileSync(file.path,'utf-8');
-                let result = await pm_facade.importProjects(data,req,fields);
-                fs.unlinkSync(file.path);
+                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);
             }
 
         })
@@ -831,4 +835,17 @@ module.exports = {
             }
         });
     }
-};
+};
+
+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);
+}

+ 71 - 8
modules/pm/facade/pm_facade.js

@@ -72,7 +72,9 @@ let productModel = mongoose.model('product');
 let stdRationItemModel = mongoose.model('std_ration_lib_ration_items');
 let stdGljItemModel = mongoose.model('std_glj_lib_gljList');
 import BillsTemplateModel from "../models/templates/bills_template_model";
-
+let evaluateListModel = mongoose.model("evaluate_list");
+let bidListModel = mongoose.model("bid_evaluation_list");
+let contractorListModel = mongoose.model("contractor_list");
 let featureLibModel =  mongoose.model("std_project_feature_lib");
 let scMathUtil = require('../../../public/scMathUtil').getUtil();
 let counter = require('../../../public/counter/counter');
@@ -243,7 +245,10 @@ async function copyProject(userID, compilationID,data,newProjectID = null) {
         copyRationSubList(originalID,newProjectID,billMap.uuidMaping,rationMap.uuidMaping,projectGLJMap.IDMap,rationCoeModel),
         copyRationSubList(originalID,newProjectID,billMap.uuidMaping,rationMap.uuidMaping,projectGLJMap.IDMap,quantityDetailModel),
         copyRationSubList(originalID,newProjectID,billMap.uuidMaping,rationMap.uuidMaping,projectGLJMap.IDMap,rationInstallationModel),
-        copyRationSubList(originalID,newProjectID,billMap.uuidMaping,rationMap.uuidMaping,projectGLJMap.IDMap,rationTemplateModel)
+        copyRationSubList(originalID,newProjectID,billMap.uuidMaping,rationMap.uuidMaping,projectGLJMap.IDMap,rationTemplateModel),
+        copyMaterialList(originalID,newProjectID,projectGLJMap.IDMap,evaluateListModel),
+        copyMaterialList(originalID,newProjectID,projectGLJMap.IDMap,bidListModel),
+        copyMaterialList(originalID,newProjectID,projectGLJMap.IDMap,contractorListModel),
     ];
     if(originalProperty.calcProgramFile){
         copyTasks.push(commonCopy(newProjectID,originalProperty.calcProgramFile.ID,calcProgramFileID,calcProgramsModel));
@@ -466,6 +471,25 @@ async function copyRationSubList(originalPID,newProjectID,billIDMap,rationIDMap,
     await insertMany(newList,model);
 }
 
+
+async function copyMaterialList(originalPID,newProjectID,projectGLJIDMap,model) {//暂估材料等信息
+    let materialList = await model.find({projectID:originalPID}, '-_id').lean();
+    let newList = [];
+    for(let m of materialList){
+        newList.push(getNewMaterial(m,newProjectID,projectGLJIDMap))
+    }
+    await insertMany(newList,model);
+
+}
+
+function getNewMaterial(m,newProjectID,projectGLJIDMap) {
+    m.ID = uuidV1();
+    m.projectID = newProjectID;
+    if(projectGLJIDMap[m.projectGLJID]) m.projectGLJID = projectGLJIDMap[m.projectGLJID];
+    return m;
+}
+
+
 function getCopyRationSubData(doc,newProjectID,billIDMap,rationIDMap,projectGLJIDMap){
     doc.ID = uuidV1();
     doc.projectID = newProjectID;
@@ -1025,11 +1049,15 @@ async function getFullPath(projectID) {
 //获取projectIDs文件下所有子项目(不包括projectIDs本身)
 async function getPosterityProjects(projectIDs) {
     let rst = [];
-    async function getProjects(IDs) {
-        if (IDs.length > 0) {
+    async function getProjects(parentIDs) {
+        if (parentIDs.length > 0) {
             let newIDs = [];
-            let projs = await projectModel.find({ParentID: {$in: IDs}, $or: notDeleted}, '-_id');
+            let projs = await projectModel.find({ParentID: {$in: parentIDs}, $or: notDeleted}, '-_id');
             for (let proj of projs) {
+                // 兼容旧的错误数据,可能ParentID和ID相同
+                if (parentIDs.includes(proj.ID)) {
+                    continue;
+                }
                 rst.push(proj);
                 newIDs.push(proj.ID);
             }
@@ -1489,6 +1517,18 @@ async function importTenderDetail(tenderData) {
         if (tenderData.projectGLJ.length) {
             task.push(gljListModel.insertMany(tenderData.projectGLJ));
         }
+        // 承包人材料
+        if (tenderData.contractorList.length) {
+           task.push(contractorListModel.insertMany(tenderData.contractorList));
+        }
+        // 评标材料表
+        if (tenderData.bidEvaluationList.length) {
+            task.push(bidListModel.insertMany(tenderData.bidEvaluationList));
+        }
+        // 暂估价材料表
+        if (tenderData.evaluationList.length) {
+            task.push(evaluateListModel.insertMany(tenderData.evaluationList));
+        }
         //组成物
         if (tenderData.mixRatio.length) {
             task.push(mixRatioModel.insertMany(tenderData.mixRatio));
@@ -1544,6 +1584,9 @@ async function setupStdData(tenderData) {
             r.content = stdRation.jobContent;
             if (stdRation.feeType) {
                 r.programID = stdRation.feeType;
+            } else {
+                // 是标准定额,但是该标准定额取费专业为空,取定额取费专业(后台配置项)
+                r.programID = tenderData.tender.property.engineering;
             }
         } else {
             r.from = 'cpt';
@@ -1626,6 +1669,7 @@ async function exportMainData(userID,projectID) {
     let project = await projectModel.findOne({ID:projectID});
     if(!project) throw  new Error("没有找到该建设项目:"+projectID);
     result['compilationID'] = project.compilation;
+    result['from'] = "construction";
     result.projects.push(project);
     let subProjects = await  projectModel.find({"$or": [{'ParentID':projectID}, {"property.rootProjectID": projectID}]});
     for(let s of subProjects){
@@ -1683,6 +1727,9 @@ async function exportTenderData(data){
     result.rationTemplates = await rationTemplateModel.find({projectID:data.projectID});
     result.calcProgramsFile = await calcProgramsModel.findOne({projectID:data.projectID});
     result.labourCoes = await labourCoesModel.findOne({projectID:data.projectID});
+    result.evaluateList = await evaluateListModel.find({projectID:data.projectID}, '-_id').lean();
+    result.bidList = await bidListModel.find({projectID:data.projectID}, '-_id').lean();
+    result.contractorList = await contractorListModel.find({projectID:data.projectID}, '-_id').lean();
 
     return cipher.aesEncrypt(JSON.stringify(result));
 }
@@ -1696,7 +1743,10 @@ async function importProjects(data,req,fields) {
     }
     let mainData = datas.length > 0 ?datas[0]:null;
     if(mainData){
-        if(mainData.compilationID != req.session.sessionCompilation._id){
+        if(mainData.from && mainData.from != "construction"){
+            result.error = 1;
+            result.msg = "导入失败:您要导入的文件是由“纵横公路养护云版”导出,当前软件是“大司空云计价”,请选择正确的软件再进行操作!";
+        }else if(mainData.compilationID != req.session.sessionCompilation._id){
             result.error = 1;
             result.msg = "编办不同,无法导入,请重新选择!";
         }else {
@@ -1714,7 +1764,7 @@ async function importProjects(data,req,fields) {
 
 
 async function handleEachProject(data,projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap){
-    let bills = [],rations = [],projectGLJs = [],rationGLJs=[],rationCoes=[],quantityDetails=[],rationInstallations=[],rationTemplates=[];
+    let bills = [],rations = [],projectGLJs = [],rationGLJs=[],rationCoes=[],quantityDetails=[],rationInstallations=[],rationTemplates=[],evaluateList=[],bidList=[],contractorList=[];
     let newProjectSetting =null,newCalcProgramsFile = null,newLabourCoe = null;
     let billsIDMap = {},projectGLJIDMap={},rationIDMap = {};
     let newProjectID = projectIDMap[data.projSetting.projectID];
@@ -1762,6 +1812,9 @@ async function handleEachProject(data,projectIDMap,labourCoeFileIDMap,calcProgra
     if(data.quantityDetails && data.quantityDetails.length > 0) quantityDetails = setRationSubList(data.quantityDetails,newProjectID,billsIDMap,rationIDMap,projectGLJIDMap);
     if(data.rationInstallations && data.rationInstallations.length > 0) rationInstallations = setRationSubList(data.rationInstallations,newProjectID,billsIDMap,rationIDMap,projectGLJIDMap);
     if(data.rationTemplates && data.rationTemplates.length > 0) rationTemplates = setRationSubList(data.rationTemplates,newProjectID,billsIDMap,rationIDMap,projectGLJIDMap);
+    if(data.evaluateList && data.evaluateList.length > 0) evaluateList = setMaterialList(data.evaluateList,newProjectID,projectGLJIDMap);
+    if(data.bidList && data.bidList.length > 0) bidList = setMaterialList(data.bidList,newProjectID,projectGLJIDMap);
+    if(data.contractorList && data.contractorList.length > 0) contractorList = setMaterialList(data.contractorList,newProjectID,projectGLJIDMap);
 
     //生成projectSetting 文件
     if(data.projSetting){
@@ -1794,12 +1847,21 @@ async function handleEachProject(data,projectIDMap,labourCoeFileIDMap,calcProgra
     if(quantityDetails.length > 0) await insertMany(quantityDetails,quantityDetailModel);
     if(rationInstallations.length > 0) await insertMany(rationInstallations,rationInstallationModel);
     if(rationTemplates.length > 0) await insertMany(rationTemplates,rationTemplateModel);
+    if(evaluateList.length > 0) await insertMany(evaluateList,evaluateListModel);
+    if(bidList.length > 0) await insertMany(bidList,bidListModel);
+    if(contractorList.length > 0) await insertMany(contractorList,contractorListModel);
     if(newCalcProgramsFile) await calcProgramsModel.create(newCalcProgramsFile);
     if(newLabourCoe) await labourCoesModel.create(newLabourCoe);
 
 
 }
-
+function setMaterialList(datas,newProjectID,projectGLJIDMap){
+    let arrs = [];
+    for(let d of datas){
+       arrs.push(getNewMaterial(d,newProjectID,projectGLJIDMap))
+    }
+    return arrs
+}
 
 function setRationSubList(datas,newProjectID,billIDMap,rationIDMap,projectGLJIDMap) {
     let arrs = [];
@@ -1815,6 +1877,7 @@ async function handleMainProjectDatas(mainData,updateData,userID) {
     let mainProjectID = -1;
     let projectIDMap = {},feeRateFileIDMap={},unitPriceFileIDMap={},labourCoeFileIDMap={},calcProgramFileIDMap={};
     let tasks = [];
+    projectIDMap[-1] = -1;//最后一个项目的nextID为-1的情况
     //生成新的projectID
     for(let p of mainData.projects){
         let newProjectID = await getCounterID("projects");

+ 9 - 1
modules/templates/models/bills_template.js

@@ -8,6 +8,7 @@ let BillsTemplates = mongoose.model('temp_bills');
 let BillsTemplateDAO = function(){};
 import BillsTemplateModel from "../../pm/models/templates/bills_template_model";
 const uuidV1 = require('uuid/v1');
+const billsUtil = require('../../../public/billsUtil');
 
 BillsTemplateDAO.prototype.getTemplate = function (type, callback) {
     if (callback) {
@@ -82,7 +83,9 @@ BillsTemplateDAO.prototype.getNeedfulTemplate = async function (templateLibID) {
     }
     //过滤掉不含清单固定类别的模板数据 (导入接口只包含必要的清单模板数据)
     let needfulDatas = billsDatas.filter(data => getFlag(data));
-    sortToTreeData(needfulDatas);
+    sortSeqToNextSibling(needfulDatas);
+    //sortToTreeData(needfulDatas);
+    billsUtil.resetTreeData(needfulDatas, uuidV1);
     return needfulDatas;
 };
 
@@ -152,10 +155,15 @@ function sortToTreeData(needfulData) {
     for(let bill of needfulData){
         uuidMaping[bill.ID] = uuidV1();
     }
+    const reg = /@\d+/;
     needfulData.forEach(function (template) {
         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);
+        }
     });
 }
 

+ 2 - 2
modules/users/models/user_model.js

@@ -322,14 +322,14 @@ class UserModel extends BaseModel {
      * @return {version}
      */
     async getVersionFromUpgrade(ssoId, compilationId){
-        let version = '纵横建筑计价(免费云版)';
+        let version = '大司空云计价(免费公用版)';
         let userData = await this.findDataBySsoId(ssoId);
         if (userData.upgrade_list !== undefined) {
             let compilationInfo = userData.upgrade_list.find(function (item) {
                 return item.compilationID === compilationId;
             });
             if (compilationInfo !== undefined && compilationInfo.isUpgrade === true) {
-                version = '纵横建筑计价(专业云版)';
+                version = '大司空云计价(专业版)';
             }
         }
         return version;

+ 1 - 0
package.json

@@ -51,6 +51,7 @@
     "node-schedule": "^1.3.0",
     "node-xlsx": "^0.11.2",
     "pdfkit": "^0.8.2",
+    "request-promise": "^4.2.5",
     "socket.io": "^2.0.3",
     "ua-parser-js": "^0.7.14",
     "uuid": "^3.1.0",

+ 60 - 0
public/billsUtil.js

@@ -0,0 +1,60 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/11/1
+ * @version
+ */
+
+((factory) => {
+    if (typeof module !== 'undefined') {
+        module.exports = factory();
+    } else {
+        window.BILLS_UTIL = factory();
+    }
+})(() => {
+    // 清单模板各清单重设ID时,重新转换清单基数的ID引用
+    function parseCalcBase(calcBase, uuidMapping) {
+        const orgIDRefs = [...new Set(calcBase.match(/@\d+/g))];
+        orgIDRefs.forEach(orgRef => {
+            const orgID = orgRef.match(/\d+/)[0];
+            const newID = uuidMapping[orgID] || null;
+            // ID匹配不上则不转换这个引用
+            if (!newID) {
+                return;
+            }
+            const replaceStr = `@${newID}`;
+            calcBase = calcBase.replace(new RegExp(`${orgRef}\\b`, 'g'), replaceStr);
+        });
+        return calcBase;
+    }
+
+    /*
+     * @param {Array}billsList (完整的清单树结构数据)
+     * @param {Function}idFactory 生成ID的方法
+     * @return {void}
+     * */
+    function resetTreeData(billsList, idFactory) {
+        const idMapping = {};
+        idMapping['-1'] = -1;
+        // 建立新ID-旧ID映射
+        billsList.forEach(bills => idMapping[bills.ID] = idFactory());
+        const reg = /@\d+/;
+        billsList.forEach(function (bills) {
+            bills.ID = idMapping[bills.ID] ? idMapping[bills.ID] : -1;
+            bills.ParentID = idMapping[bills.ParentID] ? idMapping[bills.ParentID] : -1;
+            bills.NextSiblingID = idMapping[bills.NextSiblingID] ? idMapping[bills.NextSiblingID] : -1;
+            const needToParseCalcBase = bills.calcBase && reg.test(bills.calcBase);
+            if (needToParseCalcBase) {
+                bills.calcBase = parseCalcBase(bills.calcBase, idMapping);
+            }
+        });
+    }
+
+    return {
+        parseCalcBase,
+        resetTreeData
+    };
+});

+ 41 - 0
public/common_util.js

@@ -0,0 +1,41 @@
+/**
+ * Created by CSL on 2017-06-06.
+ * public functions.
+ */
+
+function deleteEmptyObject(arr) {
+    function isEmptyObject(e) {
+        var t;
+        for (t in e)
+            return !1;
+        return !0
+    };
+
+    for (var i = 0; i < arr.length; i++) {
+        if (isEmptyObject(arr[i])) {
+            arr.splice(i, 1);
+            i = i - 1;
+        };
+    };
+};
+
+((factory) => {
+    if (typeof module !== 'undefined') {
+        module.exports = factory();
+    } else {
+        window.commonUtil = factory();
+    }
+})(() => {
+    function isDef(val) {
+        return typeof val !== 'undefined' && val !== null;
+    }
+
+    function isEmptyVal(val) {
+        return val === null || val === undefined || val === '';
+    }
+
+    return {
+        isDef,
+        isEmptyVal
+    };
+});

+ 20 - 3
public/counter/counter.js

@@ -36,14 +36,21 @@ var counterDAO = function(){};
  *     result.value.sequence_value Ϊ�޸ĺ��id
  * }
  */
-counterDAO.prototype.getIDAfterCount = function(moduleName, stepCount, callback) {
+counterDAO.prototype.getIDAfterCount =async function(moduleName, stepCount, callback) {
     var sc = stepCount;
     if (isNaN(stepCount) || (stepCount < 0)) {
         sc = 1;
     } else if (!(/^-?\d+$/.test(stepCount))) {
         sc = Math.round(stepCount + 0.5);
     }
-    counterModel.findOneAndUpdate({_id: moduleName}, { $inc: { sequence_value: sc } }, {'new':true}, callback);//findOneAndUpdate
+    if (callback === null) {
+        let result = await counterModel.findOneAndUpdate({_id: moduleName}, { $inc: { sequence_value: sc } }, {'new':true});
+        return await checkIDResult(result,moduleName,null)
+    } else {
+        counterModel.findOneAndUpdate({_id: moduleName}, { $inc: { sequence_value: sc } }, {'new':true}, function (err,result) {
+            checkIDResult(result,moduleName,callback,err)
+        });
+    }
 }
 
 counterDAO.prototype.getIDAfterCountSync = async function (moduleName, stepCount) {
@@ -53,9 +60,19 @@ counterDAO.prototype.getIDAfterCountSync = async function (moduleName, stepCount
     } else if (!(/^-?\d+$/.test(stepCount))) {
         sc = Math.round(stepCount + 0.5);
     }
-    return await counterModel.findOneAndUpdate({_id: moduleName}, { $inc: { sequence_value: sc } }, {'new':true});//findOneAndUpdate
+    let result = await counterModel.findOneAndUpdate({_id: moduleName}, { $inc: { sequence_value: sc } }, {'new':true});
+    return await checkIDResult(result,moduleName,null)
 };
 
+async function checkIDResult(result,moduleName,callback,err){
+    if(result){
+        return callback?callback(err,result):result;
+    }else {
+        return callback?counterModel.create({_id: moduleName,sequence_value:1},callback): await counterModel.create({_id: moduleName,sequence_value:1});
+    }
+
+}
+
 counterDAO.prototype.getCurrentID = function(moduleName, callback) {
     if (callback) {
         counterModel.findOne({_id: moduleName}).exec()

+ 14 - 33
public/web/PerfectLoad.js

@@ -130,50 +130,31 @@ jQuery.bootstrapLoading = {
 
 const SCComponent = (() => {
     /*
-    * 假滚动条,0 - 80% 快, 80% - 95%很慢,95%开始停住,直到调用end方法
+    * 假滚动条,调用end方法后进度直接跳到100%
+    * 使用利用合成线程的css动画,不会被js执行阻塞。
     * */
     const InitProgressBar = (() => {
-        function ProgressBar($modal = $('#progress'), $title = $('#progress-title'),
-                             $content = $('#progress-content'), $bar = $('#progressBar')) {
-            this.$modal = $modal;
-            this.$title = $title;
-            this.$content = $content;
-            this.$bar = $bar;
+        function ProgressBar() {
+            this.$modal = $('#progress');
+            this.$title = $('#progress-title');
+            this.$title.text('导出');
+            this.$content = $('#progress-content');
+            this.$content.text('正在导出...');
+            this.$barCover = $('#progressCover');
+            this.animationName = 'progress-cover-move';
         }
         //显示滚动条,自动处理滚动条速度
         ProgressBar.prototype.start = function (title, content) {
             this.$title.text(title);
             this.$content.text(content);
-            this.$bar.css('width', `0%`);
+            this.$barCover.css('transform', 'translate(0)');
+            this.$barCover.addClass(this.animationName);
             this.$modal.modal('show');
-            this.outer = setInterval(() => {
-                let curWidth = parseInt(this.$bar[0].style.width.replace('%', ''));
-                curWidth = parseInt(curWidth + 2);
-                this.$bar.css('width', `${curWidth}%`);
-                if (curWidth >= 80) {
-                    clearInterval(this.outer);
-                }
-            }, 100);
-            this.inner = setInterval(() => {
-                let curWidth = parseFloat(this.$bar[0].style.width.replace('%', ''));
-                if (curWidth >= 80) {
-                    curWidth = parseFloat(curWidth + 0.1);
-                    this.$bar.css('width', `${curWidth}%`);
-                    if (curWidth >= 95) {
-                        clearInterval(this.inner);
-                    }
-                }
-            }, 500);
         };
         //结束显示滚动条,滚动条从当前位置滚到100% 消失
         ProgressBar.prototype.end = function () {
-            if (this.outer) {
-                clearInterval(this.outer);
-            }
-            if (this.inner) {
-                clearInterval(this.inner);
-            }
-            $('#progressBar').css('width', '100%');
+            this.$barCover.removeClass(this.animationName);
+            this.$barCover.css('transform', 'translate(100%)');
             setTimeout(() => {
                 this.$modal.modal('hide');
             }, 500);

+ 28 - 21
public/web/gljUtil.js

@@ -27,6 +27,7 @@ let gljUtil = {
             pglj[sField] = result.subdivisionQuantity;
             pglj[tField] = result.techQuantity;
             pglj[qField] = result.quantity;
+            pglj.tenderQuantity = result.tenderQuantity;
             quantityMap[pg_index] = pglj;
         }
         //计算做为组成物的消耗量
@@ -38,9 +39,11 @@ let gljUtil = {
                 let p_glj = quantityMap[pkey];
                 if(m_glj&&p_glj){
                     let quantity = scMathUtil.roundForObj(parseFloat(p_glj[qField])*parseFloat(m.consumption),q_decimal);
+                    let tenderQuantity = scMathUtil.roundForObj(parseFloat(p_glj.tenderQuantity)*parseFloat(m.consumption),q_decimal);
                     let techQuantity = scMathUtil.roundForObj(parseFloat(p_glj[tField])*parseFloat(m.consumption),q_decimal);
                     let subdivisionQuantity = scMathUtil.roundForObj(parseFloat(p_glj[sField])*parseFloat(m.consumption),q_decimal);
                     m_glj[qField] =  scMathUtil.roundForObj(parseFloat(m_glj[qField])+quantity,q_decimal);
+                    m_glj.tenderQuantity =  scMathUtil.roundForObj(parseFloat(m_glj.tenderQuantity)+tenderQuantity,q_decimal);
                     m_glj[tField] =  scMathUtil.roundForObj(parseFloat(m_glj[tField])+techQuantity,q_decimal);
                     m_glj[sField] =  scMathUtil.roundForObj(parseFloat(m_glj[sField])+subdivisionQuantity,q_decimal);
                 }
@@ -52,19 +55,24 @@ let gljUtil = {
         let quantity_sum=0;//工料机汇总消耗量
         let sum = 0;//分部分项总消耗量
         let tech_sum = 0;//技术措施总消耗量
+        let tender_qantity_sum = 0;
         for(let rg of ration_glj_list){
             let tem_ration = rationMap[rg.rationID];
             let r_quantity = tem_ration?scMathUtil.roundForObj(tem_ration.quantity,q_decimal):0;
             let glj_quantity = scMathUtil.roundForObj(rg.quantity, q_decimal);
+            let tender_r_quantity = r_quantity;
+            let tender_glj_quantity = glj_quantity;
             if(!r_quantity){
                 continue;
             }
-            if(isTender == true){
-                glj_quantity = this.getRationGLJTenderQuantity(rg,tem_ration,q_decimal,scMathUtil);
-                r_quantity = this.getRationTenderQuantity(tem_ration,q_decimal,scMathUtil);
+            if(!pglj.is_adjust_price){
+                tender_glj_quantity = this.getRationGLJTenderQuantity(rg,tem_ration,q_decimal,scMathUtil);
+                tender_r_quantity = this.getRationTenderQuantity(tem_ration,q_decimal,scMathUtil);
             }
             let total = scMathUtil.roundForObj(glj_quantity*r_quantity, q_decimal);
+            let tender_total = scMathUtil.roundForObj(tender_glj_quantity*tender_r_quantity, q_decimal);
             quantity_sum = scMathUtil.roundForObj(quantity_sum+total,q_decimal);
+            tender_qantity_sum = scMathUtil.roundForObj(tender_qantity_sum+tender_total,q_decimal);
             if(_.includes(billIDs,rg.billsItemID)){//计算分部分项
                 sum = scMathUtil.roundForObj(sum+total,q_decimal);
             }
@@ -72,7 +80,8 @@ let gljUtil = {
                 tech_sum = scMathUtil.roundForObj(tech_sum+total,q_decimal);
             }
         }
-        for(let ra of rations){//计算定额类型工料机的消耗量
+        //2019-11-07 定额类型工料机已经屏蔽,如果打开,记得加上调价后的消耗量
+        /*for(let ra of rations){//计算定额类型工料机的消耗量
             if(ra.type == this.rationType.gljRation&&ra.projectGLJID===pglj.id){
                 let r_quantity = scMathUtil.roundForObj(ra.quantity,q_decimal);
                 r_quantity = r_quantity?r_quantity:0;
@@ -87,11 +96,11 @@ let gljUtil = {
                     tech_sum = scMathUtil.roundForObj(tech_sum+r_quantity,q_decimal);
                 }
             }
-
-        }
+        }*/
         result.subdivisionQuantity = sum;
         result.techQuantity = tech_sum;
         result.quantity = quantity_sum;
+        result.tenderQuantity = tender_qantity_sum;
         return result;
     },
     getRationGLJTenderQuantity:function (ration_glj,ration,q_decimal,scMathUtil) {
@@ -109,7 +118,8 @@ let gljUtil = {
               coeField = coeMap[key];
             }
         }
-        let coe = ration.quantityCoe&&this.isNotEmpty(ration.quantityCoe[coeField])?ration.quantityCoe[coeField]:1;
+        let coe = 1;
+        coe = ration.quantityCoe&&this.isNotEmpty(ration.quantityCoe[coeField])?ration.quantityCoe[coeField]:1;
         coe = parseFloat(coe);
         let glj_quantity = scMathUtil.roundForObj(ration_glj.quantity, q_decimal);
         return scMathUtil.roundForObj(glj_quantity * coe,q_decimal);
@@ -164,9 +174,10 @@ let gljUtil = {
     getFlag:function (b) {
         return _.find(b.flags,{"fieldName":"fixed"});
     },
-    getGLJPrice:function (glj,projectGLJDatas,calcOptions,labourCoeDatas,decimalObj,isRadio,_,scMathUtil) {
+    getGLJPrice:function (glj,projectGLJDatas,calcOptions,labourCoeDatas,decimalObj,isRadio,_,scMathUtil,tenderCoe) {
         let result = {};
         result.marketPrice = this.getMarketPrice(glj,projectGLJDatas,calcOptions,decimalObj,isRadio,_,scMathUtil);
+        result.tenderPrice = this.getMarketPrice(glj,projectGLJDatas,calcOptions,decimalObj,isRadio,_,scMathUtil,tenderCoe);
         if(this.calcPriceDiff(glj,calcOptions)==true){//计取价差
             result.basePrice = this.getBasePrice(glj,projectGLJDatas,calcOptions,labourCoeDatas,decimalObj,isRadio,_,scMathUtil);
             result.adjustPrice = this.getAdjustPrice(glj,projectGLJDatas,calcOptions,labourCoeDatas,decimalObj,isRadio,_,scMathUtil);
@@ -185,12 +196,9 @@ let gljUtil = {
         if (this.notEditType.indexOf(glj.unit_price.type)!=-1&&glj.ratio_data.length>0) {//对于混凝土、配合比、砂浆、机械台班等有组成物的材料,价格需根据组成物计算得出。
             let p =0;
             for(let ratio of glj.ratio_data){
-                let tem =  _.find(projectGLJDatas.gljList,{
-                    'code': ratio.code,
-                    'name': ratio.name,
-                    'specs':ratio.specs,
-                    'type': ratio.type,
-                    'unit': ratio.unit
+                let rIndex = gljUtil.getIndex(ratio);
+                let tem =  _.find(projectGLJDatas.gljList,function(item){
+                    return rIndex == gljUtil.getIndex(item)
                 });
                 if(tem){
                     let tem_marketPrice = this.getMarketPrice(tem,projectGLJDatas,calcOptions,decimalObj,true,_,scMathUtil);  //let priceData=this.getGLJPrice(tem,projectGLJDatas,calcOptions,labourCoeDatas,decimalObj,true,_,scMathUtil);
@@ -250,13 +258,10 @@ let gljUtil = {
         } else if (this.notEditType.indexOf(glj.unit_price.type)!=-1&&glj.ratio_data.length>0) {//对于混凝土、配合比、砂浆、机械台班,调整价根据组成物计算得出。
             let p =0;
             for(let ratio of glj.ratio_data){
-                let tem =  _.find( projectGLJDatas.gljList,{
-                    'code': ratio.code,
-                    'name': ratio.name,
-                    'specs':ratio.specs,
-                    'type': ratio.type,
-                    'unit': ratio.unit
-                })
+                let rIndex = gljUtil.getIndex(ratio);
+                let tem =  _.find(projectGLJDatas.gljList,function(item){
+                    return rIndex == gljUtil.getIndex(item);
+                });
                 if(tem){
                     let priceData=this.getGLJPrice(tem,projectGLJDatas,calcOptions,labourCoeDatas,decimalObj,true,_,scMathUtil);
                     let temP = scMathUtil.roundForObj(priceData.adjustPrice*scMathUtil.roundForObj(ratio.consumption,quantity_decimal),process_decimal);
@@ -289,6 +294,7 @@ let gljUtil = {
     getIndex(obj, pops){
         let t_index = '';
         let k_arr = [];
+        if(!pops) pops = this.gljKeyArray;
         for (let p of pops) {
             let tmpK = (obj[p] == undefined || obj[p] == null || obj[p] == '') ? 'null' : obj[p];
             k_arr.push(tmpK);
@@ -593,6 +599,7 @@ let gljUtil = {
     extraType:[6,7,8],//一些其它的工料机类型
     notEditType : [202,203,204,301,304,4],
     gljKeyArray : ['code','name','specs','unit','type'],
+    materialKeyArray:['code','name','specs','unit'],
     rationType : {
         ration: 1,
         volumePrice: 2,

+ 3 - 3
public/web/headerOpr.js

@@ -1,4 +1,4 @@
-'use strict';
+                                                                                                                                                                                                                                      'use strict';
 
 /**
  *
@@ -37,7 +37,7 @@ const CommonHeader = (function () {
                             '<h6 class="card-subtitle mb-2 text-muted">' + staff.category + '</h6> ' +
                             '</div> ' +
                             '<ul class="list-group list-group-flush"> ' +
-                            '<li class="list-group-item" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="腾讯QQ"><i class="fa fa-qq"></i> ' + staff.qq + '</li> ' +
+                            '<li class="list-group-item" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="腾讯QQ"><a target="_blank" href="http://wpa.qq.com/msgrd?v=3&uin=' + staff.qq + '&site=qq&menu=yes"><img border="0" src="http://wpa.qq.com/pa?p=2:' + staff.qq + ':51" alt="点击这里给我发消息" title="点击这里给我发消息"/></a></li> ' +
                             '<li class="list-group-item" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="手机号码"><i class="fa fa-tablet"></i> ' + staff.telephone + '</li> ' +
                             '<li class="list-group-item" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="固定电话"><i class="fa fa-phone"></i> ' + staff.phone + '</li> ' +
                             '</ul> </div> </div>';
@@ -83,4 +83,4 @@ CommonHeader.banNavigatorContextMenu();
 
 $(document).ready(function(){
     CommonHeader.addEventListener();
-});
+});

+ 5 - 3
public/web/sheet/sheet_common.js

@@ -23,8 +23,8 @@ var sheetCommonObj = {
         var spreadNS = GC.Spread.Sheets;
         sheet.suspendPaint();
         sheet.suspendEvent();
-        if(setting.frozenCols)  sheet.frozenColumnCount(setting.frozenCols);//冻结列
-        sheet.setRowCount(1, spreadNS.SheetArea.colHeader);
+        if(setting.frozenCols)  sheet.frozenColumnCount(setting.frozenCols);//冻结列s
+        sheet.setRowCount(setting.headRows ? setting.headRows : 1, spreadNS.SheetArea.colHeader);
         sheet.setColumnCount(setting.header.length, spreadNS.SheetArea.viewport);
 
         if (setting && setting.view && setting.view.colHeaderHeight) {
@@ -136,13 +136,15 @@ var sheetCommonObj = {
                 if(data[row].cellType === 'comboBox'){
                     let options = data[row].options ? data[row].options.split("@") : [];
                     this.setComboBox(row,col,sheet,options);
+                }else if(data[row].cellType === 'String'){//默认设置字符输入,避免出现输入10:01变成日期的情况
+                    sheet.setFormatter(row, col,"@", GC.Spread.Sheets.SheetArea.viewport);
                 }
                 let val = data[row][setting.header[col].dataCode];
                 if(val&&setting.header[col].dataType === "Number"){
                     if(setting.header[col].hasOwnProperty('decimalField')){
                         let decimal = getDecimal(setting.header[col].decimalField);
                         val =scMathUtil.roundToString(val,decimal);
-                        sheet.setFormatter(-1, col,getFormatter(decimal), GC.Spread.Sheets.SheetArea.viewport);
+                        sheet.setFormatter(row, col,getFormatter(decimal), GC.Spread.Sheets.SheetArea.viewport);
                     }else {
                         val =scMathUtil.roundToString(val,2);
                     }

+ 4 - 2
public/web/slideResize.js

@@ -167,7 +167,8 @@ const SlideResize = (function() {
                 //设置下部分div高度
                 eleObj.bottom.height(bottomChange);
                 //设置下部分div内spread高度
-                eleObj.bottomSpread.height(bottomChange - limit.notBottomSpread);
+                let notBottomHeight = limit.bottomNav?eval(limit.bottomNav):limit.notBottomSpread;
+                eleObj.bottomSpread.height(bottomChange - notBottomHeight);
                 mouseMoveCount += Math.abs(moveSize);
                 deBounce(function () {
                     if (callback) {
@@ -221,7 +222,8 @@ const SlideResize = (function() {
         eleObj.top.height(curTopHeight);
         eleObj.topSpread.height(curTopHeight - limit.notTopSpread);
         eleObj.bottom.height(curBottomHeight);
-        eleObj.bottomSpread.height(curBottomHeight - limit.notBottomSpread);
+        let notBottomHeight = limit.bottomNav?eval(limit.bottomNav):limit.notBottomSpread;
+        eleObj.bottomSpread.height(curBottomHeight - notBottomHeight);
         if (callback) {
             callback();
         }

+ 1 - 1
server.js

@@ -95,7 +95,7 @@ app.use(async function (req, res, next) {
 
 //加载路由文件
 fileUtils.getGlobbedFiles('./modules/**/routes/*.js').forEach(function(modelPath) {
-    require(path.resolve(modelPath))(app);
+    if(modelPath.indexOf("import/routes")==-1) require(path.resolve(modelPath))(app);//排除掉导出导入服务
 });
 
 //app.use(express.static(_rootDir+"/web"));

+ 1 - 1
web/building_saas/complementary_glj_lib/js/glj.js

@@ -582,7 +582,7 @@ let repositoryGljObj = {
         // 输入编号、名称、规格时,如果输入回车符或粘贴回车符,提交时应转换为空格。
         let deESCFields = ['code', 'name', 'specs'];
         if(deESCFields.includes(me.setting.header[args.col]['dataCode'])){
-            args.editingText = me.isDef(args.editingText) ? args.editingText.toString().replace(/[\r, \n]/g, ' ') : '';
+            args.editingText = me.isDef(args.editingText) ? args.editingText.toString().replace(/[\r\n]/g, ' ') : '';
             args.sheet.setValue(args.row, args.col, args.editingText);
         }
         let rObj = sheetOpr.combineRowData(me.workBook.getSheet(0), me.setting, args.row, me),

+ 3 - 61
web/building_saas/complementary_ration_lib/html/dinge.html

@@ -52,7 +52,8 @@
         }
     </style>
     <script>
-        let gljLibId = '<%= gljLibId %>';
+        const gljLibId = '<%= gljLibId %>';
+        const userID = '<%=userID %>';
     </script>
 </head>
 <body>
@@ -630,67 +631,8 @@
 <script type="text/javascript" src="/web/building_saas/complementary_ration_lib/js/ration_installation.js"></script>
 <script src="/public/web/slideResize.js"></script>
 <script src="/web/building_saas/complementary_ration_lib/js/coe.js"></script>
+<script src="/web/building_saas/complementary_ration_lib/js/init.js"></script>
 <!--endinject-->
-<script type="text/javascript">
-    let userID = '<%=userID %>';
-    $(document).ready(function(){
-        rationOprObj.buildSheet($("#rationItemsSheet")[0]);
-
-        // tabPanel 下有多个Spread时,相互之间不能正确显示。改成一个Spread下多个Sheet。
-        var rdSpread = sheetCommonObj.createSpread($("#rdSpread")[0], 4);
-        rdSpread.options.allowUserDragFill = false;
-        rdSpread.options.allowUserDragDrop = false;
-        sheetCommonObj.spreadDefaultStyle(rdSpread);
-        rationGLJOprObj.buildSheet(rdSpread.getSheet(0));
-
-        rationAssistOprObj.buildSheet(rdSpread.getSheet(1));
-
-        rationCoeOprObj.buildSheet(rdSpread.getSheet(2));
-
-        rationInstObj.buildSheet(rdSpread.getSheet(3));
-        rationInstObj.getInstallation(parseInt(getQueryString("repository")), function () {
-            //rationInstObj.buildSheet(rdSpread.getSheet(3));
-        });
-        let rdSpreadEscSheets = [];
-        rdSpreadEscSheets.push({sheet: rdSpread.getSheet(0), editStarting: rationGLJOprObj.onEditStarting, editEnded: rationGLJOprObj.onCellEditEnd});
-        rdSpreadEscSheets.push({sheet: rdSpread.getSheet(1), editStarting: rationAssistOprObj.onEditStarting, editEnded: rationAssistOprObj.onEditEnded});
-        rdSpreadEscSheets.push({sheet: rdSpread.getSheet(2), editStarting: rationCoeOprObj.onEditStarting, editEnded: rationCoeOprObj.onEditEnded});
-        rdSpreadEscSheets.push({sheet: rdSpread.getSheet(3), editStarting: rationInstObj.onEditStarting, editEnded: rationInstObj.onEditEnded});
-        sheetCommonObj.bindEscKey(rdSpread, rdSpreadEscSheets);
-        pageOprObj.initPage();
-
-        $("#linkGLJ").click(function(){
-            rationGLJOprObj.bindRationGljDelOpr();
-            rdSpread.setActiveSheetIndex(0);
-        });
-
-        $("#linkFZDE").click(function(){
-            rationAssistOprObj.bindRationAssDel();
-            rdSpread.setActiveSheetIndex(1);
-        });
-
-        $("#linkFZTJ").click(function(){
-            rationCoeOprObj.bindRationCoeDel();
-            rdSpread.setActiveSheetIndex(2);
-        });
-
-        $("#linkAZZJ").click(function(){
-            rationInstObj.bindRationInstDel();
-            rdSpread.setActiveSheetIndex(3);
-        });
-        //解决spreadjs sheet初始化没高度宽度
-        $('#modalCon').width($(window).width()*0.5);
-        $('#gljSelTreeDiv').height($(window).height() - 300);
-        $("#gljSelSheet").height($("#gljSelTreeDiv").height()-21.6);
-        $("#gljSelSheet").width($('#modalCon').width() * 0.63);
-        $(window).resize(function () {
-            $('#modalCon').width($(window).width()*0.5);
-            $('#gljSelTreeDiv').height($(window).height() - 300);
-            $("#gljSelSheet").height($("#gljSelTreeDiv").height()-21.6);
-            $("#gljSelSheet").width($('#modalCon').width()* 0.63);
-        });
-    });
-</script>
 </body>
 <script type="text/javascript">
     autoFlashHeight();

+ 24 - 37
web/building_saas/complementary_ration_lib/js/gljSelect.js

@@ -39,28 +39,22 @@ let gljSelOprObj = {
             delete glj.ID;
         }
     },
-    getSelGljItems: function(stdGljLibId, callback) {
-        let me = this;
-        CommonAjax.post('/complementartGlj/api/getGljItems', {stdGljLibId: stdGljLibId}, function (rstData) {
-            me.stdGljList = rstData.stdGljs;
-            //兼容多单价,计算补充定额价格时,套多单价人材机的时候,默认取第一个价格
-            for(let sGlj of me.stdGljList){
-                if(sGlj.priceProperty && typeof sGlj.priceProperty.price1 !== 'undefined'){
-                    sGlj.basePrice = sGlj.priceProperty.price1;
-                }
-            }
-            me.complementaryGljList = rstData.complementaryGljs;
-            me.switchToGljId(me.stdGljList);
-            me.switchToGljId(me.complementaryGljList);
-            me.setProp('type', 'std', me.stdGljList);
-            me.setProp('type', 'complementary', me.complementaryGljList);
-            me.sortGlj(me.stdGljList);
-            me.sortGlj(me.complementaryGljList);
-            gljAdjOprObj.gljList = me.stdGljList.concat(me.complementaryGljList);
-            if(callback){
-                callback();
+    getSelGljItems: function(gljData) {
+        this.stdGljList = gljData.stdGljs;
+        //兼容多单价,计算补充定额价格时,套多单价人材机的时候,默认取第一个价格
+        for(let sGlj of this.stdGljList){
+            if(sGlj.priceProperty && typeof sGlj.priceProperty.price1 !== 'undefined'){
+                sGlj.basePrice = sGlj.priceProperty.price1;
             }
-        });
+        }
+        this.complementaryGljList = gljData.complementaryGljs;
+        this.switchToGljId(this.stdGljList);
+        this.switchToGljId(this.complementaryGljList);
+        this.setProp('type', 'std', this.stdGljList);
+        this.setProp('type', 'complementary', this.complementaryGljList);
+        this.sortGlj(this.stdGljList);
+        this.sortGlj(this.complementaryGljList);
+        gljAdjOprObj.gljList = this.stdGljList.concat(this.complementaryGljList);
     },
     initClassTree: function (type, treeData) {
         let me = this;
@@ -81,22 +75,10 @@ let gljSelOprObj = {
             }
         }
     },
-    getGljClassTree: function (gljLibId, callback) {
-        let me = this;
-        let url = '/complementartGlj/api/getMixedTree';
-        let postData = {gljLibId: gljLibId};
-        let sucFunc = function (rstData) {
-            me.treeData = rstData;
-            me.initClassTree('std', me.treeData.std);
-            gljSelOprObj.buildSheet($('#gljSelSheet')[0]);
-            if(callback){
-                callback();
-            }
-        };
-        let errFunc = function () {
-
-        };
-        CommonAjax.post(url, postData, sucFunc, errFunc);
+    getGljClassTree: function (mixedTreeData) {
+        this.treeData = mixedTreeData;
+        this.initClassTree('std', this.treeData.std);
+        gljSelOprObj.buildSheet($('#gljSelSheet')[0]);
     },
     buildSheet: function (container) {
         let me = gljSelOprObj;
@@ -359,4 +341,9 @@ $(document).ready(function () {
             return false;
         }
     });
+    $('#selGlj').on('shown.bs.modal', function () {
+        if (gljSelOprObj.workBook) {
+            gljSelOprObj.workBook.refresh();
+        }
+    });
 });

+ 79 - 0
web/building_saas/complementary_ration_lib/js/init.js

@@ -0,0 +1,79 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author CaiAoLin
+ * @date 2019/9/24
+ * @version
+ */
+const initialization = (() => {
+    $(document).ready(function () {
+        $.bootstrapLoading.start();
+        CommonAjax.get('/complementaryRation/api/initData', {}, res => {
+            if (res.error) {
+                alert('数据初始化失败,请重试。');
+                setTimeout(() => {
+                    window.location.href = '/'
+                }, 1200);
+            } else {
+                const data = res.data;
+                pageOprObj.rationTreeData = data.rationTreeData;
+                pageOprObj.mixedTreeData = data.mixedTreeData;
+                pageOprObj.mixedGLJData = data.mixedGLJData;
+                rationGLJOprObj.distTypeTree = data.gljDistTypeCache;
+                rationOprObj.rationsCodes = data.rationsCodes;
+                rationOprObj.buildSheet($("#rationItemsSheet")[0]);
+                // tabPanel 下有多个Spread时,相互之间不能正确显示。改成一个Spread下多个Sheet。
+                var rdSpread = sheetCommonObj.createSpread($("#rdSpread")[0], 4);
+                rdSpread.options.allowUserDragFill = false;
+                rdSpread.options.allowUserDragDrop = false;
+                sheetCommonObj.spreadDefaultStyle(rdSpread);
+                rationGLJOprObj.buildSheet(rdSpread.getSheet(0));
+                rationAssistOprObj.buildSheet(rdSpread.getSheet(1));
+                rationCoeOprObj.buildSheet(rdSpread.getSheet(2));
+                rationInstObj.buildSheet(rdSpread.getSheet(3));
+                rationInstObj.getInstallation(data.installationData);
+                let rdSpreadEscSheets = [];
+                rdSpreadEscSheets.push({sheet: rdSpread.getSheet(0), editStarting: rationGLJOprObj.onEditStarting, editEnded: rationGLJOprObj.onCellEditEnd});
+                rdSpreadEscSheets.push({sheet: rdSpread.getSheet(1), editStarting: rationAssistOprObj.onEditStarting, editEnded: rationAssistOprObj.onEditEnded});
+                rdSpreadEscSheets.push({sheet: rdSpread.getSheet(2), editStarting: rationCoeOprObj.onEditStarting, editEnded: rationCoeOprObj.onEditEnded});
+                rdSpreadEscSheets.push({sheet: rdSpread.getSheet(3), editStarting: rationInstObj.onEditStarting, editEnded: rationInstObj.onEditEnded});
+                sheetCommonObj.bindEscKey(rdSpread, rdSpreadEscSheets);
+                pageOprObj.initPage();
+
+                $("#linkGLJ").click(function(){
+                    rationGLJOprObj.bindRationGljDelOpr();
+                    rdSpread.setActiveSheetIndex(0);
+                });
+
+                $("#linkFZDE").click(function(){
+                    rationAssistOprObj.bindRationAssDel();
+                    rdSpread.setActiveSheetIndex(1);
+                });
+
+                $("#linkFZTJ").click(function(){
+                    rationCoeOprObj.bindRationCoeDel();
+                    rdSpread.setActiveSheetIndex(2);
+                });
+
+                $("#linkAZZJ").click(function(){
+                    rationInstObj.bindRationInstDel();
+                    rdSpread.setActiveSheetIndex(3);
+                });
+            }
+            $.bootstrapLoading.end();
+        });
+        //解决spreadjs sheet初始化没高度宽度
+        $('#modalCon').width($(window).width()*0.5);
+        $('#gljSelTreeDiv').height($(window).height() - 300);
+        $("#gljSelSheet").height($("#gljSelTreeDiv").height()-21.6);
+        $("#gljSelSheet").width($('#modalCon').width() * 0.63);
+        $(window).resize(function () {
+            $('#modalCon').width($(window).width()*0.5);
+            $('#gljSelTreeDiv').height($(window).height() - 300);
+            $("#gljSelSheet").height($("#gljSelTreeDiv").height()-21.6);
+            $("#gljSelSheet").width($('#modalCon').width()* 0.63);
+        });
+    });
+})();

+ 1 - 23
web/building_saas/complementary_ration_lib/js/ration.js

@@ -95,7 +95,6 @@ let rationOprObj = {
         let me = rationOprObj;
         me.workBook = sheetCommonObj.buildSheet(container, me.setting, 30);
         sheetCommonObj.bindEscKey(me.workBook, [{sheet: me.workBook.getSheet(0), editStarting: me.onCellEditStart, editEnded: me.onCellEditEnd}]);
-        me.getRationsCodes(rationRepId);
         me.onContextmenuOpr();
         me.rationDelOpr();
         me.setCombo(me.workBook.getSheet(0), 'dynamic');
@@ -428,7 +427,7 @@ let rationOprObj = {
         let dataCode = me.setting.header[args.col].dataCode;
         let deESCFields = ['code', 'name', 'unit'];
         if(deESCFields.includes(dataCode)){
-            args.editingText = me.isDef(args.editingText) ? args.editingText.toString().replace(/[\r, \n]/g, ' ') : '';
+            args.editingText = me.isDef(args.editingText) ? args.editingText.toString().replace(/[\r\n]/g, ' ') : '';
             args.sheet.setValue(args.row, args.col, args.editingText);
         }
         let rObj = sheetsOprObj.combineRationRowData(me.workBook.getSheet(0), me.setting, args.row),
@@ -615,27 +614,6 @@ let rationOprObj = {
         obj.machinePrice = 0;
         obj.basePrice = 0;
     },
-    isValidUnit: function (rationObj, validUnits) {
-        let rst = true;
-        if(typeof rationObj.unit !== 'undefined' && rationObj.unit && validUnits.indexOf(rationObj.unit) === -1){//无效
-            rst = false;
-        }
-        return rst;
-    },
-    getRationsCodes: function (repId) {
-        let me = rationOprObj;
-        $.ajax({
-            type: 'post',
-            url: 'api/getRationsCodes',
-            data: {data: JSON.stringify({repId: repId})},
-            dataType: 'json',
-            success: function (result) {
-                if(!result.error){
-                    me.rationsCodes = result.data;
-                }
-            }
-        })
-    },
     mixUpdateRequest: function(updateArr, addArr, removeIds, callback) {
         let me = rationOprObj;
         me.saveInString(updateArr);

+ 9 - 16
web/building_saas/complementary_ration_lib/js/ration_glj.js

@@ -25,7 +25,6 @@ var rationGLJOprObj = {
         }
     },
     getDistTypeTree: function (gljDistType) {
-        let me = this;
         let distType;
         let distTypeTree = {
             prefix : 'gljDistType',
@@ -76,21 +75,15 @@ var rationGLJOprObj = {
         })
     },
     buildSheet: function(sheet) {
-        var me = this;
-        me.sheet = sheet;
-        me.getGljDistType(function () {
-           // gljSelOprObj.getGljClassTree(pageOprObj.gljLibId, function () {
-              //  gljSelOprObj.getSelGljItems(pageOprObj.gljLibId, function () {
-                    sheetCommonObj.initSheet(me.sheet, me.setting, 30);
-                    me.onContextmenuOpr();
-                    me.bindRationGljDelOpr();
-                    me.sheet.bind(GC.Spread.Sheets.Events.ClipboardPasting, me.onClipboardPasting);
-                    me.sheet.bind(GC.Spread.Sheets.Events.ClipboardPasted, me.onClipboardPasted);
-                    me.sheet.bind(GC.Spread.Sheets.Events.EditStarting, me.onEditStarting);
-                    me.sheet.bind(GC.Spread.Sheets.Events.EditEnded, me.onCellEditEnd);
-                });
-          //  });
-        //});
+        this.sheet = sheet;
+        this.distTypeTree = this.getDistTypeTree(this.distTypeTree);
+        sheetCommonObj.initSheet(this.sheet, this.setting, 30);
+        this.onContextmenuOpr();
+        this.bindRationGljDelOpr();
+        this.sheet.bind(GC.Spread.Sheets.Events.ClipboardPasting, this.onClipboardPasting);
+        this.sheet.bind(GC.Spread.Sheets.Events.ClipboardPasted, this.onClipboardPasted);
+        this.sheet.bind(GC.Spread.Sheets.Events.EditStarting, this.onEditStarting);
+        this.sheet.bind(GC.Spread.Sheets.Events.EditEnded, this.onCellEditEnd);
     },
     onContextmenuOpr: function () {//右键菜单
         let me = this;

+ 11 - 17
web/building_saas/complementary_ration_lib/js/ration_installation.js

@@ -42,24 +42,18 @@ let rationInstObj = {
     isDef: function (v) {
         return v !== undefined && v !== null;
     },
-    getInstallation: function (callback) {
-        let me = this;
-        CommonAjax.post('api/getInstallation', {}, function (rstData) {
-            //建立name - ID 映射, ID - name 映射
-            me.feeItem = {};
-            me.IDMapping = {feeItem: {}, section: {}};
-            for(let feeItem of rstData){
-                me.feeItem[feeItem.feeItem] = {ID: feeItem.ID, section: {}};
-                me.IDMapping['feeItem'][feeItem.ID] = feeItem.feeItem;
-                for(let section of feeItem.section){
-                    me.feeItem[feeItem.feeItem]['section'][section.name] = section.ID;
-                    me.IDMapping['section'][section.ID] = section.name;
-                }
-            }
-            if(callback){
-                callback(rstData);
+    getInstallation: function (installationData) {
+        //建立name - ID 映射, ID - name 映射
+        this.feeItem = {};
+        this.IDMapping = {feeItem: {}, section: {}};
+        for(let feeItem of installationData){
+            this.feeItem[feeItem.feeItem] = {ID: feeItem.ID, section: {}};
+            this.IDMapping['feeItem'][feeItem.ID] = feeItem.feeItem;
+            for(let section of feeItem.section){
+                this.feeItem[feeItem.feeItem]['section'][section.name] = section.ID;
+                this.IDMapping['section'][section.ID] = section.name;
             }
-        });
+        }
     },
     getFeeItemCombo: function () {
         let feeItemArr = [];

+ 18 - 50
web/building_saas/complementary_ration_lib/js/section_tree.js

@@ -2,31 +2,14 @@
  * Created by Zhong on 2017/12/18.
  */
 let pageOprObj = {
-    refreshAllBooks: function () {
-        if (sectionTreeObj.workBook) {
-            sectionTreeObj.workBook.refresh();
-        }
-        if (rationOprObj.workBook) {
-            rationOprObj.workBook.refresh();
-        }
-        if (coeOprObj.workBook) {
-            coeOprObj.workBook.refresh();
-        } else {
-            pageObj.initPage();
-        }
-        if (gljAdjOprObj.workBook) {
-            gljAdjOprObj.workBook.refresh();
-        }
-        if (rationGLJOprObj.sheet.getParent()) {
-            rationGLJOprObj.sheet.getParent().refresh();
-        }
-    },
     rationLibName : null,
     rationLibId : null,
     gljLibId: gljLibId,
+    rationTreeData: null,
+    mixedTreeData: null,
+    mixedGLJData: null,
     initPage : function() {
-        let me = this;
-        sectionTreeObj.getSectionTree();
+        sectionTreeObj.getSectionTree(this.rationTreeData);
         //job
         jobContentOprObj.radiosChange(jobContentOprObj.radios, jobContentOprObj.tableAll, jobContentOprObj.tablePartial);
         $('#addConBtn').click(jobContentOprObj.bindAddConBtn());
@@ -37,9 +20,8 @@ let pageOprObj = {
         $('#fzAddConBtn').click(annotationOprObj.bindAddConBtn());
         $('#fzUpdateConBtn').click(annotationOprObj.bindUpdateConBtn());
         annotationOprObj.bindAllEvents($('#fzTxtareaAll'));
-        gljSelOprObj.getGljClassTree(gljLibId, function () {
-            gljSelOprObj.getSelGljItems(gljLibId);
-        });
+        gljSelOprObj.getGljClassTree(this.mixedTreeData);
+        gljSelOprObj.getSelGljItems(this.mixedGLJData);
     },
     getRationLibInfo: function (rationLibId, callback) {
         CommonAjax.post('api/getRationLib', {rationRepId: rationLibId}, callback);
@@ -223,32 +205,18 @@ let sectionTreeObj = {
             sheetCommonObj.setColumnWidthByRate($('#sectionSpread').width() - 65, this.workBook, [{rateWidth: 1}]);//65: 列头宽度和垂直滚动条宽度和
         }
     },
-    getSectionTree: function () {
-        let me = sectionTreeObj;
-        let url = 'api/getRationTree';
-        //type: 0-补充库 1-标准库
-        let postData = {type: 0};
-        let sucFunc = function (rstData) {
-            if(rstData.length > 0){
-                storageUtil.setSessionCache("RationGrp","repositoryID",rstData[0].rationRepId);
-            }
-            //init
-            me.buildSheet();
-            me.initTree(rstData);
-            me.cache = me.tree.items;
-            me.bindBtn();
-            me.initController(me.tree, me.sheet, me.setting.sheet);
-            me.controller.showTreeData();
-            me.setColor(me.cache);
-            me.sheet.setFormatter(-1, 0, '@');
-            me.initSelection(me.tree.selected);
-            me.loadRateWidth();
-            //explanatoryOprObj.bindEvents($('#explanationShow'), $('#ruleTextShow'));
-        };
-        let errFunc = function () {
-
-        };
-        CommonAjax.post(url, postData, sucFunc, errFunc);
+    getSectionTree: function (treeData) {
+        //init
+        this.buildSheet();
+        this.initTree(treeData);
+        this.cache = this.tree.items;
+        this.bindBtn();
+        this.initController(this.tree, this.sheet, this.setting.sheet);
+        this.controller.showTreeData();
+        this.setColor(this.cache);
+        this.sheet.setFormatter(-1, 0, '@');
+        this.initSelection(this.tree.selected);
+        this.loadRateWidth();
     },
 
     setColor: function (nodes) {

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

@@ -54,7 +54,7 @@ legend.legend{
     width: 110px;
 }
 .a_color{
-    color: #007bff;
+    color: #007bff
 }
 
 .filterType a{
@@ -344,4 +344,87 @@ input.text-right{
 /*占位底色*/
 .occupied {
     background: #f1f1f1;
+}
+/*书签批注*/
+.annotate-color-1::before{
+    color: #E2F2C5 !important;
+    -webkit-text-stroke:.5px #ced4da;
+}
+.annotate-color-2::before{
+    color: #F9E2CF !important;
+    -webkit-text-stroke:.5px #ced4da;
+}
+.annotate-color-3::before{
+    color:#F2EFD9 !important;
+    -webkit-text-stroke:.5px #ced4da;
+}
+.annotate-color-4::before{
+    color:#F5D1DA !important;
+    -webkit-text-stroke:.5px #ced4da;
+}
+.annotate-color-5::before{
+    color:#E3E3E3 !important;
+    -webkit-text-stroke:.5px #ced4da;
+}
+.annotate-color-6::before{
+    color:#B6F3F2 !important;
+    -webkit-text-stroke:.5px #ced4da;
+}
+.annotate-color-7::before{
+    color:#ECE0F5 !important;
+    -webkit-text-stroke:.5px #ced4da;
+}
+.progress-bar {
+    position: relative;
+    width: 100%;
+}
+.progress-move {
+    position: absolute;
+    left: 0;
+    top: 0;
+    z-index: 999;
+    width: 200%;
+    height: 100%;
+    border-radius: .25rem;
+    animation: progressMove 20s linear infinite;
+}
+@keyframes progressMove {
+    from {
+        transform: translateX(0);
+    }
+    to {
+        transform: translateX(-280px);
+    }
+}
+.progress-cover {
+    position: absolute;
+    left: 0;
+    top: 0;
+    z-index: 1000;
+    width: 100%;
+    height: 100%;
+    background-color: #e9ecef;
+}
+.progress-cover-move {
+    animation: progressCoverMove 40s linear;
+    animation-fill-mode: forwards;
+}
+@keyframes progressCoverMove {
+    0% {
+        transform: translateX(0);
+    }
+    20% {
+        transform: translateX(330px);
+    }
+    40% {
+        transform: translateX(380px);
+    }
+    100% {
+        transform: translateX(420px);
+    }
+}
+.text-ellipsis {
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
 }

+ 265 - 222
web/building_saas/css/main.css

@@ -3,87 +3,92 @@
 body {
     font-size: 0.9rem;
     overflow: hidden;
+    font-family: "Helvetica Neue","Hiragino Sans GB",stheiti,"Microsoft Yahei","微软雅黑",tahoma,sans-serif
 }
 a{
-    outline: none
+  outline: none
 }
 .dropdown-menu {
     font-size: 0.9rem
 }
 .btn.disabled, .btn:disabled {
-    color:#999
+  color:#999
 }
 .btn-link:focus, .btn-link:hover{
-    text-decoration: none
+  text-decoration: none
 }
 .dropdown-menu {
-    padding:.2rem 0;
+  padding:.2rem 0;
 }
 .dropdown-item:focus, .dropdown-item:hover{
-    background-color: #f7f7f9
+  background-color: #f7f7f9
 }
 .text-indent {
-    text-indent:26px;
+  text-indent:26px;
 }
 .modal-header{
-    background:#f2f2f2;
-    border-bottom:1px solid #ccc;
-    padding:.8rem 1rem;
+  background:#f2f2f2;
+  border-bottom:1px solid #ccc;
+  padding:.8rem 1rem;
 }
 .modal-title{
-    font-size:1rem;
+  font-size:1rem;
 }
 .modal-footer{
-    padding:.8rem 1rem;
+  padding:.8rem 1rem;
 }
 .form-check .form-check-label,.form-radio .form-check-label{
-    cursor: pointer;
+  cursor: pointer;
 }
 .custom-file-sm,.custom-file-sm .custom-file-label{
-    height: calc(1.5em + .5rem + 2px);
-    padding-top: .25rem;
-    padding-bottom: .25rem;
-    padding-left: .5rem;
+  height: calc(1.5em + .5rem + 2px);
+  padding-top: .25rem;
+  padding-bottom: .25rem;
+  padding-left: .5rem;
 }
 .custom-file-sm .custom-file-label:after{
-    height: calc(1.4em + .5rem + 2px);
-    padding-top: .25rem;
-    padding-bottom: .25rem;
-    font-size: .875rem;
+  height: calc(1.4em + .5rem + 2px);
+padding-top: .25rem;
+padding-bottom: .25rem;
+font-size: .875rem;
 }
 .custom-file-sm input{
-    height: calc(1.5em + .5rem + 2px);
-    font-size: .875rem;
+  height: calc(1.5em + .5rem + 2px);
+font-size: .875rem;
 }
 /*自定义css*/
 .login-body,.login-html{
-    height:100%;
+  height:100%;
 }
 .login-bg{
-    width: 100%;
-    height: 100%;
-    background: #f2f6f5 url("login-bg.jpg") bottom center no-repeat;
-    background-size: 100% auto;
-    display: inline-block;
-    position: absolute;
+  width: 100%;
+  height: 100%;
+  background: #f2f6f5 url("login-bg.jpg") bottom center no-repeat;
+  background-size: 100% auto;
+  display: inline-block;
+  position: absolute;
 }
 .login-panel {
-    width: 500px;
-    margin: 0 auto;
-    position: absolute;
-    left: 0;
-    right: 0;
-    top: 20%;
-    padding:30px;
-    background:#fff;
-    box-shadow:#333 1px 1px 5px
+  width: 500px;
+  margin: 0 auto;
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 20%;
+  padding:30px;
+  background:#fff;
+  box-shadow:#333 1px 1px 5px
+}
+.login-img-cr{
+  right:0;
+  font-size:9px;
 }
 .ver-panel {
-    width:100%;
-    top:10%;
+  width:100%;
+  top:10%;
 }
 .header {
-    border-bottom: 1px solid #ccc
+  border-bottom: 1px solid #ccc
 }
 .header .header-logo {
     background: #ff6501;
@@ -95,26 +100,26 @@ a{
     padding-left:50px;
 }
 .header-logo div.v-title{
-    font-size:11px;
-    color:#aeaeae;
-    line-height: 12px;
-    margin-top:3px;
+  font-size:11px;
+  color:#aeaeae;
+  line-height: 12px;
+  margin-top:3px;
 }
 .header-logo div.p-title{
-    font-size:16px;
-    color:#ff6501;
+  font-size:16px;
+  color:#ff6501;
 }
 .header-logo div.b-title{
-    font-size:24px;
-    color:#ff6501;
+  font-size:24px;
+  color:#ff6501;
 }
 .top-msg{
-    position: fixed;
-    top:10px;
-    width:900px;
-    z-index: 9999;
-    left:50%;
-    margin-left:-450px;
+  position: fixed;
+  top:10px;
+  width:900px;
+  z-index: 9999;
+  left:50%;
+  margin-left:-450px;
 }
 .main {
     position: relative;
@@ -146,45 +151,55 @@ a{
     color: #333
 }
 .main-nav .nav-tabs{
-    border-bottom: none
+  border-bottom: none
 }
 .poj-manage .col-auto{
-    background:#f7f7f9
+  background:#f7f7f9
 }
 .poj-cate {
-    width:58px;
-    margin-right:-1px;
-    margin-top:2px;
+  width:58px;
+  margin-right:-1px;
+  margin-top:2px;
 }
 .poj-cate .nav-pills .nav-link{
-    border-radius: 0;
-    font-size: 24px;
-    color: #007bff
+  border-radius: 0;
+  font-size: 24px;
+  color: #007bff
 }
 .poj-cate .nav-item{
-    position: relative;
+  position: relative;
 }
 .poj-cate .nav-pills .badge{
-    position: absolute;
-    right:5px;
-    top:5px;
+  position: absolute;
+  right:5px;
+  top:5px;
 }
 .poj-cate .nav-pills .nav-link:hover{
-    color:#0056b3
+  color:#0056b3
 }
 .poj-cate .nav-pills .nav-link.active, .nav-pills .show > .nav-link{
-    background-color: #6c757d;
-    color:#fff
+  background-color: #6c757d;
+  color:#fff
+}
+.gl-side.gl-side-lg{
+  width:200px;
+}
+.nav-side .nav-link{
+  padding:.5rem;
+}
+.nav-side .nav-link.active{
+  background:#ddd;
+  color:#333;
 }
 .pm-side{
-    position: fixed;
-    left:0;
-    height:100%;
-    border-right:1px solid #ccc;
-    background: #f7f7f9
+  position: fixed;
+  left:0;
+  height:100%;
+  border-right:1px solid #ccc;
+  background: #f7f7f9
 }
 .pm-content{
-    padding-left:58px
+  padding-left:58px
 }
 .content {
     border-left: 1px solid #ccc;
@@ -192,8 +207,8 @@ a{
     background: #fff
 }
 .toolsbar,.toolsbar-f {
-    background: #f7f7f9;
-    border-bottom: 1px solid #ccc
+  background: #f7f7f9;
+  border-bottom: 1px solid #ccc
 }
 .tools-btn {
     height: 30px;
@@ -203,40 +218,40 @@ a{
     background: #f7f7f9;
 }
 .toolsbar .btn-light {
-    color:#007bff;
-    min-width: 34px;
+  color:#007bff;
+  min-width: 34px;
 }
 .toolsbar .btn-light.disabled {
-    color:#999;
+  color:#999;
 }
 .main-side {
     border-left: 1px solid #ccc;
     overflow-y: hidden;
 }
 .main-data-top,.main-data-full{
-    overflow: hidden;
-    width:100%
+  overflow: hidden;
+  width:100%
 }
 .main-data-top-side{
-    overflow: hidden
+  overflow: hidden
 }
 .main-content.col-lg-8{
-    width:66.666667%
+  width:66.666667%
 }
 .main-content.col-lg-12{
-    width:100%
+  width:100%
 }
 .main-side.col-lg-4{
-    width: 33.333333%;
+  width: 33.333333%;
 }
 .main-side.col-lg-0{
-    width:0%;
+  width:0%;
 }
 .sidebar-middle{
-    height:80px;
+  height:80px;
 }
 .sidebar-bottom,.sidebar-bottom .col-lg-6,.sidebar-bottom .col-lg-12 {
-    height:300px
+  height:300px
 }
 .top-content, .fluid-content {
     overflow: auto;
@@ -260,7 +275,7 @@ a{
     z-index: 999
 }
 .side-tabs .nav-tabs .nav-item {
-    z-index: 999
+  z-index: 999
 }
 .side-tabs .nav-tabs {
     border-bottom: none;
@@ -373,19 +388,9 @@ a{
         transform: translateX(10px);
     }
 }
-.gl-side.gl-side-lg{
-    width:200px;
-}
-.nav-side .nav-link{
-    padding:.5rem;
-}
-.nav-side .nav-link.active{
-    background:#ddd;
-    color:#333;
-}
 .gl-side{
-    border-right:1px solid #ccc;
-    width:120px
+  border-right:1px solid #ccc;
+  width:120px
 }
 .bottom-content .tab-content .main-data-bottom{
     height: 300px;
@@ -399,141 +404,141 @@ a{
     overflow: hidden;
 }
 .tn-nav,.rn-nav{
-    width:30px;
-    height: 100%;
-    border-left:1px solid #dee2e6 ;
+  width:30px;
+  height: 100%;
+  border-left:1px solid #dee2e6 ;
 }
 .tn-nav,.cn-nav{
-    border-right:1px solid #dee2e6 ;
-    border-left:none;
+  border-right:1px solid #dee2e6 ;
+  border-left:none;
 }
 .tn-nav > span{
-    width:20px;
+  width:20px;
 }
 .tn-nav:hover{
-    background:#f7f7f9;
-    cursor: pointer;
+  background:#f7f7f9;
+  cursor: pointer;
 }
 .rn-nav {
-    background: #f7f7f9
+  background: #f7f7f9
 }
 .rn-nav .nav{
-    padding: 0
+  padding: 0
 }
 .rn-nav .nav-tabs {
-    border:none;
-    margin-top:2px;
+  border:none;
+  margin-top:2px;
 }
 .rn-nav .nav-tabs .nav-link{
-    padding:0.4em 0.4em;
-    line-height: 16px
+  padding:0.4em 0.4em;
+  line-height: 16px
 }
 .rn-nav .nav-tabs .nav-link.active {
-    border:none;
-    background-color: #fff;
-    border-left:1px solid #fff;
-    margin-left:-1px;
-    border-top:1px solid #ccc;
-    border-right:1px solid #ccc;
-    border-bottom:1px solid #ccc;
+  border:none;
+  background-color: #fff;
+  border-left:1px solid #fff;
+  margin-left:-1px;
+  border-top:1px solid #ccc;
+  border-right:1px solid #ccc;
+  border-bottom:1px solid #ccc;
 }
 .cn-nav .nav-tabs .nav-link.active {
-    border:none;
-    background-color: #fff;
-    border-right:1px solid #fff;
-    margin-right:-1px;
-    border-top:1px solid #ccc;
-    border-left:1px solid #ccc;
-    border-bottom:1px solid #ccc;
+  border:none;
+  background-color: #fff;
+  border-right:1px solid #fff;
+  margin-right:-1px;
+  border-top:1px solid #ccc;
+  border-left:1px solid #ccc;
+  border-bottom:1px solid #ccc;
 }
 .rn-nav .nav-tabs .nav-link span{
-    display:none;
+  display:none;
 }
 .rn-nav .nav-tabs .nav-link.active span{
-    display: inline-block;
+  display: inline-block;
 }
 .poj-list, .side-content ,.form-view{
     overflow: auto;
 }
 .print-toolsbar{
-    padding:5px
+  padding:5px
 }
 .print-toolsbar .panel {
-    display:inline-block;
-    vertical-align:top
+  display:inline-block;
+  vertical-align:top
 }
 .print-toolsbar .panel .panel-foot{
-    text-align: center;
-    font-size: 12px;
-    padding-bottom:3px;
-    background:#F2F2F2
+  text-align: center;
+  font-size: 12px;
+  padding-bottom:3px;
+  background:#F2F2F2
 }
 .print-list {
-    border-right:1px solid #ccc
+  border-right:1px solid #ccc
 }
 .print-list .form-list {
-    overflow: auto
+  overflow: auto
 }
 .print-list .list-tools{
-    height:50px;
-    padding:10px 0;
-    border-bottom:1px solid #f2f2f2
+  height:50px;
+  padding:10px 0;
+  border-bottom:1px solid #f2f2f2
 }
 .pageContainer {
-    text-align: center
+  text-align: center
 }
 .pageContainer .page{
-    border:9px solid transparent;
-    display: inline-block;
+  border:9px solid transparent;
+  display: inline-block;
 }
 .pageContainer .page img{
-    width:inherit;
-    height: inherit;
+  width:inherit;
+  height: inherit;
 }
 .modal-auto-height {
-    height: 400px;
-    overflow-y: auto;
+  height: 400px;
+  overflow-y: auto;
 }
 .modal-toolbar + .modal-auto-height{
-    height:369px
+  height:369px
 }
 .modal-fixed-height {
-    height: 400px;
-    overflow-y: hidden;
+  height: 400px;
+  overflow-y: hidden;
 }
 .sidebar-tools-bar {
-    background:#fff
+  background:#fff
 }
 .side-search-box{
-    background:#fff;
-    border-bottom:1px solid #ddd
+  background:#fff;
+  border-bottom:1px solid #ddd
 }
 .navbar-crumb span{
-    float:left;
+  float:left;
 }
 .navbar-crumb span.text-truncate{
-    max-width: 200px;
+  max-width: 200px;
 }
 .dropdown-item{
-    color:#007bff
+  color:#007bff
 }
 .dropdown-item:hover{
-    color:#0056b3
+  color:#0056b3
 }
 .dropdown-item.disabled, .dropdown-item:disabled{
-    pointer-events:none
+  pointer-events:none
 }
 .tools-symbol{
-    width:450px
+  width:450px
 }
 .tools-symbol p{
-    margin: 0
+  margin: 0
 }
 .tools-symbol a.btn{
-    font-size: .95rem;
-    width:26px;
-    padding: 0;
-    height:26px;
+  font-size: .95rem;
+  width:26px;
+  padding: 0;
+  height:26px;
 }
 .custom-file-input:lang(zh) ~ .custom-file-label::after {
     content: "浏览";
@@ -566,111 +571,149 @@ a{
     border-color:#000 transparent transparent transparent;
 }
 .box-text-style {
-    font-size: 12px;
-    font-family:"Microsoft YaHei"
+  font-size: 12px;
+  font-family:"Microsoft YaHei"
 }
 .box-text-style p{
-    margin:0 0 2px 0;
+  margin:0 0 2px 0;
 }
 .box-text-style table{
-    width: 100%;
-    text-align: center;
+  width: 100%;
+  text-align: center;
 }
 .box-text-style table th,.box-text-style table td{
-    border:1px solid #fff;
+  border:1px solid #fff;
 }
 
 .box-text-style table p{
-    text-align: left;
+  text-align: left;
 }
 .sys-setting-nav .nav-link{
-    padding:.3rem 1rem;
+  padding:.3rem 1rem;
 }
 /*快捷切换单位工程*/
 .navbar-crumb{
-    position: relative;
+  position: relative;
 }
 .navbar-crumb .f-nav,.navbar-crumb .s-nav{
-    position: absolute;
-    z-index: 999;
-    width: 200px;
-    background:#fff;
-    border: 1px solid rgba(0,0,0,.15);
-    padding: .5rem 0;
+  position: absolute;
+  z-index: 999;
+  width: 200px;
+  background:#fff;
+  border: 1px solid rgba(0,0,0,.15);
+  padding: .5rem 0;
 }
 .navbar-crumb .f-nav{
-    right:50px;
-    top:35px;
+  right:50px;
+  top:35px;
 }
 .navbar-crumb .s-nav{
-    left:200px
+  left:200px
 }
 .navbar-crumb .f-nav li{
-    padding: .25rem 1rem;
-    cursor: default;
+  padding: .25rem 1rem;
+  cursor: default;
 }
 .navbar-crumb .f-nav li.focus{
-    color: #16181b;
-    background-color: #f7f7f9;
+color: #16181b;
+background-color: #f7f7f9;
 }
 .navbar-crumb .f-nav li .s-nav{
-    display: none
+  display: none
 }
 .navbar-crumb .f-nav li.focus .s-nav{
-    display: block
+  display: block
 }
 /*书签颜色*/
 .annotate-color-1{
-    background: #E2F2C5
+  background: #E2F2C5;
+  color: #E2F2C5;
+}
+.annotate-color-1::before{
+  color: #E2F2C5;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-2{
-    background: #F9E2CF
+  background: #F9E2CF;
+  color:#F9E2CF
+}
+.annotate-color-2::before{
+  color: #F9E2CF;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-3{
-    background: #F2EFD9
+  background: #F2EFD9;
+  color:#F2EFD9
+}
+.annotate-color-3::before{
+  color:#F2EFD9;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-4{
-    background: #F5D1DA
+  background: #F5D1DA;
+  color:#F5D1DA
+}
+.annotate-color-4::before{
+  color:#F5D1DA;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-5{
-    background: #E3E3E3
+  background: #E3E3E3;
+  color:#E3E3E3
+}
+.annotate-color-5::before{
+  color:#E3E3E3;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-6{
-    background: #B6F3F2
+  background: #B6F3F2;
+  color:#B6F3F2
+}
+.annotate-color-6::before{
+  color:#B6F3F2;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-7{
-    background: #ECE0F5
+  background: #ECE0F5;
+  color:#ECE0F5
+}
+.annotate-color-7::before{
+  color:#ECE0F5;
+  -webkit-text-stroke:.5px #ced4da;
+}
+.input-group-text.active{
+  color:#666;
 }
 /*宽屏菜单*/
 @media screen and (min-width: 900px) {
-    #fluid-menu{
-    }
-    #min-menu{
-        display: none
-    }
+#fluid-menu{
+}
+#min-menu{
+  display: none
+}
 }
 @media screen and (min-width: 912px) {
-    #share-info{
-        overflow: auto;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-        max-width:inherit;
-    }
+#share-info{
+  overflow: auto;
+text-overflow: ellipsis;
+white-space: nowrap;
+max-width:inherit;
+}
 }
 /*窄屏菜单*/
 @media screen and (max-width: 900px) {
-    #fluid-menu{
-        display: none
-    }
-    #min-menu{
+#fluid-menu{
+  display: none
+}
+#min-menu{
 
-    }
+}
 }
 @media screen and (max-width: 1018px) {
-    #share-info{
-        overflow: hidden;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-        max-width:80px;
-    }
+#share-info{
+  overflow: hidden;
+text-overflow: ellipsis;
+white-space: nowrap;
+max-width:80px;
+}
 }

+ 89 - 0
web/building_saas/glj/html/project_glj.html

@@ -32,12 +32,44 @@
                 <li class="nav-item"><a class="nav-link" href="javascript:void(0)" id="SCHZ">三材汇总</a></li>
                 <li class="nav-item"><a class="nav-link" href="javascript:void(0)" id="ZYCL">主要材料</a></li>
                 <li class="nav-item"><a class="nav-link" href="javascript:void(0)" id="AMAE">承包人主要材料设备</a></li>
+                <li class="nav-item"><a class="nav-link" href="javascript:void(0)" id="PBCL">评标材料</a></li>
             </ul>
         </div>
         <div class="main-content col p-0" id="material_adjust" style="overflow: hidden; display: none">
             <div class="" id="material_adjust_sheet" style="overflow: hidden;width: 100%">
             </div>
         </div>
+        <div class="main-content col p-0" id="config_material" style="overflow: hidden; display: none"><!--暂估材料,主要材料等-->
+            <div class="top-content" id="configMaterialTop" style="overflow: hidden">
+                <div class="main-data-top" style="width: 100%; overflow: hidden" id="config_material_sheet">
+                </div>
+            </div>
+            <div class="resize-y" id="configMaterialResize"></div>
+            <div class="bottom-content" id="configMaterialBottom">
+                <ul class="nav nav-tabs" role="tablist">
+                      <li class="nav-item">
+                          <a class=" nav-link  active " data-toggle="tab" data-name="related_sheet" id="related_link" href="#related_div" role="tab">关联暂估材料</a>
+                      </li>
+                  <!--   <li class="nav-item">
+                         <a class=" nav-link " data-toggle="tab" data-name="detail_sheet" id="detail_link" href="#related_div" role="tab">关联暂估材料明细</a>
+                     </li>-->
+                </ul>
+                <!-- Tab panes -->
+                <div class="tab-content">
+                    <div class="tab-pane active" id="related_div" role="tabpanel">
+                        <div class="form-inline py-1 toolsbar_feeRate" id="filterToolDiv">
+                            <label class="mx-2" for="filterGljName">材料名称包括:</label>
+                            <input type="text" class="form-control form-control-sm" id="filterGljName">
+                            <button type="button" class="btn btn-outline-primary btn-sm" id = "filterAgain">重新过滤</button>
+                           <!-- <button type="button" class="btn btn-outline-primary btn-sm" id = "autoRelated">自动关联招标材料</button>-->
+                        </div>
+                        <div class="main-data-bottom" id="related_sheet" style="overflow:hidden">
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
         <div class="main-content col p-0" id="project-glj-main" style="overflow: hidden; ">
             <div class="top-content" id="projectGljTop" style="overflow: hidden">
                 <div class="main-data-top" style="width: 100%; overflow: hidden" id="project_glj_sheet">
@@ -172,6 +204,7 @@
         </div>
     </div>
 </div>
+
 <!--弹出 从人材机汇总中选择-->
 <div class="modal fade" id="selectFromGLJ" data-backdrop="static">
     <div class="modal-dialog modal-lg" role="document">
@@ -234,4 +267,60 @@
             </div>
         </div>
     </div>
+</div>
+
+<!--暂估材料弹出 从人材机汇总中选择-->
+<div class="modal fade" id="selectPGLJ" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">从人材机汇总中选择</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body" style="padding: 0px">
+                <div class="btn-toolbar" style="margin: 10px">
+                    <div class="btn-group btn-group-sm mr-2">
+                        <button type="button" class="btn btn-outline-primary pglj_sel_check_btn">全选</button>
+                        <button type="button" class="btn btn-outline-primary pglj_sel_check_btn">全选主材</button>
+                        <button type="button" class="btn btn-outline-primary pglj_sel_check_btn">全选设备</button>
+                    </div>
+                    <!--前面3个按钮 有选中状态,出现 取消 按钮-->
+                    <button type="button" class="btn btn-outline-danger btn-sm mr-2 pglj_sel_check_btn">取消</button>
+                    <div class="input-group input-group-sm mr-2" style="width:200px">
+                        <input type="text" class="form-control form-control-sm"  placeholder="查找" id="pglj_sel_input" value="">
+                        <div class="input-group-append">
+                            <button class="btn btn-outline-primary btn-sm" id="pglj_sel_btn_find" type="button"><i class="fa fa-search" aria-hidden="true"></i></button>
+                        </div>
+                    </div>
+                    <div class="input-group input-group-sm">
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input pglj_sel_input" type="checkbox" id="pglj_sel_all" value="0">
+                            <label class="form-check-label" for="pglj_sel_all">所有</label>
+                        </div>
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input pglj_sel_input pglj_sel_input_other" type="checkbox" id="pglj_sel_material" checked value="2">
+                            <label class="form-check-label" for="pglj_sel_material">材</label>
+                        </div>
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input pglj_sel_input pglj_sel_input_other" type="checkbox" id="pglj_sel_main" checked value="4">
+                            <label class="form-check-label" for="pglj_sel_main">主材</label>
+                        </div>
+                        <div class="form-check form-check-inline">
+                            <input class="form-check-input pglj_sel_input pglj_sel_input_other" type="checkbox" id="pglj_sel_eqp" checked value="5">
+                            <label class="form-check-label" for="pglj_sel_eqp">设备</label>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-auto-height" style="margin-top:10px; border: 1px solid #ccc">
+                    <div  id="pglj_from_sheet" style="overflow: hidden;height: 100%"></div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-primary" id="pglj_sel_confirm" data-dismiss="modal">确定</button>
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+            </div>
+        </div>
+    </div>
 </div>

BIN
web/building_saas/img/building2.png


BIN
web/building_saas/img/logo.png


BIN
web/building_saas/img/qqgroupqrcode.png


+ 14 - 5
web/building_saas/js/global.js

@@ -35,6 +35,7 @@ function autoFlashHeight(){
     $('#comments').find('textarea').height($('#comments').height() - 25);
     typeof(adaptiveTzjnrWidth)== 'function' ?adaptiveTzjnrWidth():'';
     $('#project-glj-main').width($(window).width()-($('.main-nav').width()+ 2)-($('.filterType').width()+12)-5); //2、12是padding宽度,width 是不算padding宽度的
+    $('#config_material').width($(window).width()-($('.main-nav').width()+ 2)-($('.filterType').width()+12)-5); //2、12是padding宽度,width 是不算padding宽度的
     $('#material_adjust').width($(window).width()-($('.main-nav').width()+ 2)-($('.filterType').width()+12)-5);//材料调差
     $('#index_content').width($(window).width()-($('.main-nav').width()+ 2)-($('#index_nav').width()+12)-5);//材料调差
     //typeof zmhs_obj === 'object' ? zmhs_obj.loadSideResize() : '';
@@ -76,6 +77,10 @@ $(function () {
 
     });*/
     /*侧滑*/
+    /*显示QQ交流群*/
+    $("#showqqgroup").hover(function(){
+        $("#qqgroup").toggle();
+    });
     /*工具提示*/
     $(function () {
         $('[data-toggle="tooltip"]').tooltip();
@@ -141,10 +146,14 @@ function getFeeIndex(fees) {
     let feesIndex = {};
     if (fees) {
         for(let fee of fees){
-            fee.unitFee = parseFloat(fee.unitFee);
-            fee.totalFee = parseFloat(fee.totalFee);
-            fee.tenderUnitFee = parseFloat(fee.tenderUnitFee);
-            fee.tenderTotalFee = parseFloat(fee.tenderTotalFee);
+            const parsedUnitFee = parseFloat(fee.unitFee);
+            const parsedTotalFee = parseFloat(fee.totalFee);
+            const parsedTenderUnitFee = parseFloat(fee.tenderUnitFee);
+            const parsedTenderTotalFee = parseFloat(fee.tenderTotalFee);
+            fee.unitFee = parsedUnitFee || 0;
+            fee.totalFee = parsedTotalFee || 0;
+            fee.tenderUnitFee = parsedTenderUnitFee || 0;
+            fee.tenderTotalFee = parsedTenderTotalFee || 0;
             feesIndex[fee.fieldName] = fee;
         }
     }
@@ -165,4 +174,4 @@ function setTimeoutSync(handle, time) {
             resolve();
         }, time);
     });
-}
+}

+ 11 - 5
web/building_saas/main/html/main.html

@@ -5,7 +5,7 @@
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     <meta http-equiv="x-ua-compatible" content="ie=edge">
-    <title><%= projectData.name !== undefined ? projectData.name : '造价书' %>-纵横建筑计价</title>
+    <title><%= projectData.name !== undefined ? projectData.name : '造价书' %>-大司空云计价</title>
 
 
     <link rel="stylesheet" href="/lib/jquery-ui/jquery-ui.css" type="text/css">
@@ -25,7 +25,7 @@
     <link rel="stylesheet" href="/lib/spreadjs/views/gc.spread.views.dataview.10.0.0.css">
     <!-- jquery.contextmenu -->
    <!-- <link rel="stylesheet" href="/lib/jquery-contextmenu/jquery.contextMenu.css" type="text/css">-->
-   
+
     <link rel="shortcut icon" href="/web/building_saas/css/favicon.ico">
     <link rel="icon" type="image/gif" href="/web/building_saas/css/animated_favicon1.gif">
     <style type="text/css">
@@ -75,7 +75,8 @@
                 <li class="nav-item"><a data-toggle="tab" href="#project_glj" id="tab_project_glj" data-name="tab_project_glj" role="tab">人材机汇总</a></li>
                 <li class="nav-item"><a data-toggle="tab" href="#fee_rates" id="tab_fee_rate" role="tab" >费率</a></li>
                 <li class="nav-item"><a data-toggle="tab" href="#calc_program_manage" id="tab_calc_program_manage" role="tab" style="display:none">总计算程序</a></li>
-                <li class="nav-item"><a data-toggle="tab" href="#tender_price" id="tab_tender_price" role="tab" style="display:none">调价</a></li>
+                <!--<li class="nav-item"><a data-toggle="tab" href="#tender_price" id="tab_tender_price" role="tab" style="display:none">调价</a></li>-->
+              <!--  <li class="nav-item"><a data-toggle="tab" href="#tender_price" id="tab_tender_price" role="tab">调价</a></li>-->
                 <li class="nav-item"><a data-toggle="tab" href="#reports" role="tab" id="tab_report" onclick="rptTplObj.iniPage();">报表</a></li>
                 <li class="nav-item"><a data-toggle="tab" href="#index" id="tab_index" role="tab" style="display:none">指标信息</a></li>
             </ul>
@@ -1209,7 +1210,7 @@
                 </div>
             </div>
         </div>
-    </div>    
+    </div>
     <!--弹出列设置-->
   <!--  <div class="modal fade" id="column" data-backdrop="static">
         <div class="modal-dialog modal-lg" role="document" style="width: 350px;">
@@ -2169,7 +2170,11 @@
             <div class="modal-body">
                 <h5 id="progress-content" class="my-3"></h5>
                 <div class="progress mb-3">
-                    <div class="progress-bar progress-bar-striped progress-bar-animated" id="progressBar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
+                    <!--<div class="progress-bar progress-bar-striped progress-bar-animated" id="progressBar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">-->
+                    <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
+                        <div class="progress-move progress-bar-striped"></div>
+                        <div class="progress-cover" id="progressCover"></div>
+                    </div>
                 </div>
             </div>
         </div>
@@ -2307,6 +2312,7 @@
     <script type="text/javascript" src='/web/building_saas/main/js/views/zlfb_view.js'></script>
     <script type="text/javascript" src='/web/building_saas/main/js/views/installation_fee_view.js'></script>
     <script type="text/javascript" src='/web/building_saas/main/js/views/material_adjust_view.js'></script>
+    <script type="text/javascript" src='/web/building_saas/main/js/views/config_material_view.js'></script>
     <script type="text/javascript" src='/web/building_saas/main/js/views/index_view.js'></script>
     <script type="text/javascript" src='/web/building_saas/main/js/views/project_glj_view.js'></script>
     <script type="text/javascript" src="/web/building_saas/main/js/views/importBills.js"></script>

+ 59 - 12
web/building_saas/main/js/controllers/block_controller.js

@@ -54,6 +54,28 @@ let BlockController = {
         }
         return false;
     },
+    // 恢复删除节点有效性
+    recoverBlockDisabled: function () {
+        if (projectReadOnly) {
+            return true;
+        }
+        const recycle = getLocalCache('project_recycle');
+        if (!recycle) {
+            return true;
+        }
+        const recycleObj = JSON.parse(recycle);
+        const firstData = recycleObj.datas.length ? recycleObj.datas[0] : null;
+        if (!firstData || firstData.projectID !== projectObj.project.ID()) {
+            return true;
+        }
+        const parentID = firstData.sourceType === projectObj.project.Bills.getSourceType() ? firstData.ParentID : firstData.billsItemID;
+        const parent = projectObj.project.mainTree.findNode(parentID);
+        // parentID为-1时,为大项费用
+        if (parentID !== -1 && !parent) {
+            return true;
+        }
+        return false;
+    },
     haveCalcBase : function (node) {
         if(node.data.calcBase ==null||node.data.calcBase ==undefined|| node.data.calcBase == ""){
             return false;
@@ -77,10 +99,9 @@ let BlockController = {
         }
         return null;
     },
-
-    copyBlock:function (selection) {
+    extractBlockData: function (selection) {
         let startTime = +new Date();
-        let blockOjb = {
+        let blockObj = {
             compilationID:projectObj.project.projectInfo.compilation,
             datas:[]
         };
@@ -95,21 +116,31 @@ let BlockController = {
             }
         }
         if(firstNode.sourceType == projectObj.project.Bills.getSourceType()){
-            blockOjb.firstNodeType = firstNode.data.type;
+            // 特殊处理大项费用,因为DXFY在billType中为1,与blockType中的RATION冲突了
+            blockObj.firstNodeType = firstNode.data.type === billType.DXFY ? blockType.DXFY : firstNode.data.type;
         }else if(firstNode.sourceType == projectObj.project.Ration.getSourceType()){
-            blockOjb.firstNodeType = blockType.RATION;
+            blockObj.firstNodeType = blockType.RATION;
         }
-        blockOjb.isFBFX = projectObj.project.Bills.isFBFX(firstNode);
+        blockObj.isFBFX = projectObj.project.Bills.isFBFX(firstNode);
         let ration_glj_Map = _.groupBy(projectObj.project.ration_glj.datas, 'rationID');
         for(let cNode of copyNodes){
-            blockOjb.datas.push(this.getNodeDatas(cNode,ration_glj_Map));
+            blockObj.datas.push(this.getNodeDatas(cNode,ration_glj_Map));
         }
         let getDataTime = +new Date();
         console.log(`复制所用时间——${getDataTime - startTime}`);
-        blockOjb.copyTime = +new Date();//设置复制时间,可以用来做过期处理
-        setLocalCache('project_block',JSON.stringify(blockOjb));
+        blockObj.copyTime = +new Date();//设置复制时间,可以用来做过期处理
+        setLocalCache('project_block',JSON.stringify(blockObj));
         let endTime = +new Date();
         console.log(`复制所用时间——${endTime - startTime}`);
+        return blockObj;
+    },
+    copyBlock:function (selection) {
+        const block = this.extractBlockData(selection);
+        setLocalCache('project_block', JSON.stringify(block));
+    },
+    recycleBlock: function (selection) {
+        const block = this.extractBlockData(selection);
+        setLocalCache('project_recycle', JSON.stringify(block));
     },
     getNodeDatas : function (node,ration_glj_Map) {
         let data = node.data;
@@ -147,7 +178,21 @@ let BlockController = {
         this.confirmPaste(blockData,selected,position);
         //do paste
     },
-
+    // 恢复删除节点
+    recoverBlock: function () {
+        const recycle = JSON.parse(getLocalCache('project_recycle'));
+        const firstData = recycle.datas[0];
+        const parentID = firstData.sourceType === projectObj.project.Bills.getSourceType() ? firstData.ParentID : firstData.billsItemID;
+        // 恢复到父节点最末子项
+        if (parentID !== -1) {
+            const parent = projectObj.project.mainTree.findNode(parentID);
+            this.confirmPaste(recycle, parent, 'sub');
+        } else {
+            // 恢复到最末根节点后项
+            const lastRoot = projectObj.project.mainTree.roots[projectObj.project.mainTree.roots.length - 1];
+            this.confirmPaste(recycle, lastRoot, 'next');
+        }
+    },
     initOptionDiv:function (selected,blockData) {
         let Bills = projectObj.project.Bills;
         if( blockData.firstNodeType == blockType.RATION){//如果剪切板中是复制的定额块
@@ -344,7 +389,9 @@ let BlockController = {
         let sels = projectObj.mainController.sheet.getSelections();
         //设置选中并更新下方显示
         projectObj.mainController.setTreeSelected(firstNode);
-        projectObj.mainController.sheet.setSelection(firstNode.serialNo(), sels[0].col, 1, 1);
+        const serialNo = firstNode.serialNo();
+        projectObj.mainController.sheet.setSelection(serialNo, sels[0].col, 1, 1);
+        projectObj.mainController.sheet.showRow(serialNo, GC.Spread.Sheets.VerticalPosition.center);
         return rationNodes;
 
         function loadTreeNode(parentID,nextID,data,type) {
@@ -552,7 +599,7 @@ let BlockController = {
             let newID = uuid.v1(); //新的清单ID
             billsIDMap[temData.ID] = newID;
             temData.ID = newID; //新的清单ID
-            if(temData.code.length == 12){//是从清单库来的  //temData.billsLibId && temData.billsLibId!=""    2019-09-03  修改需求,不做清单库ID判断
+            if(temData.code && temData.code.length == 12){//是从清单库来的  //temData.billsLibId && temData.billsLibId!=""    2019-09-03  修改需求,不做清单库ID判断
                 let value = temData.code.substr(0,9);
                 if (value&&value.length === 9) {//&& /^[\d]+$/.test(value) 去掉全数字判断    07-31 zhang
                     temData.code = me.newFormatCode(value);

+ 2 - 0
web/building_saas/main/js/models/bills.js

@@ -787,6 +787,8 @@ var Bills = {
             updateData.user_id = userID;
             $.bootstrapLoading.start();
             CommonAjax.post("/bills/multiDelete", updateData, function () {
+                // 回收删除节点
+                BlockController.recycleBlock(selection);
                 let quantity_detail_datas = project.quantity_detail.datas;
                 let ration_datas = project.Ration.datas;
                 let nodes = controller.tree.nodes;

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

@@ -1589,7 +1589,6 @@ let calcBase = {
             if(!cbTools.isDef(exp)){
                 throw '表达式不正确';
             }
-
             //输入式转换表达式
             let compileExp = $CBP.toCompileExpr(exp);
             //计算

+ 35 - 29
web/building_saas/main/js/models/calc_program.js

@@ -796,6 +796,7 @@ let calcTools = {
             quantity: treeNode.data.quantity,
             calcBase: treeNode.data.calcBase,
             calcBaseValue: treeNode.data.calcBaseValue,
+            tenderCalcBaseValue: treeNode.data.tenderCalcBaseValue,
             programID: treeNode.data.programID,
             marketUnitFee: treeNode.data.marketUnitFee,
             marketTotalFee: treeNode.data.marketTotalFee,
@@ -840,7 +841,8 @@ let calcTools = {
         if (this.isBill(treeNode))                 // 清单只有一个工程量,没有调整后工程量。
             return this.uiNodeQty(treeNode)
         else{
-            let qCoe = (treeNode.data.rationQuantityCoe == undefined || treeNode.data.rationQuantityCoe == 0) ? 1 : treeNode.data.rationQuantityCoe;
+            let qCoe = (treeNode.data.rationQuantityCoe == undefined || treeNode.data.rationQuantityCoe == null ||
+                treeNode.data.rationQuantityCoe == 0) ? 1 : treeNode.data.rationQuantityCoe;
             treeNode.data.tenderQuantity = (this.uiNodeQty(treeNode) * qCoe).toDecimal(decimalObj.decimal("quantity", treeNode));
             return treeNode.data.tenderQuantity;
         }
@@ -852,38 +854,42 @@ let calcTools = {
         };
 
         let qCoe = 1;
-        if (gljType.LABOUR == glj.type){
-            if (treeNode.data.quantityCoe.labour)
-                qCoe = treeNode.data.quantityCoe.labour;
-        }
-        else if (baseMaterialTypes.indexOf(glj.type)){
-            if (treeNode.data.quantityCoe.material)
-                qCoe = treeNode.data.quantityCoe.material;
-        }
-        else if (baseMachineTypes.indexOf(glj.type)){
-            if (treeNode.data.quantityCoe.machine)
-                qCoe = treeNode.data.quantityCoe.machine;
-        }
-        else if (gljType.MAIN_MATERIAL == glj.type){
-            if (treeNode.data.quantityCoe.main)
-                qCoe = treeNode.data.quantityCoe.main;
+        let projGLJ = calcTools.getProjectGLJ(glj);
+        if (projGLJ.is_adjust_price != 1) {      // 先检查项目工料机里,该工料机是否参与调价
+            if (gljType.LABOUR == glj.type){
+                if (treeNode.data.quantityCoe.labour)
+                    qCoe = treeNode.data.quantityCoe.labour;
+            }
+            else if (baseMaterialTypes.indexOf(glj.type)){
+                if (treeNode.data.quantityCoe.material)
+                    qCoe = treeNode.data.quantityCoe.material;
+            }
+            else if (baseMachineTypes.indexOf(glj.type)){
+                if (treeNode.data.quantityCoe.machine)
+                    qCoe = treeNode.data.quantityCoe.machine;
+            }
+            else if (gljType.MAIN_MATERIAL == glj.type){
+                if (treeNode.data.quantityCoe.main)
+                    qCoe = treeNode.data.quantityCoe.main;
+            }
+            else if (gljType.EQUIPMENT == glj.type){
+                if (treeNode.data.quantityCoe.equipment)
+                    qCoe = treeNode.data.quantityCoe.equipment;
+            };
         }
-        else if (gljType.EQUIPMENT == glj.type){
-            if (treeNode.data.quantityCoe.equipment)
-                qCoe = treeNode.data.quantityCoe.equipment;
-        };
-
         glj.tenderQuantity = (glj.quantity * qCoe).toDecimal(decimalObj.glj.quantity);
     },
     calcGLJTenderPrice: function (glj) {
-        if (!glj.projectGLJID){   // 量价定额虚拟出来的工料机,没有projectGLJID,在项目工料机中查不到。
+        let projGLJ = calcTools.getProjectGLJ(glj);
+        if (projGLJ == null){   // 量价定额虚拟出来的工料机,在项目工料机中查不到。
+            glj.tenderPrice = projectObj.project.projectGLJ.getTenderMarketPrice(projGLJ);
+        }else{
             let pCoe = 1;
-            if (projectObj.project.property.tenderSetting && projectObj.project.property.tenderSetting.gljPriceTenderCoe)
-                pCoe = projectObj.project.property.tenderSetting.gljPriceTenderCoe;
+            if (projGLJ.is_adjust_price != 1){      // 先检查项目工料机里,该工料机是否参与调价
+                if (projectObj.project.property.tenderSetting && projectObj.project.property.tenderSetting.gljPriceTenderCoe)
+                    pCoe = projectObj.project.property.tenderSetting.gljPriceTenderCoe;
+            };
             glj.tenderPrice = (glj.marketPrice * pCoe).toDecimal(decimalObj.glj.unitPrice);
-        }else{
-            let projGLJ = calcTools.getProjectGLJ(glj);
-            glj.tenderPrice = projectObj.project.projectGLJ.getTenderMarketPrice(projGLJ);
         };
     },
     // 界面显示的工料机价格,包括定额价、市场价等。参数 price 传入一个普通的价格数值即可。
@@ -1902,11 +1908,11 @@ class CalcProgram {
                     calcItem.unitFee = (eval(calcItem.compiledExpr) * feeRate * 0.01).toDecimal(decimalObj.decimal('unitPrice', treeNode));
                     calcItem.totalFee = (calcItem.unitFee * calcTools.uiNodeQty(treeNode)).toDecimal(decimalObj.decimal('totalPrice', treeNode));
 
-                    if (tender == tenderTypes.ttCalc) {
+                    // if (tender == tenderTypes.ttCalc) {
                         let tExpr = analyzer.getCompiledTenderExpr(calcItem.compiledExpr);
                         calcItem.tenderUnitFee = (eval(tExpr) * feeRate * 0.01).toDecimal(decimalObj.decimal('unitPrice', treeNode));
                         calcItem.tenderTotalFee = (calcItem.tenderUnitFee * treeNode.data.tenderQuantity).toDecimal(decimalObj.decimal('totalPrice', treeNode));
-                    };
+                    // };
 
                     if (calcItem.fieldName) {
                         fnArr.push(calcItem.fieldName);

+ 1 - 1
web/building_saas/main/js/models/exportSEIInterface.js

@@ -30,7 +30,7 @@ async function exportSEI(projectID) {
 
     let tenderProjects = [];
     let result = await ajaxPost("/project/getSEIProjects",{projectID:projectID});
-    let pr = new SCComponent.InitProgressBar($('#progress'), $('#progress-title'), $('#progress-content'), $('#progressBar'));
+    let pr = new SCComponent.InitProgressBar();
     pr.start('导出数据接口', '正在导出文件,请稍候……');
     console.log(result);
     let project = getProject(result);

+ 63 - 39
web/building_saas/main/js/models/exportStandardInterface.js

@@ -173,7 +173,7 @@ const XMLStandard = (function () {
             this.constraints = {
                 engCode: [],    //单项工程编号
                 tenderCode: [], //单位工程编号
-                billsCode: [],  //清单项目项目编码(修改成了建设项目下唯一)
+                billsCode: [],  //清单项目项目编码(修改成了建设项目下唯一,xsd中没体现,通过跟pm沟通得知)
             };
             XML_EXPORT_BASE.Element.call(this, '标段', attrs);
         }
@@ -237,7 +237,7 @@ const XMLStandard = (function () {
         function SystemInfo(source) {
             let attrs = [
                 {name: 'ID1', value: source.softInfo, required: true},
-                {name: 'ID2', value: '', required: true},
+                {name: 'ID2', value: _util.generateHardwareId(), required: true},
                 {name: '生成时间', value: source.generatedTime, type: _config.TYPE.DATE_TIME, required: true},
             ];
             XML_EXPORT_BASE.Element.call(this, '系统信息', attrs);
@@ -301,14 +301,14 @@ const XMLStandard = (function () {
                     '土建工程', '装饰工程', '安装工程', '市政工程', '园林绿化工程',
                     '仿古建筑工程', '房修工程', '轨道工程', '构筑物工程', '机械(爆破)土石方',
                     '围墙工程', '幕墙工程', '市政安装工程', '城市轨道交通安装', '人工土石方',
-                    '房屋安装修缮工程', '房屋修缮单拆除'
+                    '房屋安装修缮工程', '房屋修缮单拆除', '城市地下综合管廊', '城市管线迁改'
                 ]},
                 {name: '金额', value: exportKind === _config.EXPORT_KIND.Tender
                     ? source.summaryInfo.engineeringCost
                     : '0', type: _config.TYPE.NUM2},
                 {name: '定额库编码', value: source.defaultRationLibCode, enumeration: [
                     'CQ18TJ', 'CQ18ZS', 'CQ18AZ', 'CQ18SZ', 'CQ18YL', 'CQ18FG', 'CQ18GD', 'CQ18FS',
-                    'CQ18LS', 'CQ18GZW', 'CQ18BP', 'CQ18ZP'
+                    'CQ18LS', 'CQ18GZW', 'CQ18BP', 'CQ18ZP', 'CQ18GL', 'CQ18GX'
                 ]},
                 {name: '计税方法', value: TAX[source.taxType], required: true, enumeration: ['营业税', '一般计税', '简易计税']},
             ];
@@ -794,7 +794,7 @@ const XMLStandard = (function () {
         function DifferentiaGljDetail(adjustType, source) {
             let attrs = [
                 //getGljCode(source.id)
-                {name: '关联材料号', value: source.code, minLen: 1, maxLen: 20, whiteSpace: _config.WHITE_SPACE.COLLAPSE, required: true},
+                {name: '关联材料号', value: source.relCode, minLen: 1, maxLen: 20, whiteSpace: _config.WHITE_SPACE.COLLAPSE, required: true},
                 {name: '名称', value: source.name, minLen: 1, maxLen: 255, whiteSpace: _config.WHITE_SPACE.COLLAPSE, required: true},
                 {name: '规格', value: source.specs, maxLen: 255, whiteSpace: _config.WHITE_SPACE.COLLAPSE},
                 {name: '单位', value: source.unit, minLen: 1, maxLen: 20, whiteSpace: _config.WHITE_SPACE.COLLAPSE, required: true},
@@ -821,7 +821,7 @@ const XMLStandard = (function () {
         function ExponentialGljDetail(adjustType, source) {
             let attrs = [
                 // getGljCode(source.id)
-                {name: '关联材料号', value: source.code, minLen: 1, maxLen: 20, whiteSpace: _config.WHITE_SPACE.COLLAPSE, required: true},
+                {name: '关联材料号', value: source.relCode, minLen: 1, maxLen: 20, whiteSpace: _config.WHITE_SPACE.COLLAPSE, required: true},
                 {name: '名称', value: source.name, minLen: 1, maxLen: 255, whiteSpace: _config.WHITE_SPACE.COLLAPSE, required: true},
                 {name: '规格', value: source.specs, maxLen: 255, whiteSpace: _config.WHITE_SPACE.COLLAPSE},
                 {name: '变值权重B', value: adjustType === _config.ADJUST_TYPE.coe ? source.varWeight : '0', type: _config.TYPE.DECIMAL, required: true},
@@ -891,9 +891,9 @@ const XMLStandard = (function () {
             XML_EXPORT_BASE.Element.call(this, '暂估价材料表', []);
         }
         //材料明细定义
-        function MaterialDetail(parentName, source) {
+        function MaterialDetail(source) {
             let attrs = [
-                {name: '序号', value: parentName === 'evalEstimateMaterial' ? source.orgCode : source.serialNo, minLen: 1, maxLen: 20, whiteSpace: _config.WHITE_SPACE.COLLAPSE, required: true},
+                {name: '序号', value: source.seq, minLen: 1, maxLen: 20, whiteSpace: _config.WHITE_SPACE.COLLAPSE, required: true},
                 {name: '关联材料号', value: source.code, minLen: 1, maxLen: 20, whiteSpace: _config.WHITE_SPACE.COLLAPSE},
                 {name: '材料名称', value: source.name, minLen: 1, maxLen: 255, whiteSpace: _config.WHITE_SPACE.COLLAPSE, required: true},
                 {name: '规格型号', value: source.specs, maxLen: 255},
@@ -901,10 +901,10 @@ const XMLStandard = (function () {
                 {name: '数量', value: exportKind === _config.EXPORT_KIND.Tender ? source.quantity : '0', type: _config.TYPE.DECIMAL},
                 {name: '单价', value: source.marketPrice, type: _config.TYPE.DECIMAL},
                 {name: '合价', value: exportKind === _config.EXPORT_KIND.Tender ? source.totalPrice : '0', type: _config.TYPE.NUM2},
-                {name: '产地', value: source.originPlace, maxLen: 255},
-                {name: '厂家', value: source.vender, maxLen: 255},
-                {name: '品牌', value: source.brand, maxLen: 255},
-                {name: '质量等级', value: source.qualityGrace, maxLen: 255},
+                {name: '产地', value: exportKind === _config.EXPORT_KIND.Tender ? source.originPlace : '', maxLen: 255},
+                {name: '厂家', value: exportKind === _config.EXPORT_KIND.Tender ? source.vender : '', maxLen: 255},
+                {name: '品牌', value: exportKind === _config.EXPORT_KIND.Tender ? source.brand : '', maxLen: 255},
+                {name: '质量等级', value: exportKind === _config.EXPORT_KIND.Tender ? source.qualityGrace : '', maxLen: 255},
                 {name: '备注', value: source.remark, maxLen: 255},
             ];
             XML_EXPORT_BASE.Element.call(this, '材料明细', attrs);
@@ -979,7 +979,6 @@ const XMLStandard = (function () {
         async function loadProject(projectData) {
             // 标记自检提示的开始(一次性多出多个文件类型,会导出多次)
             _failList.push(_config.HINT_START);
-            console.log(projectData);
             //标段
             let project = new Project({
                 basicInformation: projectData.property.basicInformation,
@@ -1023,6 +1022,7 @@ const XMLStandard = (function () {
             }
             //主要清单汇总、评审材料汇总 排在后面
             project.children = [...project.children.slice(2), mainBillsSummaryEle, appraisalSummary];
+            console.log(project);
             return [{
                 data: project,
                 exportKind: exportKind,
@@ -1140,19 +1140,26 @@ const XMLStandard = (function () {
             if (gljSumarryInfo) {
                 //投标导出承包人材料相关
                 if (exportKind === _config.EXPORT_KIND.Tender) {
+                    let projectGLJList = tenderDetail.projectGLJ.datas.gljList;
+                    let contractorList = tenderDetail.contractor_list.datas;
+                    let decimalObj = tenderDetail.projectInfo.property.decimal;
                     let adjustType = tenderData.property.gljAdjustType || _config.ADJUST_TYPE.info;
                     //承包人材料差额法表
-                    let diffGljs = materialAdjustObj.getPirceInfoDatas(tenderDetail.projectGLJ.datas.gljList);
+                    let diffGljs = materialAdjustObj.getPirceInfoDatas(projectGLJList, contractorList, decimalObj);
                     if (diffGljs.length) {
                         let diffP = new DifferentiaGlj();
-                        diffGljs.forEach(data => diffP.children.push(new DifferentiaGljDetail(adjustType, data)));
+                        diffGljs.forEach(data => {
+                            const relGLJ = _util.getRelGLJ(allGljs, data.projectGLJID);
+                            data.relCode = relGLJ ? relGLJ.code : '';
+                            diffP.children.push(new DifferentiaGljDetail(adjustType, data))
+                        });
                         tender.children.push(diffP);
                     }
                     //承包人材料指数法表
                     let engineeringCostNode = tenderDetail.Bills.tree.roots.find(node => node.getFlag() === fixedFlag.ENGINEERINGCOST);
                     if (engineeringCostNode) {
                         let ecTotalFee = _util.getFee(engineeringCostNode.data.fees, 'common.totalFee');
-                        let exponentialGljs = materialAdjustObj.getPriceCoeDatas(tenderDetail.projectGLJ.datas.gljList, ecTotalFee);
+                        let exponentialGljs = materialAdjustObj.getPriceCoeDatas(projectGLJList, contractorList, ecTotalFee, decimalObj);
                         if (exponentialGljs.length) {
                             // 变值权重B累加
                             let totalVarWeight = exponentialGljs.reduce((acc, cur) => acc += cur.varWeight || 0, 0);
@@ -1160,6 +1167,8 @@ const XMLStandard = (function () {
                             let fixedWeight = adjustType === _config.ADJUST_TYPE.coe ? 1 - totalVarWeight : '1';
                             let exP = new ExponentialGlj(fixedWeight);
                             exponentialGljs.forEach(data => {
+                                const relGLJ = _util.getRelGLJ(allGljs, data.projectGLJID);
+                                data.relCode = relGLJ ? relGLJ.code : '';
                                 exP.children.push(new ExponentialGljDetail(adjustType, data));
                             });
                             tender.children.push(exP);
@@ -1259,8 +1268,8 @@ const XMLStandard = (function () {
                     feature = [];
                 let jobText = node.data.jobContentText || '';
                 let featureText = node.data.itemCharacterText || '';
-                let jobSplit = jobText.split(/[\r,\n]/g),
-                    featureSplit = featureText.split(/[\r,\n]/g);
+                let jobSplit = jobText.split(/[\r\n]/g),
+                    featureSplit = featureText.split(/[\r\n]/g);
                 //将特征和内容分类,通过[项目特征][工作内容]区分。若没有,则不区分:工作内容列全部数据为工作内容,项目特征列全部数据为项目特征
                 //@param {Array}data(splitdata) {String}field(job、feature)
                 function classifyData(data, field) {
@@ -1391,14 +1400,16 @@ const XMLStandard = (function () {
                 //定额人材机排序
                 rationGljData = gljUtil.sortRationGLJ(rationGljData);
                 for (let rGlj of rationGljData) {
-                    if (skipGLJTypes.includes(rGlj.type)) {
+                    const totalQuantity = gljUtil.getTotalQuantity(rGlj,  rationData, decimal.glj.quantity, decimal.ration.quantity);
+                    const parsedTotalQuantity = parseFloat(totalQuantity);
+                    if (skipGLJTypes.includes(rGlj.type) || !parsedTotalQuantity) {
                         continue;
                     }
                     let gljSource = {
                         code: rGlj.code,
                         //code: getGljCode(rGlj.projectGLJID),
                         quantity: rGlj.quantity,
-                        totalQuantity: gljUtil.getTotalQuantity(rGlj,  rationData, decimal.glj.quantity, decimal.ration.quantity)
+                        totalQuantity
                     };
                     let gljContent = new GljContent(gljSource);
                     gljAnalyze.children.push(gljContent);
@@ -1898,8 +1909,7 @@ const XMLStandard = (function () {
          * */
         function loadGlj(appraisalSummary, detail) {
             let gljList = detail.projectGLJ.datas.gljList;
-            let evalBidSeq = 1,    //评标序号
-                evalEstSeq = 1;    //暂估序号
+            // 人材机提取数据映射 id-数据映射
             if (gljList.length > 0) {
                 //创建人材机汇总节点
                 let gljSummary = new GljSummary();
@@ -1929,6 +1939,7 @@ const XMLStandard = (function () {
                     '5': '400', //为“商品砼”、商品砂浆”时,取“400”。
                     '6': '400'
                 };
+                // 人材机汇总
                 for (let glj of allGljs) {
                     // 企业管理费、利润、一般风险费不导出
                     if (skipGLJTypes.includes(glj.type)) {
@@ -1994,26 +2005,39 @@ const XMLStandard = (function () {
                         }
                     }
                     gljSummary.children.push(gljEle);
-                    gljSource.totalPrice = scMathUtil.roundForObj(gljSource.quantity * gljSource.marketPrice,
-                        detail.projectInfo.property.decimal.bills.totalPrice);
-                    //评标和暂估材料表下的材料明细中的人材机代码,要求单位工程内唯一,由于人材机汇总也有这个限制,所以
-                    // 这里不再处理
-                    if (glj.is_eval_material) { //评标
-                        gljSource.serialNo = evalBidSeq++;
-                        evalBidMaterial.children.push(new MaterialDetail('evalBidMaterial', gljSource));
-                        //给建设项目下的评审材料汇总设置明细数据,这里需要检测代码(编号)唯一性,因为是汇总所有单位工程的,要求所有单位工程内评审材料代码唯一
-                        appraisalSummary.children.push(new AppraisalDetail(gljSource));
-                        //_util.checkUnique(curTenderEle.constraints.appraisalDetailCode, gljSource.code, '评审材料明细代码', gljSource.orgCode);
-                        _util.checkUnique(curTenderEle.constraints.detailCode, gljSource.code, '材料明细关联材料号', gljSource.orgCode);
-                    }
-                    if (glj.is_evaluate) {  //暂估
-                        gljSource.serialNo = evalEstSeq++;
-                        evalEstimateMaterial.children.push(new MaterialDetail('evalEstimateMaterial', gljSource));
-                        _util.checkUnique(curTenderEle.constraints.detailCode, gljSource.code, '材料明细关联材料号', gljSource.orgCode);
-                    }
                 }
+                const projectGLJList = detail.projectGLJ.datas.gljList;
+                const bidEvaluationList = detail.bid_evaluation_list.datas;
+                const evaluateList = detail.evaluate_list.datas;
+                const decimalObj = detail.projectInfo.property.decimal;
+                // 评标材料
+                const bidEvaluationDetail = loadMaterialDetail(configMaterialObj.getBidMaterialDatas(projectGLJList, bidEvaluationList, decimalObj));
+                evalBidMaterial.children.push(...bidEvaluationDetail);
+                // 给建设项目下的评审材料汇总设置明细数据 //code name specs unit marketPrice
+                const appraisalSummaryDetail = bidEvaluationDetail.map(ele => {
+                    const attrObj = _util.getPlainAttrs(ele);
+                    const src = {
+                        code: attrObj['关联材料号'],
+                        name: attrObj['材料名称'],
+                        specs: attrObj['规格型号'],
+                        unit: attrObj['计量单位'],
+                        marketPrice: attrObj['单价']
+                    };
+                    return new AppraisalDetail(src);
+                });
+                appraisalSummary.children.push(...appraisalSummaryDetail);
+                // 暂估材料
+                const evaluationDetail = loadMaterialDetail(configMaterialObj.getEvaluateMaterialDatas(projectGLJList, evaluateList, decimalObj));
+                evalEstimateMaterial.children.push(...evaluationDetail);
                 return {gljSummary, evalBidMaterial, evalEstimateMaterial};
             }
+            // 加载材料明细
+            function loadMaterialDetail(datas) {
+                return datas.map(data => {
+                    _util.checkUnique(curTenderEle.constraints.detailCode, data.code, '材料明细关联材料号', data.code);
+                    return new MaterialDetail(data);
+                });
+            }
         }
         /*
          * 加载清单综合单价计算程序

+ 132 - 64
web/building_saas/main/js/models/exportStdInterfaceBase.js

@@ -121,9 +121,9 @@ const XML_EXPORT_BASE = (() => {
         _cache.failList.push(...this.fail);
     }
     /*
-    * xml字符实体的处理,这些特殊字符不处理会导致xml文件格式出错:""、<>、&
-    * 要先处理&amp
-    * */
+     * xml字符实体的处理,这些特殊字符不处理会导致xml文件格式出错:""、<>、&
+     * 要先处理&amp
+     * */
     let _xmlEntity = {
         '&': '&amp;',
         '\n': '&#xA;',
@@ -172,16 +172,18 @@ const XML_EXPORT_BASE = (() => {
                     : ''
                 : String(data.value);
             if (data.whiteSpace && data.whiteSpace === WHITE_SPACE.COLLAPSE) {  //处理空格相关
-                data.value = data.value.replace(/[\r,\n,\t]/g, ' ');
+                data.value = data.value.replace(/[\r\n\t]/g, ' ');
                 data.value = data.value.trim();
                 data.value = data.value.replace(/\s{1,}/g, ' ');
             }
             /*if (!data.value && !data.minLen && !data.enumeration) {  //值为空,且没有限制最小字符数,且没有限制值,则不需判断
-                rst.filterAttrs.push(data);
-                continue;
-            }*/
-            let isFail = false,
-                tempFail = '';
+             rst.filterAttrs.push(data);
+             continue;
+             }*/
+            let isFail = false;
+            let tempFail = '';
+            // 提示的名称需要处理,有的接口xml属性为英文,需要显示其展示名称
+            const name = data.dName || data.name;
             if (data.minLen && data.value.length < data.minLen){
                 isFail = true;
                 tempFail = data.failHint
@@ -309,17 +311,27 @@ const XML_EXPORT_BASE = (() => {
     function isDef(v) {
         return typeof v !== 'undefined' && v !== null;
     }
-    // v是否有值,不为undefined、null、''
     function hasValue(v) {
+        // v是否有值,不为undefined、null、''
         return typeof v !== 'undefined' && v !== null && v !== '';
     }
     /*
-    * 从fees数组中获取相关费用
-    * @param  {Array}fees 费用数组
-    *         {String}feeFields 费用字段
-    * @return {Number}
-    * @example getFee(source.fees, 'common.totalFee')
-    * */
+     * 将节点属性数据(attr数组)转换成简单key-value数据
+     * @param  {Object}ele 元素节点数据Element实例
+     * @return {Object}
+     * */
+    function getPlainAttrs(ele) {
+        const obj = {};
+        ele.attrs.forEach(attr => obj[attr.name] = attr.value);
+        return obj;
+    }
+    /*
+     * 从fees数组中获取相关费用
+     * @param  {Array}fees 费用数组
+     *         {String}feeFields 费用字段
+     * @return {Number}
+     * @example getFee(source.fees, 'common.totalFee')
+     * */
     function getFee(fees, feeFields) {
         if (!Array.isArray(fees)) {
             return 0;
@@ -332,12 +344,12 @@ const XML_EXPORT_BASE = (() => {
         return fee[fields[1]] || 0;
     }
     /*
-    * 根据key获取对应的基本信息、工程特征数据
-    * @param  {Array}data
-    *         {String}key
-    * @return {String}
-    * @example getValueByKey(source.basicInformation, 'projectScale')
-    * */
+     * 根据key获取对应的基本信息、工程特征数据
+     * @param  {Array}data
+     *         {String}key
+     * @return {String}
+     * @example getValueByKey(source.basicInformation, 'projectScale')
+     * */
     function getValueByKey(data, key) {
         for (let d of data) {
             if (d.key === key) {
@@ -352,6 +364,59 @@ const XML_EXPORT_BASE = (() => {
         }
         return '';
     }
+    // 获取关联材料
+    function getRelGLJ(allGLJs, gljId) {
+        return allGLJs.find(glj => glj.id === gljId);
+    }
+    // 随机生成机器信息码:CPU信息;硬盘序列号;mac地址;
+    // 保存在localStorage中
+    function generateHardwareId() {
+        const hardwareCacheId = window.localStorage.getItem('hardwareId');
+        if (hardwareCacheId) {
+            return hardwareCacheId;
+        }
+        const charList = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
+            'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
+            'V', 'W', 'X', 'Y', 'Z'];
+        function generateCpuId() {
+            let id = '';
+            let count = 16;
+            while (count--) {
+                const randomIdx = parseInt(Math.random() * 16);
+                id += charList[randomIdx];
+            }
+            return id;
+        }
+        function generateDiskId() {
+            let id = '';
+            let count = 8;
+            while (count--) {
+                const randomIdx = parseInt(Math.random() * 36);
+                id += charList[randomIdx];
+            }
+            return id;
+        }
+        function generateMacId() {
+            const idList = [];
+            let outerCount = 6;
+            while (outerCount--) {
+                let tempId = '';
+                let innerCount = 2;
+                while (innerCount--) {
+                    const randomIdx = parseInt(Math.random() * 16);
+                    tempId += charList[randomIdx];
+                }
+                idList.push(tempId);
+            }
+            return idList.join('-');
+        }
+        const cpuId = generateCpuId();
+        const diskId = generateDiskId();
+        const macId = generateMacId();
+        const hardwareId = [cpuId, diskId, macId].join(';');
+        window.localStorage.setItem('hardwareId', hardwareId);
+        return hardwareId;
+    }
     // 数组打平成对象
     function arrayToObj(arr) {
         let rst = {};
@@ -361,11 +426,11 @@ const XML_EXPORT_BASE = (() => {
         return rst;
     }
     /*
-    * 检测层数
-    * @param  {Number}maxDepth(最大深度)
-    *         {Object}node(需要检测的清单树节点)
-    * @return {Boolean}
-    * */
+     * 检测层数
+     * @param  {Number}maxDepth(最大深度)
+     *         {Object}node(需要检测的清单树节点)
+     * @return {Boolean}
+     * */
     function validDepth(maxDepth, node) {
         let nodeDepth = node.depth();
         let allNodes = node.getPosterity();
@@ -379,13 +444,13 @@ const XML_EXPORT_BASE = (() => {
         return true;
     }
     /*
-    * 检测唯一性,有些属性在规定的数据范围内唯一
-    * @param  {Object}constraints(约束池)
-    *         {All}data(检测的数据)
-    *         {String}hint(提示已存在的内容)
-    *         {String}subHint(额外提示,有额外提示时,不用data提示)
-    * @return {void}
-    * */
+     * 检测唯一性,有些属性在规定的数据范围内唯一
+     * @param  {Object}constraints(约束池)
+     *         {All}data(检测的数据)
+     *         {String}hint(提示已存在的内容)
+     *         {String}subHint(额外提示,有额外提示时,不用data提示)
+     * @return {void}
+     * */
     function checkUnique(constraints, data, hint, subHint) {
         if (constraints.includes(data)) {
             let failHint = subHint
@@ -428,12 +493,12 @@ const XML_EXPORT_BASE = (() => {
     }
 
     /*
-    * 根据粒度获取项目(不包含详细数据)数据
-    * @param  {Number}granularity 导出粒度
-    *         {Number}tenderID 单位工程ID
-    *         {String}userID 用户ID
-    * @return {Object} 返回的数据结构:{children: [{children: []}]} 最外层为建设项目,中间为单项工程,最底层为单位工程
-    * */
+     * 根据粒度获取项目(不包含详细数据)数据
+     * @param  {Number}granularity 导出粒度
+     *         {Number}tenderID 单位工程ID
+     *         {String}userID 用户ID
+     * @return {Object} 返回的数据结构:{children: [{children: []}]} 最外层为建设项目,中间为单项工程,最底层为单位工程
+     * */
     async function getProjectByGranularity(granularity, tenderID, userID) {
         let projectData = _cache.projectData;
         // 没有数据,需要拉取
@@ -445,11 +510,11 @@ const XML_EXPORT_BASE = (() => {
     }
 
     /*
-    * 通过getData接口获取单位工程详细数据
-    * @param  {Number}tenderID 单位工程ID
-    *         {String}userID 用户ID
-    * @return {Object} 跟projectObj.project的数据结构一致
-    * */
+     * 通过getData接口获取单位工程详细数据
+     * @param  {Number}tenderID 单位工程ID
+     *         {String}userID 用户ID
+     * @return {Object} 跟projectObj.project的数据结构一致
+     * */
     async function getTenderDetail(tenderID, userID) {
         // 获取单位工程详细数据
         let tenderDetail = _cache.tenderDetailMap[tenderID];
@@ -647,15 +712,15 @@ const XML_EXPORT_BASE = (() => {
         return srcEle.children.filter(ele => ele.name === eleName);
     }
     /*
-    * 设置完工程编号后,更新原始数据的工程编号
-    * 更新原始数据前需要将编号里的特殊字符进行转换
-    * @param  {Array}exportData 提取出来的需要导出的数据
-    *         {Array}codes 工程编号表中填写的工程编号
-    *         {String}EngineeringName 单项工程元素的名称
-    *         {String}tenderName 单位工程元素的名称
-    *         {String}codeName 编号属性的名称
-    * @return {void}
-    * */
+     * 设置完工程编号后,更新原始数据的工程编号
+     * 更新原始数据前需要将编号里的特殊字符进行转换
+     * @param  {Array}exportData 提取出来的需要导出的数据
+     *         {Array}codes 工程编号表中填写的工程编号
+     *         {String}EngineeringName 单项工程元素的名称
+     *         {String}tenderName 单位工程元素的名称
+     *         {String}codeName 编号属性的名称
+     * @return {void}
+     * */
     function setupCode(exportData, codes, EngineeringName, tenderName, codeName) {
         // 转换xml实体字符
         let parsedCodes = getParsedData(codes);
@@ -679,7 +744,10 @@ const XML_EXPORT_BASE = (() => {
         hasValue,
         setTimeoutSync,
         getFee,
+        getPlainAttrs,
         getValueByKey,
+        getRelGLJ,
+        generateHardwareId,
         arrayToObj,
         validDepth,
         checkUnique,
@@ -758,15 +826,15 @@ const XML_EXPORT_BASE = (() => {
     }
 
     /*
-    * 根据各自费用定额的文件结构,导出文件
-    * 每个费用定额可能导出的结果文件都不同
-    * 比如广东18需要将一个建设项目文件,多个单位工程文件打包成一个zip文件。重庆18就没这种要求
-    * @param  {Array}codes 工程编号数据
-    *         {Array}extractData 提取的数据
-    *         {Function}setCodeFunc 各自费用定额的导出前重设用户输入的工程编号方法
-    *         {Function}saveAsFunc 各自费用定额的导出方法
-    * @return {void}
-    * */
+     * 根据各自费用定额的文件结构,导出文件
+     * 每个费用定额可能导出的结果文件都不同
+     * 比如广东18需要将一个建设项目文件,多个单位工程文件打包成一个zip文件。重庆18就没这种要求
+     * @param  {Array}codes 工程编号数据
+     *         {Array}extractData 提取的数据
+     *         {Function}setCodeFunc 各自费用定额的导出前重设用户输入的工程编号方法
+     *         {Function}saveAsFunc 各自费用定额的导出方法
+     * @return {void}
+     * */
     async function exportFile(codes, extractData, setCodeFunc, saveAsFunc) {
         // 编号重置后将会被导出,需要将编号进行xml字符实体转换
         codes = getParsedData(codes);
@@ -789,7 +857,7 @@ const XML_EXPORT_BASE = (() => {
         // 导出
         await saveAsFunc(fileData);
     }
-    
+
     return {
         CONFIG,
         CACHE,

Разница между файлами не показана из-за своего большого размера
+ 1030 - 0
web/building_saas/main/js/models/exportStdInterface_gd18.js


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

@@ -199,9 +199,12 @@ var FeeRate = {
                     calcProgramObj.refreshCalcProgram(node, 3);
                 }
             }
-            project.calcProgram.calcAllNodesAndSave(calcAllType.catAll);
-            project.markUpdateProject({projectID:project.ID(),feeRateID:this.getActivateFeeRateFileID()},"feeRate",function () {
-                socket.emit('feeRateChangeNotify', {projectID:project.ID(),feeRateID:me.getActivateFeeRateFileID(),userID:userID});
+            project.calcProgram.calcAllNodesAndSave(calcAllType.catAll,function () {
+                $.bootstrapLoading.start();
+                project.markUpdateProject({projectID:project.ID(),feeRateID:me.getActivateFeeRateFileID()},"feeRate",function () {
+                    $.bootstrapLoading.end();
+                    socket.emit('feeRateChangeNotify', {projectID:project.ID(),feeRateID:me.getActivateFeeRateFileID(),userID:userID});
+                });
             });
         };
 

+ 204 - 92
web/building_saas/main/js/models/importStandardInterface.js

@@ -76,7 +76,9 @@ const ImportXML = (() => {
         //住宅工程质量分户验收费
         HOUSE_QUALITY_ACCEPT_FEE:31,
         //组织措施费
-        ORGANIZATION:32
+        ORGANIZATION:32,
+        //其他措施费
+        OTHER_MEASURE_FEE:33
     };
     // 限定取值的取费类别与固定ID对应
     const FEE_TYPE_FLAG = {
@@ -153,6 +155,30 @@ const ImportXML = (() => {
         '&quot;': 'escape{quot}',
         '&apos;': 'escape{apos}'
     };
+    // 第x册定额名称包含字符与计算程序ID的匹配映射
+    const NameProgramMapping = {
+        '机械设备': 25,
+        '热力设备': 26,
+        '静置设备与工艺金属结构制作': 27,
+        '电气设备': 28,
+        '建筑智能化': 29,
+        '自动化控制仪表': 30,
+        '通风空调': 31,
+        '工业管道': 32,
+        '消防': 33,
+        '给排水、采暖、燃气': 34,
+        '刷油、防腐蚀、绝热': 35
+    };
+    // 获取定额取费专业,根据名称匹配有无固定映射,若没有则取单位工程取费专业(后台配置)
+    function getProgramID(name, mapping, projectEngineering) {
+        for (let mapName in mapping) {
+            const reg = new RegExp(`第.{1,2}册\\s*${mapName}(安装)?工程`);
+            if (name.match(reg)) {
+                return mapping[mapName];
+            }
+        }
+        return projectEngineering;
+    }
     // 避免字符实体进行转义。原文本中含有xml字符实体,转换为其他字符。
     function escapeXMLEntity(str) {
         for (let [key, value] of Object.entries(XML_ENTITY)) {
@@ -334,6 +360,7 @@ const ImportXML = (() => {
                     differentiaSummary: loadDifferentiaSummary(src) ,//承包人材料差额法表
                     exponentialSummary: loadExponentialSummary(src) ,//承包人材料指数法表
                     evalBidSummary: loadEvalBidSummary(src),    //评标材料表
+                    evalSummary: loadEvalSummary(src) // 暂估价材料表
                 };
             });
         }
@@ -379,7 +406,8 @@ const ImportXML = (() => {
                 let obj = {
                     rowCode: getValue(itemSrc, ['_行代号']),
                     name: getValue(itemSrc, ['_项目名称']),
-                    calcBase: getValue(itemSrc, ['_计算基础表达式'])
+                    calcBase: getValue(itemSrc, ['_计算基础表达式']),
+                    feeType: getValue(itemSrc, ['_费用类别'])
                 };
                 if (importFileKind === FileKind.tender) {
                     // obj.calcBase = getValue(itemSrc, ['_计算基础表达式']);
@@ -850,19 +878,16 @@ const ImportXML = (() => {
         }
         //根据费用类别、配比类比获取人材机类别(后端匹配不到标准人材机的时候用)
         function getGljTypeData(feeType, ratioType, name) {
-            let map = {
-                '-': {type: 201, shortName: '材'},
-                '1-': {type: 1, shortName: '人'},
-                '2-': {type: 201, shortName: '材'},
-                '2-1': {type: 202, shortName: '砼'},
-                '2-2': {type: 205, shortName: '商砼'},
-                '2-3': {type: 203, shortName: '浆'},
-                '2-4': {type: 206, shortName: '商浆'},
-                '2-5': {type: 204, shortName: '配比'},
-                '3-': {type: 301, shortName: '机'},
-                '4-': {type: 4, shortName: '主'},
+            // 因为这份文件不太标准,各家也不统一,没有一个靠谱的方法能准备获取人材机类型,所以有些地方需要特殊处理
+            // 有的人材机类型根据人材机名称特殊获取
+            const specialMap = {
+                '定额管理费': {type: 6, shortName: '管'}
             };
-            let nameMap = {
+            if (specialMap[name]) {
+                return specialMap[name];
+            }
+            // 一些需要特殊处理的动力材料
+            const powerNameMap = {
                 '柴油': {type: 305, shortName: '动'},
                 '柴油(机械用)': {type: 305, shortName: '动'},
                 '柴油(机械用)': {type: 305, shortName: '动'},
@@ -874,12 +899,26 @@ const ImportXML = (() => {
                 '电(机械用)': {type: 305, shortName: '动'},
                 '机上人工': {type: 303, shortName: '机人'},
             };
-            if (feeType === '3' && nameMap[name]) {
-                return nameMap[name] || null;
+            // 人、机不需要匹配配比类别
+            if (feeType === '1') {
+                return {type: 1, shortName: '人'};
+            } else if (feeType === '3') {
+                // 特殊处理动力材料
+                return powerNameMap[name] || {type: 301, shortName: '机'};
             }
+            const map = {
+                '-': {type: 201, shortName: '材'},
+                '2-': {type: 201, shortName: '材'},
+                '2-1': {type: 202, shortName: '砼'},
+                '2-2': {type: 205, shortName: '商砼'},
+                '2-3': {type: 203, shortName: '浆'},
+                '2-4': {type: 206, shortName: '商浆'},
+                '2-5': {type: 204, shortName: '配比'},
+                '4-': {type: 4, shortName: '主'},
+            };
             return map[`${feeType}-${ratioType}`] || null;
         }
-        //主要材料类别-三材类别映射、、
+        //主要材料类别-三材类别映射
         const MaterialMap = {
             '100': 1,   //钢材钢筋认为钢材,因为文件没细分
             '200': 4,   //水泥
@@ -947,8 +986,13 @@ const ImportXML = (() => {
             return gljsSrc.map(gljSrc => {
                 return {
                     code: getValue(gljSrc, ['_关联材料号']),
+                    name: getValue(gljSrc, ['_名称']),
+                    specs: getValue(gljSrc, ['_规格']),
+                    unit: getValue(gljSrc, ['_单位']),
+                    quantity: getValue(gljSrc, ['_数量']),
                     riskCoe: getValue(gljSrc, ['_风险系数']),
                     standardPrice: getValue(gljSrc, ['_基准单价']),
+                    market_price: getValue(gljSrc, ['_投标单价']),
                     remark: getValue(gljSrc, ['_备注']),
                 };
             });
@@ -959,6 +1003,8 @@ const ImportXML = (() => {
             return gljsSrc.map(gljSrc => {
                 return {
                     code: getValue(gljSrc, ['_关联材料号']),
+                    name: getValue(gljSrc, ['_名称']),
+                    specs: getValue(gljSrc, ['_规格']),
                     varWeight: getValue(gljSrc, ['_变值权重B']),
                     FO: getValue(gljSrc, ['_基本价格指数']),
                     FI: getValue(gljSrc, ['_现行价格指数']),
@@ -970,7 +1016,38 @@ const ImportXML = (() => {
         function loadEvalBidSummary(tenderSrc) {
             let gljsSrc = arrayValue(tenderSrc, ['评标材料表', '材料明细']);
             return gljsSrc.map(gljSrc => {
-                return {code: getValue(gljSrc, ['_关联材料号'])};
+                return {
+                    seq: getValue(gljSrc, ['_序号']),
+                    code: getValue(gljSrc, ['_关联材料号']),
+                    name: getValue(gljSrc, ['_材料名称']),
+                    specs: getValue(gljSrc, ['_规格型号']),
+                    unit: getValue(gljSrc, ['_计量单位']),
+                    quantity: getValue(gljSrc, ['_数量']),
+                    market_price: getValue(gljSrc, ['_单价']),
+                    // 由于数据库有这两个字段,暂时也导入(前端隐藏了,目前实际上应该是没用的,以防万一)
+                    originPlace: getValue(gljSrc, ['_产地']),
+                    vender: getValue(gljSrc, ['_厂家']),
+                    remark: getValue(gljSrc, ['_备注'])
+                };
+            });
+        }
+        // 暂估价材料
+        function loadEvalSummary(tenderSrc) {
+            const gljsSrc = arrayValue(tenderSrc, ['暂估价材料表', '材料明细']);
+            return gljsSrc.map(gljSrc => {
+                return {
+                    seq: getValue(gljSrc, ['_序号']),
+                    code: getValue(gljSrc, ['_关联材料号']),
+                    name: getValue(gljSrc, ['_材料名称']),
+                    specs: getValue(gljSrc, ['_规格型号']),
+                    unit: getValue(gljSrc, ['_计量单位']),
+                    quantity: getValue(gljSrc, ['_数量']),
+                    market_price: getValue(gljSrc, ['_单价']),
+                    // 由于数据库有这两个字段,暂时也导入(前端隐藏了,目前实际上应该是没用的,以防万一)
+                    originPlace: getValue(gljSrc, ['_产地']),
+                    vender: getValue(gljSrc, ['_厂家']),
+                    remark: getValue(gljSrc, ['_备注'])
+                }
             });
         }
 
@@ -988,26 +1065,29 @@ const ImportXML = (() => {
             );
             return isCoe ? AdjustType.coe : AdjustType.info;
         }
-        
-        let toMatches = [
-            {reg: /分部分项/, flag: fixedFlag.SUB_ENGINERRING},
-            {reg: /^措施项目/, flag: fixedFlag.MEASURE},
-            {reg: /技术措施/, flag: fixedFlag.CONSTRUCTION_TECH},
-            {reg: /组织措施/, flag: fixedFlag.CONSTRUCTION_ORGANIZATION},
-            {reg: /组织措施/, flag: fixedFlag.CONSTRUCTION_ORGANIZATION},
-            {reg: /安全文明/, flag: fixedFlag.SAFETY_CONSTRUCTION},
-            {reg: /其他项目/, flag: fixedFlag.OTHER},
-            {reg: /暂列金额/, flag: fixedFlag.PROVISIONAL},
-            {reg: /暂估价/, flag: fixedFlag.ESTIMATE},
-            {reg: /计日工/, flag: fixedFlag.DAYWORK},
-            {reg: /总承包服务/, flag: fixedFlag.TURN_KEY_CONTRACT},
-            {reg: /索赔(?:及|与|和)现场签证/, flag: fixedFlag.TURN_KEY_CONTRACT},
-            {reg: /规费/, flag: fixedFlag.CHARGE},
-            {reg: /税金/, flag: fixedFlag.TAX},
-            {reg: /增值税/, flag: fixedFlag.ADDED_VALUE_TAX},
-            {reg: /^附加税/, flag: fixedFlag.ADDITIONAL_TAX},
-            {reg: /环境保护税/, flag: fixedFlag.ENVIRONMENTAL_PROTECTION_TAX},
-            {reg: /(?:合\s*价)|(?:工程造价)/, flag: fixedFlag.ENGINEERINGCOST},
+
+        // 个软件公司确定了对应关系的其他项目、税金,使用费用类别进行匹配,其他按照名称关键字匹配
+        const matchRegs = [
+            {field: 'feeType', reg: /^1300$/, flag: fixedFlag.OTHER},
+            {field: 'feeType', reg: /^900$/, flag: fixedFlag.TAX},
+            {field: 'name', reg: /分部分项/, flag: fixedFlag.SUB_ENGINERRING},
+            {field: 'name', reg: /^措施项目/, flag: fixedFlag.MEASURE},
+            {field: 'name', reg: /技术措施/, flag: fixedFlag.CONSTRUCTION_TECH},
+            {field: 'name', reg: /组织措施/, flag: fixedFlag.CONSTRUCTION_ORGANIZATION},
+            {field: 'name', reg: /组织措施/, flag: fixedFlag.CONSTRUCTION_ORGANIZATION},
+            {field: 'name', reg: /安全文明/, flag: fixedFlag.SAFETY_CONSTRUCTION},
+           // {reg: /其他项目/, flag: fixedFlag.OTHER},
+            {field: 'name', reg: /暂列金额/, flag: fixedFlag.PROVISIONAL},
+            {field: 'name', reg: /暂估价/, flag: fixedFlag.ESTIMATE},
+            {field: 'name', reg: /计日工/, flag: fixedFlag.DAYWORK},
+            {field: 'name', reg: /总承包服务/, flag: fixedFlag.TURN_KEY_CONTRACT},
+            {field: 'name', reg: /索赔(?:及|与|和)现场签证/, flag: fixedFlag.TURN_KEY_CONTRACT},
+            {field: 'name', reg: /规费/, flag: fixedFlag.CHARGE},
+          //  {reg: /税金/, flag: fixedFlag.TAX},
+            {field: 'name', reg: /增值税/, flag: fixedFlag.ADDED_VALUE_TAX},
+            {field: 'name', reg: /^附加税/, flag: fixedFlag.ADDITIONAL_TAX},
+            {field: 'name', reg: /环境保护税/, flag: fixedFlag.ENVIRONMENTAL_PROTECTION_TAX},
+            {field: 'name', reg: /(?:合\s*价)|(?:工程造价)/, flag: fixedFlag.ENGINEERINGCOST}
         ];
         //处理单位工程费用汇总的清单,这一部分没有靠谱的规则,特殊处理并添加相关清单
         function setupFeeSummary(feeSummary, needfulTemplate) {
@@ -1043,8 +1123,8 @@ const ImportXML = (() => {
                 } else {
                     let isMatched = false;
                     //匹配固定项
-                    for (let match of toMatches) {
-                        if (!match.reg.test(feeBills.name)) {
+                    for (let match of matchRegs) {
+                        if (!match.reg.test(feeBills[match.field])) {
                             continue;
                         }
                         let findBills = needfulTemplate.find(nData => getFlag(nData) === match.flag);
@@ -1311,7 +1391,7 @@ const ImportXML = (() => {
             JSCS_JXF: '{技术措施项目定额施工机具使用费}',
             JSCS_ZCF: '{技术措施项目主材费}',
             JSCS_GR: '{技术措施项目人工工日}',
-            JZMJ: '{建筑面积}',
+            //JZMJ: '{建筑面积}',
             RCJJC: '{人材机价差}',
             RGJC: '{人工价差}',
             CLJC: '{材料价差}',
@@ -1333,10 +1413,33 @@ const ImportXML = (() => {
             SJHJ: '{税金}',
             SQGCZJ: '{税前工程造价}'
         };
+        // 检查固定清单引用的基数造成自身循环,比如分部分项部分引用了FBFXHJ
+        // 有文件的单位工程费汇总中,含有技术措施项目费清单,含有基数,且无子项。
+        function isCalcBaseCycle(bills) {
+            const flag = getFlag(bills);
+            if (!flag) {
+                return false;
+            }
+            // 跟固定清单直接相关联的基数,若相关固定清单引用了其下的基数,则造成自身循环
+            const CycleMap = {
+                [fixedFlag.SUB_ENGINERRING]: ['FBFXHJ', 'RGF', 'CLF', 'JXF', 'ZCF', 'GR'],
+                [fixedFlag.MEASURE]: ['CSXMHJ'],
+                [fixedFlag.CONSTRUCTION_ORGANIZATION]: ['ZZCSF'],
+                [fixedFlag.CONSTRUCTION_TECH]: ['JSCSF', 'JSCS_RGF', 'JSCS_CLF', 'JSCS_JXF', 'JSCS_ZCF', 'JSCS_GR'],
+                [fixedFlag.CHARGE]: ['GF'],
+                [fixedFlag.TAX]: ['SJ', 'SJHJ']
+            };
+            const match = CycleMap[flag];
+            if (!match) {
+                return false;
+            }
+            return match.some(item => bills.calcBase.match(new RegExp(`\\b${item}\\b`)));
+        }
         //转换计算基数
         //1.有子项数据,则清空基数
-        //2.行代号引用转换为ID引用
-        //3.对应字典代号转换,对应字典里找不到则设置成金额
+        //2.引用的基数造成自身循环,比如分部分项部分引用了FBFXHJ
+        //3.行代号引用转换为ID引用
+        //4.对应字典代号转换,对应字典里找不到则设置成金额
         function transformCalcBase(billsData) {
             //行代号 - ID映射
             let rowCodeMap = {};
@@ -1359,6 +1462,12 @@ const ImportXML = (() => {
                 if (typeof bills.calcBase !== 'string') {
                     bills.calcBase = String(bills.calcBase);
                 }
+                // 引用的基数造成自循环,清空基数
+                const isCycle = isCalcBaseCycle(bills);
+                if (isCycle) {
+                    bills.calcBase = '';
+                    continue;
+                }
                 //提取基数
                 bills.calcBase = bills.calcBase.replace(/\s/g, '');
                 let bases = bills.calcBase.split(/[\+\-\*\/]/g);
@@ -1485,6 +1594,8 @@ const ImportXML = (() => {
                     }
                     //提取详细数据
                     let needfulTemplate = _.cloneDeep(templateMapping[curTender.property.templateLibID]);
+                    // 重设模板树结构数据
+                    BILLS_UTIL.resetTreeData(needfulTemplate, uuid.v1);
                     let postTenderData = await transformTender(curTender, IDPlaceholder, needfulTemplate);
                     postTenderData.tender.property.rootProjectID = postConstructData.ID;
                     postEngData.tenders.push(postTenderData);
@@ -1538,6 +1649,9 @@ const ImportXML = (() => {
                 rationGLJ: [],  //定额人材机
                 rationCoe: [],   //定额系数,全为默认
                 projectGLJ: [], //项目人材机
+                contractorList: [], //承包人材料
+                bidEvaluationList: [], //评标材料
+                evaluationList: [], //暂估材料
                 unitPrice: [],  //单价文件
                 mixRatio: []    //组成物
             };
@@ -1545,6 +1659,13 @@ const ImportXML = (() => {
             // 项目人材机customCode、code-原始数据映射,有的导入数据中,有一部分关联自生成材料号CX,有的关联代码...
             // 因此customCode、code都需要跟原始数据映射,通过customCode取不到数据的时候,通过orgCode获取
             let projectGLJMap = {};
+            // 代码映射
+            let customCodeMap = {};
+            // 原始代码映射
+            let originCodeMap = {};
+            function getGLJByMap(code) {
+                return customCodeMap[code] || originCodeMap[code] || null;
+            }
             //投标文件才需要导入定额等数据
             if (importFileKind === FileKind.tender) {
                 tenderData.gljSummary.forEach(pGLJ => {
@@ -1554,14 +1675,17 @@ const ImportXML = (() => {
                     pGLJ.type = pGLJ.type || 201;
                     pGLJ.shortName = pGLJ.shortName || '材';
                     //gljCodeMap[pGLJ.code] = pGLJ;
-                    projectGLJMap[pGLJ.customCode] = pGLJ;
-                    projectGLJMap[pGLJ.code] = pGLJ;
+                    //projectGLJMap[pGLJ.customCode] = pGLJ;
+                    //projectGLJMap[pGLJ.code] = pGLJ;
+                    customCodeMap[pGLJ.customCode] = pGLJ;
+                    originCodeMap[pGLJ.code] = pGLJ;
                 });
                 //处理项目人材机数据
                 tenderData.gljSummary.forEach(pGLJ => {
                     //组成物数据
                     pGLJ.ratios.forEach(ratio => {
-                        let matchData = projectGLJMap[ratio.code];
+                        //let matchData = projectGLJMap[ratio.code];
+                        let matchData = getGLJByMap(ratio.code);
                         ratio.code = matchData.code;
                         ratio.projectGLJID = pGLJ.id; //后端查找标准数据后,方便更新组成物数据
                         ratio.id = IDPlaceholder.ratio++;
@@ -1579,38 +1703,41 @@ const ImportXML = (() => {
                     rst.unitPrice.push(getUnitPrice(pGLJ));
 
                 });
-                //处理评标材料
-                tenderData.evalBidSummary.forEach(glj => {
-                    let pGLJ = projectGLJMap[glj.code];
-                    if (pGLJ) {
-                        pGLJ.is_eval_material = true;
-                    }
-                });
-                //处理承包人材料差额法表
-                tenderData.differentiaSummary.forEach(glj => {
-                    let pGLJ = projectGLJMap[glj.code];
-                    if (pGLJ) {
-                        pGLJ.is_info_adjust = 1;
-                        pGLJ.riskCoe = glj.riskCoe;
-                        pGLJ.standardPrice = glj.standardPrice;
-                        pGLJ.remark = glj.remark;
-                    }
-                });
-                //处理承包人材料指数法表
-                tenderData.exponentialSummary.forEach(glj => {
-                    let pGLJ = projectGLJMap[glj.code];
-                    if (pGLJ) {
-                        pGLJ.is_coe_adjust = 1;
-                        pGLJ.varWeight = glj.varWeight;
-                        pGLJ.FO = glj.FO;
-                        pGLJ.FI = glj.FI;
-                        pGLJ.remark = glj.remark;
-                    }
-                });
+                // 处理承包人材料(差额/指数)法表
+                handleContractorList();
+                // 处理评标材料
+                handleGLJRelatedList(tenderData.evalBidSummary, rst.bidEvaluationList, 'is_eval_material');
+                // 处理暂估价材料
+                handleGLJRelatedList(tenderData.evalSummary, rst.evaluationList, 'is_evaluate');
                 //处理定额数据
                 //获取含有定额数据的清单
                 handleRation(billsData);
             }
+            // 处理与项目人材机关联的承包人材料
+            function handleContractorList() {
+                const contractorType = getAdjustType(tenderData);
+                const contractorData = contractorType === AdjustType.info ? tenderData.differentiaSummary : tenderData.exponentialSummary;
+                handleGLJRelatedList(contractorData, rst.contractorList, 'is_contractor_material');
+            }
+            // 处理与项目人材机关联的承包人材料、评标材料、暂估材料
+            function handleGLJRelatedList(list, container, relatedType) {
+                list.forEach(data => {
+                    data.ID = uuid.v1();
+                    data.projectID = tenderData.ID;
+                    data.projectGLJID = -1;
+                    if (typeof data.seq === 'undefined') {
+                        data.seq = data.code;
+                    }
+                    //const projectGLJ = projectGLJMap[data.code];
+                    const projectGLJ = getGLJByMap(data.code);
+                    if (projectGLJ) {
+                        projectGLJ[relatedType] = 1;
+                        data.is_related = 1;
+                        data.projectGLJID = projectGLJ.id;
+                    }
+                    container.push(data);
+                });
+            }
             //处理清单 设置必要数据 删除无用属性
             billsData.forEach(bills => {
                 //处理综合单价
@@ -1649,11 +1776,7 @@ const ImportXML = (() => {
                 billsHasRations.forEach(bills => {
                     //处理定额
                     bills.rations.forEach(ration => {
-                        // 定额的取费专业在定额库默认为空的话,取定额取费专业
-                        // 如果是量价、与定额同级的人材机,则取单位工程取费专业
-                        ration.programID = ration.type === RationType.ration
-                            ? tenderData.property.engineering
-                            : tenderData.property.projectEngineering;
+                        ration.programID = getProgramID(ration.name, NameProgramMapping, tenderData.property.projectEngineering);
                         ration.ID = uuid.v1();
                         ration.projectID = tenderData.ID;
                         ration.billsItemID = bills.ID;
@@ -1664,21 +1787,10 @@ const ImportXML = (() => {
                             let tempV = ration.quantity / bills.quantity;
                             ration.contain = isFinite(tempV) ? scMathUtil.roundForObj(tempV, 6) : '0';
                         }
-                        //工程量表达式:工程量 * 单位前的量
-                        /*if (!ration.unit) {
-                            ration.quantityEXP = ration.quantity;
-                        } else {
-                            let unitNum = ration.unit.match(/^\d+/);
-                            //工程量小数位数
-                            let qDecimal = ration.quantity.match(/\.\d+/);
-                            ration.quantityEXP = unitNum
-                                ? scMathUtil.roundForObj(ration.quantity * unitNum[0], qDecimal ? qDecimal[0] - 1 : 0)
-                                : ration.quantity;
-                        }*/
-                        // 问题
                         //处理定额人材机,添加需要的数据
                         ration.rationGljs.forEach(rGLJ => {
-                            let matchGLJ = projectGLJMap[rGLJ.code];
+                            //let matchGLJ = projectGLJMap[rGLJ.code];
+                            let matchGLJ = getGLJByMap(rGLJ.code);
                             if (matchGLJ) {
                                 rGLJ.projectGLJID = matchGLJ.id;
                                 rGLJ.code = matchGLJ.code;

+ 207 - 0
web/building_saas/main/js/models/importStdInterfaceBase.js

@@ -0,0 +1,207 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/6/28
+ * @version
+ */
+
+const XML_IMPORT_BASE = (() => {
+    // 清单类型
+    const BILLS_TYPE ={
+        DXFY: 1, //大项费用
+        FB: 2,   //分部
+        FX: 3,   //分项
+        BILLS: 4, //清单
+        BX: 5    //补项
+    };
+    // 项目类型
+    const PROJECT_TYPE = {
+        PROJECT: 'Project',
+        ENGINEERING: 'Engineering',
+        TENDER: 'Tender'
+    };
+    // 人材机调整法
+    const ADJUST_TYPE = {
+        INFO: 'priceInfo',  //造价信息差额调整法
+        COE: 'priceCoe'     //价格指数调整法
+    };
+
+    const CONFIG = Object.freeze({
+        BILLS_TYPE,
+        PROJECT_TYPE,
+        ADJUST_TYPE
+    });
+
+    /*
+    * 读取文件转换为utf-8编码的字符串
+    * @param {Blob}file
+    * @return {Promise}
+    * */
+    function readAsTextSync(file) {
+        return new Promise((resolve, reject) => {
+            let fr = new FileReader();
+            fr.readAsText(file);    // 默认utf-8,如果出现乱码,得看导入文件是什么编码
+            fr.onload = function () {
+                resolve(this.result);
+            };
+            fr.onerror = function () {
+                reject('读取文件失败,请重试。');
+            }
+        });
+    }
+
+    /*
+     * 根据字段数组获得所要字段的值 eg: 要获取标段下的单项工程: ['标段', '单项工程'];
+     * @param {Object}source 源数据
+     *        {Array}fields 字段数组
+     * @return {String}
+     * @example getValue(source, ['标段', '_文件类型'])
+     * */
+    function getValue(source, fields) {
+        let cur = source;
+        for (let field of fields) {
+            if (!cur[field]) {
+                return '';
+            }
+            cur = cur[field];
+        }
+        return cur || '';
+    }
+    // 获取数据类型
+    function _plainType(v) {
+        return Object.prototype.toString.call(v).slice(8, -1);
+    }
+    /*
+     * 获取某字段的值,强制返回数组
+     * @param {Object}source 数据源
+     *        {Array}fields 取的字段
+     * @return {Array}
+     * @example arrayValue(source, ['标段', '单项工程'])
+     * */
+    function arrayValue(source, fields) {
+        let target = getValue(source, fields);
+        if (_plainType(target) === 'Object') {
+            target = [target];
+        } else if (_plainType(target) !== 'Array') {
+            target = []
+        }
+        return target;
+    }
+    // 获取费用
+    function getFee(fees, fields) {
+        if (!Array.isArray(fees) || !fees.length) {
+            return '0';
+        }
+        let feeData = fees.find(fee => fee.fieldName === fields[0]);
+        return feeData[fields[1]] || '0';
+    }
+    // 获取固定ID
+    function getFlag(data) {
+        return data.flags && data.flags[0] && data.flags[0].flag || 0;
+    }
+    /*
+     * 递归获取相关数据,eg:获取组织措施清单下的所有子清单,该子清单们可能由分类及公式措施项各种组合组成。(参考调用处)
+     * @param {Object}src(数据源) {Array}fields(二维数组,数组里的成员由需要取的字段组成)
+     *         eg: ['组织措施分类'], ['公式计算措施项'],该层数据可能由组织措施分类 或 公式计算措施项组成 (同层不可同时存在)
+     *        {Function} 获得源数据后,需要提取的数据方法
+     * @return {Array}
+     * */
+    function getItemsRecur(src, fields, extractFuc) {
+        let itemsSrc = [],
+            curField = [''];
+        for (let field of fields) {
+            itemsSrc = arrayValue(src, field);
+            if (itemsSrc.length) {
+                curField = field;
+                break;
+            }
+        }
+        return itemsSrc.map(itemSrc => {
+            let obj = extractFuc(itemSrc, curField);
+            obj.items = getItemsRecur(itemSrc, fields, extractFuc);
+            return obj;
+        });
+    }
+    /*
+    * 转换计算基数
+    * 1.有子项数据,则清空基数
+    * 2.行代号引用转换为ID引用
+    * 3.对应字典代号转换,对应字典里找不到则设置成金额
+    * @param {Array}billsData 清单数据
+    *        {Object}CalcBaseMap 基数映射
+    * @return {void}
+    * */
+    function transformCalcBase(billsData, CalcBaseMap) {
+        //行代号 - ID映射
+        let rowCodeMap = {};
+        billsData.forEach(data => {
+            if (data.rowCode) {
+                rowCodeMap[data.rowCode] = data.ID;
+            }
+        });
+        for (let bills of billsData) {
+            if (!bills.calcBase) {
+                continue;
+            }
+            let sub = billsData.find(data => data.ParentID === bills.ID);
+            //有子项数据,则清空基数,费率
+            if (sub) {
+                bills.calcBase = '';
+                bills.feeRate = '';
+                continue;
+            }
+            //提取基数
+            bills.calcBase = bills.calcBase.replace(/\s/g, '');
+            let bases = bills.calcBase.split(/[\+\-\*\/]/g);
+            //提取操作符
+            let oprs = bills.calcBase.match(/[\+\-\*\/]/g);
+            //转换后的基数
+            let newBase = [];
+            let illegal = false;    //不合法
+            for (let base of bases) {
+                if (rowCodeMap[base]) { //行引用
+                    newBase.push(`@${rowCodeMap[base]}`);
+                } else if (CalcBaseMap[base]) { //基数字典
+                    newBase.push(CalcBaseMap[base]);
+                } else {    //都没匹配到,说明软件无法识别此基数
+                    illegal = true;
+                    break;
+                }
+            };
+            if (illegal) {
+                let fee = getFee(bills.fees, ['common', 'totalFee']);
+                let feeRate = bills.feeRate && parseFloat(bills.feeRate) !== 0 ? parseFloat(bills.feeRate) : 0;
+                if (fee && parseFloat(fee) !== 0 && feeRate) {
+                    bills.calcBase = scMathUtil.roundForObj(parseFloat(fee) * 100 / feeRate, 2);
+                } else {
+                    bills.calcBase = fee !== '0' ? fee : '';
+                }
+            } else {
+                let newCalcBase = '';
+                for (let i = 0; i < newBase.length; i++) {
+                    newCalcBase += newBase[i];
+                    if (oprs && oprs[i]) {
+                        newCalcBase += oprs[i];
+                    }
+                }
+                bills.calcBase = newCalcBase;
+            }
+        }
+    }
+    // 获取必要的清单模板
+    async function getNeedfulTemplate(templateLibID) {
+        return await ajaxPost('/template/bills/api/getNeedfulTemplate',
+            {templateLibID});
+    }
+
+
+    const UTIL = Object.freeze({
+        getValue,
+        arrayValue,
+        getFee,
+        transformCalcBase
+    });
+})();

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

@@ -17,7 +17,10 @@ const ModuleNames = {
     labour_coe: 'labour_coe',
     calc_program: 'calc_program',
     installation_fee:'installation_fee',
-    projectInfo: 'project_info'
+    projectInfo: 'project_info',
+    evaluate_list:'evaluate_list',
+    contractor_list:'contractor_list',
+    bid_evaluation_list:'bid_evaluation_list'
 };
 
 let gljType = gljUtil.gljType;
@@ -271,6 +274,8 @@ const fixedFlag = {
     HOUSE_QUALITY_ACCEPT_FEE:31,
     //组织措施费
     ORGANIZATION:32,
+    //其他措施费
+    OTHER_MEASURE_FEE:33
 };
 // 只读的固定类别(工程量、单价、综合合价只读,相当于是标题)
 const titleFlags = [
@@ -385,7 +390,8 @@ const blockType ={
     FB:2,//分部
     FX:3,//分项
     BILL:4,//清单
-    BX:5//补项
+    BX:5,//补项
+    DXFY:6//大项费用
 };
 
 const filterType = {
@@ -401,7 +407,8 @@ const filterType = {
     ZGCL:'9',
     SCHZ:'10',
     ZYCL:'11',
-    AMAE:'12'
+    AMAE:'12',
+    PBCL:'13'
 };
 // 文件类型
 const _fileKind = {

+ 8 - 1
web/building_saas/main/js/models/project.js

@@ -102,6 +102,9 @@ var PROJECT = {
             this.composition.loadData();
             this.labourCoe = new LabourCoe(this);
             this.calcProgram = new CalcProgram(this);
+            this.evaluate_list = new EvaluateList(this);
+            this.bid_evaluation_list = new BidEvaluationList(this);
+            this.contractor_list = new ContractorList(this);
             this.calcBase = calcBase;
 
             // this.masterField = {ration: 'billsItemID', volumePrice: 'billsItemID'};
@@ -335,7 +338,11 @@ var PROJECT = {
             }
         };
         project.prototype.markUpdateProject = function (data,type,callback) {
-            CommonAjax.post("/project/markUpdateProject",{updateInfo:data,type:type},callback);
+            $.bootstrapLoading.start()
+            CommonAjax.post("/project/markUpdateProject",{updateInfo:data,type:type},function(){
+                if(callback) callback();
+                $.bootstrapLoading.end()
+            });
         };
 
         project.prototype.saveProperty = function (propertyName, propertyValue) {

+ 48 - 7
web/building_saas/main/js/models/project_glj.js

@@ -12,6 +12,8 @@ function ProjectGLJ() {
     this.getRatioId = null;
 }
 
+
+
 /**
  * 加载数据
  *
@@ -321,6 +323,7 @@ ProjectGLJ.prototype.batchUpdateGLJProperty = function (updateMap,callback) {
         $.bootstrapLoading.end();
         let supplyChangeIDs = [],evaluate_gljs = [],rationNodes = [];
         console.log(updateMap);
+        let needCalcQuantity = false;
         for(let idKey in updateMap){
             let gljID = parseInt(idKey);
             let glj = me.getByID(gljID);
@@ -331,9 +334,8 @@ ProjectGLJ.prototype.batchUpdateGLJProperty = function (updateMap,callback) {
             if(doc.hasOwnProperty("supply")||doc.hasOwnProperty("supply_quantity")){
                 supplyChangeIDs.push(gljID);
             }
-            if(doc.hasOwnProperty("is_evaluate")){
-                evaluate_gljs.push(glj);
-            }
+            if(doc.hasOwnProperty("is_evaluate")) evaluate_gljs.push(glj);
+            if(doc.hasOwnProperty("is_adjust_price")) needCalcQuantity = true;
         }
         if(supplyChangeIDs.length>0){
             let temRationNodes = calcTools.getRationsByProjectGLJ(supplyChangeIDs);
@@ -350,7 +352,7 @@ ProjectGLJ.prototype.batchUpdateGLJProperty = function (updateMap,callback) {
                 projectObj.mainController.refreshTreeNode(projectObj.project.mainTree.roots);
             });
         }
-
+        if(needCalcQuantity) me.calcQuantity();
         if(callback){
             callback();
         }
@@ -473,6 +475,7 @@ ProjectGLJ.prototype.pGljUpdate= function (data,callback) {
         if(data.field == 'is_evaluate'){
             impactList =  me.changeIsEvaluate(data.id);
         }
+        if(data.field == "is_adjust_price") me.calcQuantity();
         if(callback){
             callback(impactList);
         }
@@ -843,11 +846,16 @@ ProjectGLJ.prototype.getTenderMarketPrice = function (glj,isRadio) {
     let proGLJ =  projectObj.project.projectGLJ;
     let calcOptions=projectObj.project.projectInfo.property.calcOptions;
     let decimalObj = projectObj.project.projectInfo.property.decimal;
+    let tenderCoe = this.getTenderPriceCoe(glj);
+    return gljUtil.getMarketPrice(glj,proGLJ.datas,calcOptions,decimalObj,isRadio,_,scMathUtil,tenderCoe);
+};
+
+ProjectGLJ.prototype.getTenderPriceCoe = function(glj){
     let tenderCoe = 1;
-    if (projectObj.project.property.tenderSetting && gljUtil.isDef(projectObj.project.property.tenderSetting.gljPriceTenderCoe) ){
+    if (!glj.is_adjust_price&&projectObj.project.property.tenderSetting && gljUtil.isDef(projectObj.project.property.tenderSetting.gljPriceTenderCoe) ){
         tenderCoe = parseFloat(projectObj.project.property.tenderSetting.gljPriceTenderCoe);
     }
-    return gljUtil.getMarketPrice(glj,proGLJ.datas,calcOptions,decimalObj,isRadio,_,scMathUtil,tenderCoe);
+    return tenderCoe
 };
 
 ProjectGLJ.prototype.isEstimateType = function(type){
@@ -898,4 +906,37 @@ ProjectGLJ.prototype.calcTenderQuantity  = function (){
     let rationDatas = projectObj.project.Ration.datas;
     let billsDatas = projectObj.project.Bills.datas;
     gljUtil.calcProjectGLJQuantity(this.datas,rationGLJDatas,rationDatas,billsDatas,getDecimal("glj.quantity"),_,scMathUtil,true);
-};
+};
+
+class EvaluateList {
+    constructor (project) {
+        this.project = project;
+        this.datas = [];
+        project.registerModule(ModuleNames.evaluate_list, this);
+    };
+    loadData (datas) {
+        this.datas = datas;
+    };
+}
+
+class BidEvaluationList {
+    constructor (project) {
+        this.project = project;
+        this.datas = [];
+        project.registerModule(ModuleNames.bid_evaluation_list, this);
+    };
+    loadData (datas) {
+        this.datas = datas;
+    };
+}
+
+class ContractorList {
+    constructor (project) {
+        this.project = project;
+        this.datas = [];
+        project.registerModule(ModuleNames.contractor_list, this);
+    };
+    loadData (datas) {
+        this.datas = datas;
+    };
+}

+ 1 - 0
web/building_saas/main/js/views/calc_program_view.js

@@ -52,6 +52,7 @@ let calcProgramObj = {
                 break;
             case 2:
                 projectObj.project.calcProgram.innerCalc(treeNode, []);
+                projectObj.project.calcProgram.rationMap = null;
                 delete treeNode.changed;
                 break;
             case 3:

Разница между файлами не показана из-за своего большого размера
+ 1038 - 0
web/building_saas/main/js/views/config_material_view.js


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

@@ -539,6 +539,7 @@ var feeRateObject={
         this.mainFeeRateSheet.bind(GC.Spread.Sheets.Events.RangeChanged, this.onMainFeeRateRangeChanged);
         this.mainFeeRateSheet.bind(GC.Spread.Sheets.Events.EditStarting, function (e,args) {
             let me =feeRateObject;
+            if($.bootstrapLoading.isLoading()) args.cancel = true;
             if(!me.mainFeeRateEditChecking(args.row,args.col)){
                 args.cancel = true;
             }
@@ -637,8 +638,11 @@ var feeRateObject={
                         feerateInfo.push({rateID:r.rateID,value:r.value});
                     }
                 }
-                feerateInfo.length > 0 ?feeRate.onFeeRatesChange(feerateInfo):'';
-                $.bootstrapLoading.end();
+                if(feerateInfo.length > 0){
+                    feeRate.onFeeRatesChange(feerateInfo);
+                }else {
+                    $.bootstrapLoading.end();
+                }
             })
         }
     },

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

@@ -60,7 +60,7 @@ let gljCol = {
             {headerName: "备注", headerWidth: 100, dataCode: "remark", hAlign: "left", dataType: "String"}
         ],
         view: {
-            lockColumns: ["code","name","specs","unit","short_name","adjustPrice","quantity"]
+            lockColumns: ["code","name","specs","unit","short_name","tenderPrice","adjustPrice","quantity","tenderQuantity"]
         },
         frozenCols:4
     },

+ 14 - 5
web/building_saas/main/js/views/glj_view.js

@@ -249,6 +249,7 @@ var gljOprObj = {
     startEditChecking:function (sender,args) {
         let me = gljOprObj;
         let selected = projectObj.project.mainTree.selected;
+        if($.bootstrapLoading.isLoading())  args.cancel = true;
         if(selected){
             if(me.isInstallationNode(selected)==true){
                 args.cancel = true;
@@ -772,6 +773,8 @@ var gljOprObj = {
                         ration_gljs[i].subList = mixRatios;
                     }
                     if(ration) gljOprObj.getTotalQuantity(ration_gljs[i], ration);
+                }else {
+                    console.log("没有找到工料机:"+ration_gljs[i].name)
                 }
             }
         }
@@ -782,10 +785,12 @@ var gljOprObj = {
         let calcOptions=projectObj.project.projectInfo.property.calcOptions;
         let decimalObj = projectObj.project.projectInfo.property.decimal;
         let labourCoeDatas =  projectObj.project.labourCoe.datas;
+        let tenderCoe = projectObj.project.projectGLJ.getTenderPriceCoe(glj);
         glj = glj?glj:_.find(proGLJ.datas.gljList, {'id': data.projectGLJID});
         if(glj){
-            let result = gljUtil.getGLJPrice(glj,proGLJ.datas,calcOptions,labourCoeDatas,decimalObj,isRadio,_,scMathUtil);
+            let result = gljUtil.getGLJPrice(glj,proGLJ.datas,calcOptions,labourCoeDatas,decimalObj,isRadio,_,scMathUtil,tenderCoe);
             data.marketPrice = result.marketPrice;
+            data.tenderPrice = result.tenderPrice;
             data.basePrice =  result.basePrice;
             data.adjustPrice = result.adjustPrice;
             data.marketUnitFee = data.marketPrice;//更新树节点市场单价列的值
@@ -839,7 +844,7 @@ var gljOprObj = {
                 return gljOprObj.getIndex(item,gljKeyArray) == mIndex
             });//改关联关系
             if(pg){
-                var tem = {
+                let tem = {
                     projectGLJID: pg.id,
                     code: pg.code,
                     name: pg.name,
@@ -862,8 +867,10 @@ var gljOprObj = {
                     tem.isEstimate =  pg.is_evaluate;
                 }
                 this.setGLJPrice(tem,pg);
+                temRationGLJs.push(tem);
+            }else {
+                console.log("没有找到工料机:"+mIndex)
             }
-            temRationGLJs.push(tem);
         }
         temRationGLJs = gljUtil.sortRationGLJ(temRationGLJs);
         return temRationGLJs;
@@ -1027,17 +1034,19 @@ var gljOprObj = {
     * @param  {Boolean}init 是否是初始化
     *         {Boolean}reset 是否重置数据 (点击分类树等)
     *         {Boolean}location 是否定位(替换初始化)
-    *         {Number}index 分页数据开始索引
+    *         {Number}index 下一页数据开始索引,根据这个值获取上次分页最大的编码数据(不用skip进行分页,skip跳过大量数据会降低性能,用$gt: data.code)
     * @return {Object}
     * */
     getPagingCondition: function (init, reset, location, index) {
+        // 上一次分页的最末人材机编码,index为0时,code为空
+        const code = gljOprObj.AllRecode && gljOprObj.AllRecode[index - 1] ? gljOprObj.AllRecode[index - 1].code : '';
         // 初始化情况下的条件
         let condition = {
+            code,
             // 是否重置数据
             reset,
             // 是否定位
             location,
-            // 开始取数据的位置
             index,
             // 限制条数
             limit: 50,

+ 1 - 1
web/building_saas/main/js/views/importBills.js

@@ -20,7 +20,7 @@ const importBills = (function(){
     
     //去除转义字符
     function _deESC(data) {
-        return _isDef(data) ? data.toString().replace(/[\r,\n,\s,\t]/g, '') : data;
+        return _isDef(data) ? data.toString().replace(/[\r\n\s\t]/g, '') : data;
     }
 
     function _deNR(data) {

+ 18 - 10
web/building_saas/main/js/views/locate_view.js

@@ -59,7 +59,7 @@ let locateObject={
         settingList :[
             {background:"E2F2C5",describe:""},
             {background:"F9E2CF",describe:""},
-            {background:"F2EFD9",describe:"hehe"},
+            {background:"F2EFD9",describe:""},
             {background:"F5D1DA",describe:""},
             {background:"E3E3E3",describe:""},
             {background:"B6F3F2",describe:""},
@@ -67,6 +67,11 @@ let locateObject={
         ],
         selected:"E2F2C5"
     },
+    getDescribeByColor: function (color) {
+        const setting = projectObj.project.property.bookmarkSetting ? projectObj.project.property.bookmarkSetting : locateObject.bookmarkPropertySetting;
+        const item = setting.settingList.find(item => item.background === color);
+        return item && item.describe ? item.describe : '描述';
+    },
     initMainSpread:function(){
         if(!this.mainSpread){
             this.mainSpread = SheetDataHelper.createNewSpread($("#locate_result")[0],3);
@@ -173,10 +178,11 @@ let locateObject={
             focus = true;
         }
         sel.row = selectedID?_.findIndex(this.bookmarkDatas,{'ID':selectedID}):0;
-        sheet.setSelection(sel.row==-1?0:sel.row,sel.col,sel.rowCount,sel.colCount);
+        const locatedRow = sel.row === -1 ? 0 : sel.row;
+        sheet.setSelection(locatedRow,sel.col,sel.rowCount,sel.colCount);
         sheet.setRowCount(this.bookmarkDatas.length);
-        if(sel.row!=-1) sheet.showRow(sel.row, GC.Spread.Sheets.VerticalPosition.bottom);
-        this.showAnnotation(sel.row,focus);
+        sheet.showRow(locatedRow, GC.Spread.Sheets.VerticalPosition.bottom)
+        this.showAnnotation(locatedRow,focus);
     },
     getBookmarkDatas:function(){
         let datas = [];
@@ -434,6 +440,8 @@ let locateObject={
             $("#bookmarkNodeID").val(recode.ID);
             $("#bookmarkNodeType").val(recode.type);
             if(focus) $("#annotationTextarea").focus();
+        } else {
+            $("#annotationTextarea").val('');
         }
     },
     locateNode:function (ID) {
@@ -461,13 +469,13 @@ let locateObject={
         setting.selected = $("#bookmarkSelected").val();
         this.updateProjectProperty("bookmarkSetting",setting);
     },
-    setOrCancelBookmark:function(node){
+    // background为null则为取消书签批注
+    setOrCancelBookmark:function(node, background){
         $("#bookmark").prop("checked", true);
-        let setting = projectObj.project.property.bookmarkSetting?projectObj.project.property.bookmarkSetting:locateObject.bookmarkPropertySetting;
-        let background = null;
-        if(_.isEmpty(node.data.bookmarkBackground)) background = setting.selected;
-        let updateData = {type:node.sourceType,data:{'ID' : node.data.ID,bookmarkBackground:background}};
-        if(background == null) updateData.data.bookmarkAnnotation = null;
+        const updateData = {type: node.sourceType, data: {ID: node.data.ID, bookmarkBackground: background}};
+        if(!background) {
+            updateData.data.bookmarkAnnotation = null;
+        }
         $.bootstrapLoading.start();
         projectObj.project.updateNodes([updateData],function () {
             $.bootstrapLoading.end();

+ 258 - 148
web/building_saas/main/js/views/material_adjust_view.js

@@ -9,40 +9,38 @@ let materialAdjustObj = {
     priceCoeDatas:[],
     priceInfoSetting:{
         header: [
-            {headerName: "编号", headerWidth: 80, dataCode: "code", dataType: "String"},
+            {headerName: "关联", headerWidth: 80, dataCode: "is_related", dataType: "String",cellType:'checkBox'},
+            {headerName: "材料号", headerWidth: 80, dataCode: "code", dataType: "String"},
             {headerName: "材料名称", headerWidth: 160, dataCode: "name", dataType: "String",cellType:'tipsCell'},
             {headerName: "规格型号", headerWidth: 120, dataCode: "specs", hAlign: "left", dataType: "String",cellType:'tipsCell'},
             {headerName: "单位", headerWidth: 45, dataCode: "unit", hAlign: "center", dataType: "String"},
             {headerName: "数量", headerWidth: 90, dataCode: "quantity", hAlign: "right", dataType: "Number",decimalField:'glj.quantity'},
-            {headerName: "风险系数%", headerWidth: 60, dataCode: "riskCoe", hAlign: "right", dataType: "Number",validator:"number"},
+            {headerName: "风险系数%", headerWidth: 80, dataCode: "riskCoe", hAlign: "right", dataType: "Number",validator:"number"},
             {headerName: "基准单价", headerWidth: 70, dataCode: "standardPrice", hAlign: "right", dataType: "Number",validator:"number"},
             {headerName: "投标单价", headerWidth: 70, dataCode: "marketPrice", hAlign: "right", dataType: "Number",validator:"number"},
             {headerName: "合价", headerWidth: 90, dataCode: "totalPrice", hAlign: "right", dataType: "Number",validator:"number",decimalField:'bills.totalPrice'},
-            {headerName: "产地", headerWidth: 80, dataCode: "originPlace", hAlign: "left", dataType: "String"},
-            {headerName: "厂家", headerWidth: 80, dataCode: "vender", hAlign: "left", dataType: "String"},
             {headerName: "供货方式", headerWidth: 70, dataCode: "supply", hAlign: "center", dataType: "String",cellType:'comboBox',editorValueType:true,options:supplyComboMap},
             {headerName: "备注", headerWidth: 100, dataCode: "remark", hAlign: "left", dataType: "String"}
         ],
-        view:{ lockColumns: ["code","name","specs","unit","quantity","totalPrice","originPlace","vender","supply","remark"]}
+        view:{ lockColumns: ["is_related","quantity","totalPrice","originPlace","vender","supply"]}
     },
     priceCoeSetting:{
         header: [
-            {headerName: "编号", headerWidth: 80, dataCode: "code", dataType: "String"},
+            {headerName: "关联", headerWidth: 80, dataCode: "is_related", dataType: "String",cellType:'checkBox'},
+            {headerName: "材料号", headerWidth: 80, dataCode: "code", dataType: "String"},
             {headerName: "材料名称", headerWidth: 160, dataCode: "name", dataType: "String",cellType:'tipsCell'},
             {headerName: "规格型号", headerWidth: 120, dataCode: "specs", hAlign: "left", dataType: "String",cellType:'tipsCell'},
             {headerName: "单位", headerWidth: 45, dataCode: "unit", hAlign: "center", dataType: "String"},
-            {headerName: "变值权重B", headerWidth: 90, dataCode: "varWeight", hAlign: "right", dataType: "Number",validator:"number"},
+            {headerName: "变值权重B", headerWidth: 90, dataCode: "varWeight", hAlign: "right", dataType: "Number",validator:"number",formatter:"0.00"},
             {headerName: "基本价格指数F0", headerWidth: 70, dataCode: "FO", hAlign: "right", dataType: "Number",validator:"number"},
             {headerName: "现行价格指数Ft", headerWidth: 70, dataCode: "FI", hAlign: "right", dataType: "Number",validator:"number"},
             {headerName: "数量", headerWidth: 90, dataCode: "quantity", hAlign: "right", dataType: "Number",decimalField:'glj.quantity'},
             {headerName: "单价", headerWidth: 70, dataCode: "marketPrice", hAlign: "right", dataType: "Number",validator:"number"},
             {headerName: "合价", headerWidth: 90, dataCode: "totalPrice", hAlign: "right", dataType: "Number",validator:"number",decimalField:'bills.totalPrice'},
-            {headerName: "产地", headerWidth: 80, dataCode: "originPlace", hAlign: "left", dataType: "String"},
-            {headerName: "厂家", headerWidth: 80, dataCode: "vender", hAlign: "left", dataType: "String"},
             {headerName: "供货方式", headerWidth: 70, dataCode: "supply", hAlign: "center", dataType: "String",cellType:'comboBox',editorValueType:true,options:supplyComboMap},
             {headerName: "备注", headerWidth: 100, dataCode: "remark", hAlign: "left", dataType: "String"}
         ],
-        view:{ lockColumns: ["code","name","specs","unit","quantity","totalPrice","originPlace","varWeight","vender","supply","remark"]}
+        view:{ lockColumns: ["is_related","quantity","totalPrice","originPlace","varWeight","vender","supply"]}
     },
     refreshSheetDatas:function () {
         //读项目属性的值,若没有则默认为造价信息差额调整法
@@ -52,100 +50,121 @@ let materialAdjustObj = {
         } else {
             materialAdjustObj.showPriceCoeDatas();
         }
-        //let sheetIndex = $("#adjustType").val()
+        configMaterialObj.showEvaluateRelateDatas();
     },
-    initSpread:function () {
-        if(!this.spread){
-            this.spread = SheetDataHelper.createNewSpread($("#material_adjust_sheet")[0],2);
-            sheetCommonObj.spreadDefaultStyle(this.spread);
-        }
-        this.initPriceInfoSheet();
-        this.initPriceCoeSheet();
-        //disableRightMenu("project_glj_sheet",this.projectGljSpread,this.rightClickCallback);
-        //打开别人分享的项目,只读
-        if(projectReadOnly){
-            disableSpread(this.spread);
-        }else {
-            this.initRightClick();
-        }
+    getSheetDatas:function () {
+        let adjustType = $('#adjustType').val();
+        return adjustType === 'priceInfo' ? this.priceInfoDatas : this.priceCoeDatas;
+    },
+    getSheet:function () {
+        let adjustType = $('#adjustType').val();
+        return adjustType === 'priceInfo' ? this.priceInfoSheet : this.priceCoeSheet;
     },
     initPriceInfoSheet:function () {
-        this.priceInfoSheet = this.spread.getSheet(0);
+        this.priceInfoSheet = configMaterialObj.configSpread.getSheet(2);
         sheetCommonObj.initSheet(this.priceInfoSheet,this.priceInfoSetting,30);
         this.priceInfoSheet.bind(GC.Spread.Sheets.Events.ValueChanged, this.onSheetValueChange);
+        this.priceInfoSheet.bind(GC.Spread.Sheets.Events.SelectionChanged,this.onSelectionChange);
+        this.priceInfoSheet.bind(GC.Spread.Sheets.Events.EditStarting, this.onPriceInfoEditString);
         this.priceInfoSheet.bind(GC.Spread.Sheets.Events.RangeChanged, this.onSheetRangeChange);
         this.priceInfoSheet.name('priceInfoSheet');
         this.priceInfoSheet.setRowHeight(0, 36, 1);
     },
     initPriceCoeSheet:function () {
-        this.priceCoeSheet = this.spread.getSheet(1);
+        this.priceCoeSheet = configMaterialObj.configSpread.getSheet(3);
         sheetCommonObj.initSheet(this.priceCoeSheet,this.priceCoeSetting,30);
         this.priceCoeSheet.bind(GC.Spread.Sheets.Events.ValueChanged, this.onSheetValueChange);
+        this.priceCoeSheet.bind(GC.Spread.Sheets.Events.SelectionChanged,this.onSelectionChange);
+        this.priceCoeSheet.bind(GC.Spread.Sheets.Events.EditStarting, this.onPriceInfoEditString);
         this.priceCoeSheet.bind(GC.Spread.Sheets.Events.RangeChanged, this.onSheetRangeChange);
         this.priceCoeSheet.name('priceCoeSheet');
         this.priceCoeSheet.setRowHeight(0, 36, 1);
     },
+    onSelectionChange:function (sender, args) {
+        let me = configMaterialObj;
+        let newSel = args.newSelections[0];
+        let oldSel = args.oldSelections?args.oldSelections[0]:{};
+        if(newSel.row != oldSel.row){
+            me.showEvaluateRelateDatas();
+            me.relateFilterAgain = false;
+        }
+    },
     onSheetRangeChange:function (a,args) {
-        let me = materialAdjustObj,updateMap = {};
+        let me = materialAdjustObj,updateMap = {},tasks=[];
         let projectGLJ = projectObj.project.projectGLJ;
         let setting = $("#adjustType").val() == "priceInfo"?me.priceInfoSetting:me.priceCoeSetting;
         let datas = $("#adjustType").val() == "priceInfo"?me.priceInfoDatas:me.priceCoeDatas;
         for(let c of args.changedCells){
             let dataCode = setting.header[c.col].dataCode;
             let value= args.sheet.getCell(c.row, c.col).text();
+            let recode = datas[c.row];
+            if(dataCode == "unit" && recode.is_related){
+                me.refreshSheetDatas();
+                return ;
+            }
             if (value&&!sheetCommonObj.checkData(c.col,setting,value)) {
                 alert('输入的数据类型不对,请重新输入!');
                 me.refreshSheetDatas();
                 return ;
             }
-            let recode = datas[c.row];
             if(dataCode=='marketPrice'){
-                let editField ="market_price";
-                projectGLJ.updatePrice(recode,editField,value,'pg',me.refreshSheetDatas);
+                dataCode = 'market_price';
+                if(recode.is_related){//关联的情况下,直接修改工料机价格
+                    return projectGLJ.updatePrice(recode,dataCode,value,'rg', projectGljObject.refreshViewsData);
+                }
             }else {
-                let tem = {};
-                if(value) value = scMathUtil.roundForObj(value,getDecimal("process"));
+                let tem = updateMap[recode.ID]?updateMap[recode.ID]:{};
+                if(value&&dataCode == "standardPrice") value = scMathUtil.roundForObj(value,getDecimal('glj.unitPrice'))+"";
                 if(value == recode[dataCode]) continue;
                 tem[dataCode] = value;
-                updateMap[recode.id] = tem;
+                updateMap[recode.ID] = tem;
             }
         }
-        if( _.isEmpty(updateMap)) return;
-        projectGLJ.batchUpdateGLJProperty(updateMap,function () {
-            me.refreshSheetDatas();
-        });
+        for(let ID in updateMap){
+            let edata = {type:"contractor_list",doc:updateMap[ID],action:"update",ID:ID};
+            tasks.push(edata);
+        }
+        if(tasks.length > 0)  configMaterialObj.updateEvaluateMaterial(tasks,false);
+
 
     },
+    onPriceInfoEditString:function (a,args) {
+        let me = materialAdjustObj;
+        let setting = $("#adjustType").val() == "priceInfo"?me.priceInfoSetting:me.priceCoeSetting;
+        let datas = $("#adjustType").val() == "priceInfo"?me.priceInfoDatas:me.priceCoeDatas;
+        let dataCode = setting.header[args.col].dataCode;
+        let recorde = datas[args.row];
+        if(recorde){
+            if(recorde.is_related && dataCode =="unit") args.cancel = true;
+        }
+    },
 
     onSheetValueChange:function (a,args) {
         let me = materialAdjustObj,row = args.row, col = args.col;
-        let projectGLJ = projectObj.project.projectGLJ;
         let setting = $("#adjustType").val() == "priceInfo"?me.priceInfoSetting:me.priceCoeSetting;
         let datas = $("#adjustType").val() == "priceInfo"?me.priceInfoDatas:me.priceCoeDatas;
         let dataCode = setting.header[col].dataCode;
-        let recode = datas[row];
+        let evaluate = datas[row];
         let value = args.newValue;
-        if(value === undefined ){
-            me.refreshSheetDatas();
-            return;
-        }
-        if (value&&!projectGljObject.checkData(col,setting,value)) {
+        if (value&&! sheetCommonObj.checkData(col,setting,value)) {
             alert('输入的数据类型不对,请重新输入!');
-            me.refreshSheetDatas();
-            return ;
+            return me.refreshSheetDatas();
         }
-        if(recode[dataCode] == value) return;
-
-        if(dataCode=='marketPrice'){//修改市场价和修改定额价时需要重新记算很多受影响的树节点,现在改成与定字额工料机那里调相同的方法。
-            let editField ="market_price";
-            projectGLJ.updatePrice(recode,editField,value,'pg',me.refreshSheetDatas);
-            return;
+        if(dataCode == 'marketPrice'){
+            dataCode = 'market_price';
+            if(evaluate.is_evaluate == 1){
+                alert("当前材料市场价已被锁定,修改请先返回人材机汇总界面解除锁定。");
+                return me.refreshSheetDatas();
+            }
+            if(evaluate.is_related){//关联的情况下,直接修改工料机价格
+                return projectObj.project.projectGLJ.updatePrice(evaluate,dataCode,value,'rg', projectGljObject.refreshViewsData);
+            }
         }
-        if(value) value = scMathUtil.roundForObj(value,getDecimal("process"));
-        if(value == null) value="";
-        let updateData = {id: recode.id, field: dataCode, value: value+""};
-        projectGLJ.pGljUpdate(updateData,me.refreshSheetDatas);
-
+        if(dataCode == "standardPrice") value = scMathUtil.roundForObj(value,getDecimal('glj.unitPrice'))+"";
+        let edata = {type:'contractor_list',doc:{},action:"update",ID:evaluate.ID};
+        if(evaluate[dataCode] == value) return me.refreshSheetDatas();
+        edata.doc[dataCode] = value;
+        configMaterialObj.updateEvaluateMaterial([edata],false);
     },
     deleteAdjust:function () {
         let me = this,updateMap={};
@@ -163,96 +182,107 @@ let materialAdjustObj = {
 
     },
     showPriceInfoDatas:function () {
-        this.spread.setActiveSheetIndex(0);
+        configMaterialObj.configSpread.setActiveSheetIndex(2);
+        let sel = this.priceInfoSheet.getSelections()[0];
+        let oldData = sel.row<this.priceInfoDatas.length?this.priceInfoDatas[sel.row]:"";
         this.priceInfoDatas = this.getPirceInfoDatas();
         sheetCommonObj.showData(this.priceInfoSheet, this.priceInfoSetting,this.priceInfoDatas);
         this.priceInfoSheet.setRowCount(this.priceInfoDatas.length);
+        sel.row = oldData?_.findIndex(this.priceInfoDatas,{'ID':oldData.ID}):'';
+        this.priceInfoSheet.setSelection(sel.row==-1?0:sel.row,sel.col,sel.rowCount,sel.colCount);
     },
     showPriceCoeDatas:function () {
-        this.spread.setActiveSheetIndex(1);
+        configMaterialObj.configSpread.setActiveSheetIndex(3);
+        let sel = this.priceCoeSheet.getSelections()[0];
+        let oldData = sel.row<this.priceCoeDatas.length?this.priceCoeDatas[sel.row]:"";
         this.priceCoeDatas = this.getPriceCoeDatas();
         sheetCommonObj.showData(this.priceCoeSheet, this.priceCoeSetting,this.priceCoeDatas);
         this.priceCoeSheet.setRowCount(this.priceCoeDatas.length);
+        sel.row = oldData?_.findIndex(this.priceCoeDatas,{'ID':oldData.ID}):'';
+        this.priceCoeSheet.setSelection(sel.row==-1?0:sel.row,sel.col,sel.rowCount,sel.colCount);
     },
-    getPirceInfoDatas:function(projectGLJList){
+    getPirceInfoDatas:function(projectGLJList,contractor_list,decimalObj){
         let datas = [];
-        let gljList = this.filterAdjustGlj(projectGLJList, 'is_info_adjust');
-        gljList = sortProjectGLJ(gljList);
-        for(let glj of gljList){
-            datas.push(getInfoObject(glj));
-        }
-        return datas;
-        function getInfoObject(glj) {
-            let data = materialAdjustObj.getCommonObject(glj);
-            data.riskCoe = glj.riskCoe;
-            data.standardPrice = glj.standardPrice;
-            data.totalPrice = materialAdjustObj.getTotalPrice(data);
-            return data;
+        let pgljList =   projectGLJList?projectGLJList:projectObj.project.projectGLJ.datas.gljList;
+        let contractorList = contractor_list?contractor_list:projectObj.project.contractor_list.datas;
+        let billsDecimal = decimalObj?decimalObj.bills.totalPrice:getDecimal('bills.totalPrice');
+        let gljMap = _.indexBy(pgljList,'id');
+        for(let e of contractorList){
+            let t = {
+                ID:e.ID,
+                projectID: e.projectID,
+                is_related:e.is_related,//关联,1关,0不关
+                projectGLJID:e.projectGLJID,//关联工料机ID
+                seq:e.seq,//序号
+                code:e.code,
+                name:e.name,
+                specs:e.specs,
+                unit:e.unit,
+                type:e.type,
+                marketPrice:e.market_price,
+                quantity:e.quantity,
+                remark:e.remark,
+                supply:e.supply,
+                riskCoe : e.riskCoe,
+                is_evaluate:0,
+                standardPrice : e.standardPrice
+            };
+            let pglj = gljMap[e.projectGLJID];
+            if(e.is_related && pglj){
+                gljOprObj.setGLJPrice(t,pglj);
+                t.vender = pglj.vender;
+                t.supply = pglj.supply;
+                t.is_evaluate = pglj.is_evaluate;
+                t.quantity = pglj.quantity;
+            }
+            t.totalPrice = scMathUtil.roundForObj(parseFloat(t.quantity) * parseFloat(t.marketPrice) ,billsDecimal);
+            datas.push(t);
         }
+        return _.sortByAll(datas,'code');
     },
-    filterAdjustGlj:function (projectGLJList, field) {
-        let gljList = projectGLJList?projectGLJList:projectObj.project.projectGLJ.datas.gljList;
-        return _.filter(gljList,function (item) {
-            return item.quantity !== 0 && item.quantity !== '0' && item[field] ===1
-        });
-    },
-    getPriceCoeDatas:function (projectGLJList,totalFee) {
+    getPriceCoeDatas:function (projectGLJList,contractor_list,totalFee,decimalObj) {
         let datas = [];
+        let pgljList =   projectGLJList?projectGLJList:projectObj.project.projectGLJ.datas.gljList;
+        let contractorList = contractor_list?contractor_list:projectObj.project.contractor_list.datas;
+        let billsDecimal = decimalObj?decimalObj.bills.totalPrice:getDecimal('bills.totalPrice');
+        let gljMap = _.indexBy(pgljList,'id');
         if(!gljUtil.isDef(totalFee)){
             let node =  projectObj.project.Bills.getEngineeringCostNode(projectObj.mainController);
             totalFee = node && node.data.feesIndex && node.data.feesIndex.common?node.data.feesIndex.common.totalFee:0;
         }
-        let gljList = this.filterAdjustGlj(projectGLJList, 'is_coe_adjust');
-         gljList = sortProjectGLJ(gljList);
-        for(let glj of gljList){
-            datas.push(getCoeObject(glj,totalFee));
-        }
-        return datas;
-        function getCoeObject(glj,totalFee) {
-            let data = materialAdjustObj.getCommonObject(glj);
-            data.FO = glj.FO;
-            data.FI = glj.FI;
-            data.totalPrice = materialAdjustObj.getTotalPrice(data);
-            if(totalFee !==0) data.varWeight = scMathUtil.roundForObj(data.totalPrice/totalFee,2);
-            return data;
-        }
-    },
-    getTotalPrice:function (data) {
-        return scMathUtil.roundForObj(data.quantity * data.marketPrice,getDecimal("bills.totalPrice"))
-    },
-    initRightClick : function() {
-        let me = this;
-        $.contextMenu({
-            selector: '#material_adjust_sheet',
-            build: function ($trigger, e) {
-                me.rightClickTarget = SheetDataHelper.safeRightClickSelection($trigger, e, me.spread);
-                return me.rightClickTarget.hitTestType === GC.Spread.Sheets.SheetArea.viewport ||
-                    me.rightClickTarget.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
-            },
-            items: {
-                "addFromGLJ":{
-                    name: '从人材机汇总中选择',
-                    icon: 'fa-sign-in',
-                    disabled: function () {
-                        return false;
-                    },
-                    callback: function (key, opt) {
-                        $("#selectFromGLJ").modal('show');
-                    }
-                },
-                "deleteAdjust": {
-                    name: "删除",
-                    icon: 'fa-trash-o',
-                    disabled: function () {
-                        return me.rightClickTarget.row === undefined;
-                    },
-                    callback: function (key, opt) {
-                        let row = me.rightClickTarget.row;
-                        me.deleteAdjust();
-                    }
-                }
+        for(let e of contractorList){
+            let t = {
+                ID:e.ID,
+                projectID: e.projectID,
+                is_related:e.is_related,//关联,1关,0不关
+                projectGLJID:e.projectGLJID,//关联工料机ID
+                seq:e.seq,//序号
+                code:e.code,
+                name:e.name,
+                specs:e.specs,
+                unit:e.unit,
+                type:e.type,
+                marketPrice:e.market_price,
+                quantity:e.quantity,
+                remark:e.remark,
+                supply:e.supply,
+                is_evaluate:0,
+                FO : e.FO,
+                FI : e.FI
+            };
+            let pglj = gljMap[e.projectGLJID];
+            if(e.is_related && pglj){
+                gljOprObj.setGLJPrice(t,pglj);
+                t.vender = pglj.vender;
+                t.supply = pglj.supply;
+                t.quantity = pglj.quantity;
+                t.is_evaluate = pglj.is_evaluate;
             }
-        });
+            t.totalPrice = scMathUtil.roundForObj(parseFloat(t.quantity) * parseFloat(t.marketPrice) ,billsDecimal);
+            if(totalFee !==0) t.varWeight = scMathUtil.roundForObj(t.totalPrice/totalFee,2);
+            datas.push(t);
+        }
+        return _.sortByAll(datas,'code');
     },
     getCommonObject:function (glj) {
         let data ={
@@ -272,6 +302,30 @@ let materialAdjustObj = {
         };
         gljOprObj.setGLJPrice(data,glj);
         return data;
+    },
+    checkedDefualtOption:function (className) {
+        for(let a of $("."+className)){
+            if(className == "pglj_sel_input"|| a.id == "glj_sel_material" || a.id == "glj_sel_main"||a.id =="glj_sel_eqp") $(a).prop("checked",true)
+        }
+    },
+    insertEmptyRow : function () {
+        let ndata = {type:"contractor_list",doc:{},action:"add"};
+        ndata.doc['ID'] = uuid.v1();
+        ndata.doc['is_related'] = 0;
+        ndata.doc['seq'] = "";
+        ndata.doc['projectID'] = projectObj.project.ID();
+        ndata.doc['code'] = "";
+        ndata.doc['name'] = "";
+        ndata.doc['unit'] = "";
+        ndata.doc['originPlace'] = "";
+        ndata.doc['vender'] = "";
+        ndata.doc['remark'] = "";
+        ndata.doc['supply'] = "";
+        ndata.doc['riskCoe'] = "";
+        ndata.doc['standardPrice'] = "";
+        ndata.doc['FO'] = "";
+        ndata.doc['FI'] = "";
+        configMaterialObj.updateEvaluateMaterial([ndata],false);
     }
 };
 
@@ -304,6 +358,8 @@ let gljFromObj={
         if(!this.spread){
             this.spread = SheetDataHelper.createNewSpread($("#glj_from_sheet")[0]);
             sheetCommonObj.spreadDefaultStyle(this.spread);
+        }else {
+            this.spread.refresh();
         }
         this.sheet = this.spread.getSheet(0);
         sheetCommonObj.initSheet(this.sheet,this.setting);
@@ -336,14 +392,13 @@ let gljFromObj={
         this.refreshDatas();
     },
     filterGLJ:function (gljList) {
-        let field =  $("#adjustType").val() == "priceInfo"?"is_info_adjust":"is_coe_adjust";
         let typeMap = {};
         for(let ch of $(".glj_sel_input:checked")){
             typeMap[$(ch).val()] = true;
         }
         let inputV = $("#glj_sel_input").val();
         return _.filter(gljList,function (item) {
-            if(item.quantity === 0 || item.quantity === '0' || item[field] ===1) return false;
+            if(item.quantity === 0 || item.quantity === '0') return false;
             if(!typeMap[0]){//如果没有勾选所有,下面再细分
                 let firstS = (item.type+"").substring(0,1);
                 if(!typeMap[firstS]) return false
@@ -367,6 +422,8 @@ let gljFromObj={
         data.short_name = projectGLJ.getShortNameByID(glj.type);
         // 只有材料才显示是否暂估
         if (materialIdList.indexOf(glj.type) >= 0) data.is_evaluate = glj.is_evaluate;
+        data.from = glj;
+        data.is_contractor_material = glj.is_contractor_material;
         return data;
     },
     checkByType:function (type) {
@@ -383,30 +440,83 @@ let gljFromObj={
                     if(d.type == 5) d.select = 1;
                     break;
                 case "取消":
-                    d.select = 0;
+                    d.select = d.is_contractor_material?1:0;
                     break;
             }
         }
-        //this.refreshDatas();
     },
     confirmSelect:function () {
-        let updateMap = {};
-        let adjustType =  $("#adjustType").val()
+        let tasks = [];
+        let datas = projectObj.project.contractor_list.datas,modelType= 'contractor_list',field= 'is_contractor_material';
+        let evaluateMap = _.indexBy(datas,"projectGLJID");
+        let evaluateKeyMap = {};//关键字映射表
+        for(let e of datas){
+            let eIndex = gljUtil.getIndex(e,gljUtil.materialKeyArray);
+            evaluateKeyMap[eIndex] = e;
+        }
         for(let d of this.datas){
-            if(d.select == 1){
-                let tem = {is_info_adjust:1,riskCoe:"",standardPrice:d.marketPrice+"",is_coe_adjust:1,FI:"",FO:""};//现在改成添加的话,两个页面都添加,删除两个页面都删除
-             /*   if(adjustType== "priceInfo"){
-                    tem = {is_info_adjust:1,riskCoe:"",standardPrice:d.marketPrice+""};
-                }else {
-                    tem = {is_coe_adjust:1,FI:"",FO:""};
-                }*/
-                updateMap[d.id] = tem;
+            if(d.select == 1){//如果是选中状态
+                if(d[field] == 1) {//如果已经标记为承包人材料
+                    let evl = evaluateMap[d.id];
+                    if(evl){
+                        //这里应该是错误检查的保证,按正常是不应该出现这种情况的
+                        if(evl.is_related !=1) tasks.push({type:modelType,doc:{projectGLJID:d.id,is_related:1},action:"update",ID:evl.ID});
+                    }else {
+                        tasks.push(getNewEval(d,modelType))
+                    }
+                }else {//如果不是暂估,要改成暂估
+                    let pdata = {id:d.id,doc:{},action:'update',type:"glj_list"};
+                    pdata.doc[field] = 1;
+                    tasks.push(pdata);
+                    let evaluate = evaluateKeyMap[gljUtil.getIndex(d,gljUtil.materialKeyArray)];
+                    if(evaluate) {//如果已经存在5个关键字相同的暂估材料,则自动关联起来
+                        let edata = {type:modelType,doc:{projectGLJID:d.id,is_related:1},action:"update",ID:evaluate.ID};
+                        tasks.push(edata);
+                    }else {//如果不存在,则生成一条新的
+                        tasks.push(getNewEval(d,modelType));
+                    }
+                }
+            }else {//未选中状态的,检查一下是否存在关联的情况,取消关联
+                let tdoc = {};
+                tdoc[field] = 0;
+                if(d[field] == 1)  tasks.push({id:d.id,doc:tdoc,action:'update',type:"glj_list"});
+                let evl = evaluateMap[d.id];
+                if(evl){
+                    if(evl.is_related ==1) tasks.push({type:modelType,doc:{is_related:0},action:"update",ID:evl.ID});
+                }
+                let evaluate = evaluateKeyMap[gljUtil.getIndex(d,gljUtil.materialKeyArray)];
+                if(evaluate) {
+                    if(evaluate.is_related ==1) tasks.push({type:modelType,doc:{is_related:0},action:"update",ID:evaluate.ID});
+                }
             }
         }
-        if( _.isEmpty(updateMap)) return;
-        projectObj.project.projectGLJ.batchUpdateGLJProperty(updateMap,function () {
-            materialAdjustObj.refreshSheetDatas();
-        });
+        if(tasks.length > 0) configMaterialObj.updateEvaluateMaterial(tasks);
+
+
+        function getNewEval(d,m_t) {
+            let ndata = {type:m_t,doc:{},action:"add"};
+            ndata.doc['ID'] = uuid.v1();
+            ndata.doc['projectGLJID'] = d.id;
+            ndata.doc['is_related'] = 1;
+            ndata.doc['seq'] = d.code;
+            ndata.doc['projectID'] = d.from.project_id;
+            ndata.doc['code'] = d.code;
+            ndata.doc['name'] = d.name;
+            ndata.doc['specs'] = d.specs;
+            ndata.doc['unit'] = d.unit;
+            ndata.doc['quantity'] = d.quantity;
+            ndata.doc['type'] = d.type;
+            ndata.doc['market_price'] = d.from.unit_price.market_price;
+            ndata.doc['originPlace'] = d.originPlace;
+            ndata.doc['vender'] = d.vender;
+            ndata.doc['remark'] = d.remark;
+            ndata.doc['supply'] = d.supply;
+            ndata.doc['riskCoe'] = "";
+            ndata.doc['standardPrice'] = d.from.unit_price.market_price;
+            ndata.doc['FO'] = "";
+            ndata.doc['FI'] = "";
+            return ndata;
+        }
     }
 };
 

+ 64 - 5
web/building_saas/main/js/views/project_glj_view.js

@@ -1,7 +1,7 @@
 /**
  * Created by zhang on 2018/3/13.
  */
-projectGljObject={
+let projectGljObject={
     showTag:'ration',//mixRatio/machine
     showMixRatioMark:'',
     displayType:filterType.ALL,
@@ -52,7 +52,8 @@ projectGljObject={
     ],
     initSpreads:function(){
         if(this.projectGljSpread==null) this.initProjectGljSpread();
-        if(materialAdjustObj.spread == null) materialAdjustObj.initSpread();
+        //if(materialAdjustObj.spread == null) materialAdjustObj.initSpread();
+        if(configMaterialObj.configSpread == null) configMaterialObj.initSpread();
     },
     initProjectGljSpread:function () {
         if(!this.projectGljSpread){
@@ -406,6 +407,7 @@ projectGljObject={
         let me = projectGljObject,updateMap = {};
         let projectGLJ = projectObj.project.projectGLJ;
         for(let c of changeInfo){
+            if(c.dataCode == 'is_evaluate') continue; //暂估,暂时跳过
             let recode = sheetName =="materialTreeSheet"?me.materialTree.items[c.row].data:me.projectGljSheetData[c.row];
             if(recode[c.dataCode] == c.value){
                 continue;
@@ -519,21 +521,36 @@ projectGljObject={
     },
     initSheetViews:function(){
         let me = projectGljObject;
-        if(me.displayType == filterType.AMAE){
+      /*  if(me.displayType == filterType.AMAE){
             $('#material_adjust').show();
             $('#adjustType').show();
             let adjustType = projectObj.project.projectInfo.property.gljAdjustType || 'priceInfo';
             $('#adjustType').val(adjustType);
             $('#project-glj-main').hide();
+            $('#config_material').hide();
+        }else*/
+      if(me.displayType == filterType.AMAE || me.displayType == filterType.ZGCL || me.displayType == filterType.PBCL){
+            $('#adjustType').hide();
+            if(me.displayType == filterType.AMAE){
+                let adjustType = projectObj.project.projectInfo.property.gljAdjustType || 'priceInfo';
+                $('#adjustType').val(adjustType);
+                $('#adjustType').show();
+            }
+            configMaterialObj.setNavLinkText(me.displayType);
+            $('#material_adjust').hide();
+            $('#project-glj-main').hide();
+            $('#config_material').show();
         } else {
             $('#material_adjust').hide();
             $('#adjustType').hide();
+            $('#config_material').hide();
             $('#project-glj-main').show();
         }
     },
     refreshViewsData:function(){
         let me = projectGljObject;
         if(me.displayType == filterType.AMAE) return materialAdjustObj.refreshSheetDatas();
+        if(me.displayType == filterType.ZGCL || me.displayType == filterType.PBCL) return configMaterialObj.refreshSheetDatas();
         me.refreshDataSheet();
     },
     refreshDataSheet:function () {
@@ -686,6 +703,7 @@ projectGljObject={
             type:glj.type,
             short_name:projectGLJ.getShortNameByID(glj.type),
             quantity:glj.quantity,
+            tenderQuantity:glj.tenderQuantity,
             supply:glj.supply,
             supply_quantity:glj.supply_quantity,
             materialType:glj.materialType,
@@ -752,6 +770,7 @@ projectGljObject={
     },
     initSheet: function (sheet,setting) {
         var me = this;
+
         sheetCommonObj.initSheet(sheet, setting, 30);
         sheet.bind(GC.Spread.Sheets.Events.ValueChanged, me.onSheetValueChange);
     },
@@ -928,6 +947,11 @@ projectGljObject={
             }
             if(dataCode === 'is_evaluate'||dataCode === 'is_adjust_price'||dataCode === 'is_main_material'||dataCode === 'no_tax_eqp'||dataCode == 'is_eval_material'){
                 value = value == true?1:0;
+                if(dataCode === 'is_evaluate'|| dataCode == 'is_eval_material'){
+                    configMaterialObj.updateConfigMaterial(dataCode,value,recode);//暂估材料,主要材料等新需求
+                    return
+                }
+
             }
             if(dataCode === 'materialType' && (value == null || value=="")){//删除三材类别时,清空三材系数
                 value = null;
@@ -938,6 +962,7 @@ projectGljObject={
             }
             extend = Object.keys(extend).length > 0 ?  JSON.stringify(extend) : '';
             if(recode[dataCode] == value) return;
+
             let updateData = {id: recode.id, field: dataCode, value: value, extend: extend};
             projectGLJ.pGljUpdate(updateData,callback);
         };
@@ -1116,10 +1141,28 @@ function getProjectResizeEles() {
     };
     return pojGljResizeEles;
 }
-
+function getConficMaterialResizeEles() {
+    let conficMaterialResizeEles = {};
+    conficMaterialResizeEles.eleObj = {
+        module: 'conficMaterial',
+        resize: $('#configMaterialResize'),
+        top: $('#configMaterialTop'),
+        topSpread: $('#config_material_sheet'),
+        bottom: $('#configMaterialBottom'),
+        bottomSpread: $('#related_sheet')
+    };
+    conficMaterialResizeEles.limit = {
+        min: 150,
+        max: `$(window).height()-$('.header').height()-$('#projectGljToolsBar').height()-150-5`,//5: resize.height()
+        notTopSpread: 0,
+        notBottomSpread: 0,
+        totalHeight: `$(window).height()-$('.header').height()-$('#projectGljToolsBar').height()-5`,
+        bottomNav:`$('#configMaterialBottom ul').height() + $('#filterToolDiv').height()+10`
+    };
+    return conficMaterialResizeEles;
+}
 
 function loadProjectGljSize() {
-
     if($('#project-glj-main').is(':visible')){
         let me = projectGljObject;
         let pojGljResizeEles = getProjectResizeEles();
@@ -1130,6 +1173,14 @@ function loadProjectGljSize() {
     }else if($('#material_adjust').is(':visible')){
         $('#material_adjust_sheet').height($(window).height()-$('.header').height()-$('#projectGljToolsBar').height()+3);
         if(materialAdjustObj.spread)materialAdjustObj.spread.refresh();
+    }else if($('#config_material').is(':visible')){
+        let  configMaterialResizeEles = getConficMaterialResizeEles();
+        SlideResize.loadVerticalHeight(configMaterialResizeEles.eleObj.module, configMaterialResizeEles.eleObj, configMaterialResizeEles.limit, function () {
+            configMaterialObj.configSpread? configMaterialObj.configSpread.refresh():'';
+            configMaterialObj.relatedSpread? configMaterialObj.relatedSpread.refresh():'';
+            /*
+            me.mixRatioSpread?me.mixRatioSpread.refresh():'';*/
+        });
     }
 
 }
@@ -1164,6 +1215,14 @@ $(function () {
         projectGljObject.projectGljSpread.refresh();
         projectGljObject.mixRatioSpread?projectGljObject.mixRatioSpread.refresh():'';
     });
+
+    let tr = getConficMaterialResizeEles();
+    SlideResize.verticalSlide(tr.eleObj, tr.limit, function(){
+        configMaterialObj.configSpread? configMaterialObj.configSpread.refresh():'';
+        configMaterialObj.relatedSpread? configMaterialObj.relatedSpread.refresh():'';
+    });
+
+
     $('#tab_project_glj').on('shown.bs.tab', function (e) {
         sessionStorage.setItem('mainTab', '#tab_project_glj');
         let me = projectGljObject;

+ 107 - 25
web/building_saas/main/js/views/project_view.js

@@ -839,7 +839,7 @@ var projectObj = {
         let fieldName = projectObj.mainController.setting.cols[info.col].data.field;
         //提前处理
         if (fieldName === 'code' || fieldName === 'name' || fieldName === 'unit') {
-            info.editingText = info.editingText ? info.editingText.toString().replace(new RegExp(/[\r,\n]/g), '') : info.editingText;
+            info.editingText = info.editingText ? info.editingText.toString().replace(new RegExp(/[\r\n]/g), '') : info.editingText;
         }
         // 检查输入类型等
         let value = projectObj.checkSpreadEditingText(info.editingText, colSetting);
@@ -1219,7 +1219,7 @@ var projectObj = {
                             }
                             if(isFlag(selected.data)&&selected.data.flagsIndex.fixed.flag==fixedFlag.SUB_ENGINERRING){//焦点行是分部分项工程
                                 if(selected.children.length>0){
-                                   return selected.children[0].data.type==billType.FX ||selected.children[0].data.type==billType.BX;//焦点行是分部分项工程,且子项是分项或补项
+                                    return selected.children[0].data.type==billType.FX ||selected.children[0].data.type==billType.BX;//焦点行是分部分项工程,且子项是分项或补项
                                 }else {
                                     return false
                                 }
@@ -1303,13 +1303,13 @@ var projectObj = {
                         ProjectController.addBills(project, controller);
                         projectObj.selectColAndFocus(project.mainTree.selected);
                     },
-                     visible: function(key, opt){
-                         if(project.mainTree.selected){
-                             return  project.Bills.isFBFX(project.mainTree.selected)==true?false:true;
-                         }else {
-                             return false;
-                         }
-                     }
+                    visible: function(key, opt){
+                        if(project.mainTree.selected){
+                            return  project.Bills.isFBFX(project.mainTree.selected)==true?false:true;
+                        }else {
+                            return false;
+                        }
+                    }
                 },
                 "spr1": '--------',
                 "insertRation": {
@@ -1326,12 +1326,12 @@ var projectObj = {
                     },
                     callback: function (key, opt) {
                         project.Ration.addNewRation(null,rationType.ration,projectObj.selectColAndFocus,false);
-                       // ProjectController.addRation(project, controller, rationType.ration);
+                        // ProjectController.addRation(project, controller, rationType.ration);
                     }/*,
-                    visible: function(key, opt){
-                        var selected = project.mainTree.selected;
-                        return canInsertRationNode(selected);
-                    }*/
+                     visible: function(key, opt){
+                     var selected = project.mainTree.selected;
+                     return canInsertRationNode(selected);
+                     }*/
                 },
                 "insertLJ": {
                     name: "插入量价",//插入量价不需要自动定位到编号列
@@ -1343,9 +1343,9 @@ var projectObj = {
                         return !project.Ration.canAdd(project.mainTree.selected);
                     },
                     callback: function (key, opt) {
-                       /* project.Ration.addNewRation(null,rationType.volumePrice,function (newNode) {
-                            projectObj.selectColAndFocus(newNode,null);
-                        },true);*/
+                        /* project.Ration.addNewRation(null,rationType.volumePrice,function (newNode) {
+                         projectObj.selectColAndFocus(newNode,null);
+                         },true);*/
                     },
                     items:{
                         insertLabour:{
@@ -1356,7 +1356,7 @@ var projectObj = {
                             }
                         },
                         insertMaterial:{
-                           name:"材料" ,
+                            name:"材料" ,
                             icon: 'fa-sign-in',
                             callback:function(key){
                                 project.Ration.insertVolumePrice(gljType.GENERAL_MATERIAL);
@@ -1463,18 +1463,90 @@ var projectObj = {
                     },
                 },
                 "setBookMark": {
-                    name: '设置/取消书签批注',
+                    name: '设置书签批注',
                     icon: 'fa-flag',
                     disabled: function () {
-                        if (projectReadOnly || project.mainTree.selected.sourceType == ModuleNames.ration_glj) {
+                        if (projectReadOnly || !project.mainTree.selected || project.mainTree.selected.sourceType == ModuleNames.ration_glj) {
                             return true;
                         }
                         return false
                     },
-                    callback: function () {
-                        locateObject.setOrCancelBookmark(project.mainTree.selected)
+                    items: {
+                        cancelMark: {
+                            name: '取消书签批注',
+                            icon: 'fa-remove',
+                            disabled: function () {
+                                const selected = project.mainTree.selected;
+                                return !(selected && selected.data.bookmarkBackground);
+                            },
+                            callback: function () {
+                                locateObject.setOrCancelBookmark(project.mainTree.selected, null);
+                            }
+                        },
+                        markE2F2C5: {
+                            name: function () {
+                                return locateObject.getDescribeByColor('E2F2C5');
+                            },
+                            icon: 'fa-square annotate-color-1',
+                            callback: function () {
+                                locateObject.setOrCancelBookmark(project.mainTree.selected, 'E2F2C5');
+                            }
+                        },
+                        markF9E2CF: {
+                            name: function () {
+                                return locateObject.getDescribeByColor('F9E2CF');
+                            },
+                            icon: 'fa-square annotate-color-2',
+                            callback: function () {
+                                locateObject.setOrCancelBookmark(project.mainTree.selected, 'F9E2CF');
+                            }
+                        },
+                        markF2EFD9: {
+                            name: function () {
+                                return locateObject.getDescribeByColor('F2EFD9');
+                            },
+                            icon: 'fa-square annotate-color-3',
+                            callback: function () {
+                                locateObject.setOrCancelBookmark(project.mainTree.selected, 'F2EFD9');
+                            }
+                        },
+                        markF5D1DA: {
+                            name: function () {
+                                return locateObject.getDescribeByColor('F5D1DA');
+                            },
+                            icon: 'fa-square annotate-color-4',
+                            callback: function () {
+                                locateObject.setOrCancelBookmark(project.mainTree.selected, 'F5D1DA');
+                            }
+                        },
+                        markE3E3E3: {
+                            name: function () {
+                                return locateObject.getDescribeByColor('E3E3E3');
+                            },
+                            icon: 'fa-square annotate-color-5',
+                            callback: function () {
+                                locateObject.setOrCancelBookmark(project.mainTree.selected, 'E3E3E3');
+                            }
+                        },
+                        markB6F3F2: {
+                            name: function () {
+                                return locateObject.getDescribeByColor('B6F3F2');
+                            },
+                            icon: 'fa-square annotate-color-6',
+                            callback: function () {
+                                locateObject.setOrCancelBookmark(project.mainTree.selected, 'B6F3F2');
+                            }
+                        },
+                        markECE0F5: {
+                            name: function () {
+                                return locateObject.getDescribeByColor('ECE0F5');
+                            },
+                            icon: 'fa-square annotate-color-7',
+                            callback: function () {
+                                locateObject.setOrCancelBookmark(project.mainTree.selected, 'ECE0F5');
+                            }
+                        },
                     }
-
                 },
                 "delete": {
                     name: '删除',
@@ -1553,6 +1625,16 @@ var projectObj = {
                         BlockController.pasteBlock(project.mainTree.selected);
                     }
                 },
+                "recoverDeletedNodes": {
+                    name: '恢复删除节点',
+                    icon: 'fa-undo',
+                    disabled: function (){
+                        return BlockController.recoverBlockDisabled();
+                    },
+                    callback: function () {
+                        BlockController.recoverBlock();
+                    }
+                },
                 "editEngineer": {
                     name: '编辑工程内容',
                     icon: 'fa-edit',
@@ -1560,8 +1642,8 @@ var projectObj = {
                         return projectReadOnly;
                     },
                     visible: function(key, opt){
-                         let  selected = project.mainTree.selected;
-                         return selected.sourceType==ModuleNames.bills&&project.Bills.isEngineerEst(selected);//当焦点行是“专业工程暂估价”时,右键可见并有效。
+                        let  selected = project.mainTree.selected;
+                        return selected.sourceType==ModuleNames.bills&&project.Bills.isEngineerEst(selected);//当焦点行是“专业工程暂估价”时,右键可见并有效。
                     },
                     callback: function () {
                         let node = project.mainTree.selected;//project.Bills.getNodeByFlag(project.mainTree.selected,fixedFlag.ENGINEERING_ESITIMATE);

+ 1 - 0
web/building_saas/main/js/views/tender_price_view.js

@@ -48,6 +48,7 @@ let tender_obj={
         this.tenderSheet = this.tenderSpread.getSheet(0);
         this.tenderTree = cacheTree.createNew(this);
         this.tenderTreeSetting = this.createTenderTreeSetting();
+        console.log(this.tenderTreeSetting);
         TREE_SHEET_HELPER.initSetting($('#tenderSpread')[0], this.tenderTreeSetting );
         this.tenderTreeSetting.setAutoFitRow = MainTreeCol.getEvent("setAutoFitRow");
         this.tenderController = TREE_SHEET_CONTROLLER.createNew(this.tenderTree, this.tenderSheet, this.tenderTreeSetting);

+ 2 - 1
web/building_saas/main/js/views/zmhs_view.js

@@ -222,7 +222,8 @@ let zmhs_obj = {
         }
         projectObj.mainController.refreshTreeNode(nodes, false);
 
-        let rationID= ration_glj.updateCacheAfterAdjust(result.ration_glj);
+        let rationID = result.ration.ID;
+        ration_glj.updateCacheAfterAdjust(result.ration_glj);
         if(result.projectGLJDatas){//有添加、替换、工料机等需重新加载的情况
             projectObj.project.projectGLJ.refreshByDatas(result.projectGLJDatas);
             if(result.add && result.add.length > 0) ration_glj.addToMainTree(result.add);//这个方法有再去项目工料机那里取价格,所以要在回调里调用,不像替换工料的情况

+ 29 - 15
web/building_saas/pm/html/project-management.html

@@ -5,7 +5,7 @@
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     <meta http-equiv="x-ua-compatible" content="ie=edge">
-    <title>项目管理-纵横建筑计价</title>
+    <title>项目管理-大司空云计价</title>
     <link rel="stylesheet" href="/lib/jquery-ui/jquery-ui.css" type="text/css">
     <link rel="stylesheet" href="/lib/bootstrap/css/bootstrap.min.css">
     <link rel="stylesheet" href="/web/building_saas/css/main.css">
@@ -49,6 +49,7 @@
 <img src="/web/dest/css/img/share.png" id="share_pic" style="display: none">
 <img src="/web/dest/css/img/copy.png" id="copy_pic" style="display: none">
 <img src="/web/dest/css/img/work.png" id="work_pic" style="display: none">
+<img src="/web/dest/css/img/edit.png" id="edit_pic" style="display: none">
 <div class="header">
     <div class="top-msg clearfix">
         <div class="alert alert-warning mb-0 py-0" role="alert" style="display: none;">
@@ -102,7 +103,15 @@
                         <div class="toolsbar">
                             <legend class="m-0 px-4">项目管理
                                 <div class="tools-btn btn-group align-top px-3 mt-2">
-                                    <a id="startA" href="javascript:void(0);" data-toggle="modal" data-target="#addPoj3" class="btn btn-sm"><i class="fa fa-sticky-note-o"></i> 新建</a>
+                                    <div class="btn btn-sm">
+                                        <a class="dropdown-toggle" id="addMenuBtn" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false"><i class="fa fa-plus"></i> 新建</a>
+                                        <div class="dropdown-menu dropright" id="addMenu">
+                                            <a href="javascript:;" id="addFolderSub" class="dropdown-item" data-toggle="modal" data-target="#add-folder-dialog"><i class="fa fa-folder-open-o" aria-hidden="true"></i> 新建文件夹</a>
+                                            <a href="javascript:;" id="addProjectSub" class="dropdown-item" data-toggle="modal" data-target="#add-project-dialog"><i class="fa fa-cubes" aria-hidden="true"></i> 新建建设项目</a>
+                                            <a href="javascript:;" id="addEngSub" class="dropdown-item" data-toggle="modal" data-target="#add-engineering-dialog"><i class="fa fa-cube" aria-hidden="true"></i> 新建单项工程</a>
+                                            <a href="javascript:;" id="addTenderSub" class="dropdown-item" data-toggle="modal" data-target="#add-tender-dialog"><i class="fa fa-sticky-note-o" aria-hidden="true"></i> 新建单位工程</a>
+                                        </div>
+                                    </div>
                                     <a id="upLevel" href="javascript:void(0);" class="btn btn-light btn-sm disabled" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="升级"><i class="fa fa-arrow-left" aria-hidden="true"></i></a>
                                     <a id="downLevel" href="javascript:void(0);" class="btn btn-light btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="降级"><i class="fa fa-arrow-right" aria-hidden="true"></i></a>
                                     <a id="upMove" href="javascript:void(0);" class="btn btn-light btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
@@ -235,7 +244,7 @@
                     <div class="form-group row">
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">建设项目</label>
                         <div class="col">
-                            <input type="text" class="form-control form-control-sm" placeholder="输入建设项目名称" id="project-name">
+                            <input type="text" class="form-control form-control-sm" placeholder="输入建设项目名称" id="project-name" autofocus="autofocus">
                             <span class="form-text text-danger" id="project-name-info" style="display: none;">已存在 “建筑工程1”</span>
                         </div>
                     </div>
@@ -303,7 +312,7 @@
                 <form>
                     <div class="form-group">
                         <label>单项工程</label>
-                        <input type="text" class="form-control" placeholder="输入单项工程名称" id="engineering-name">
+                        <input type="text" class="form-control" placeholder="输入单项工程名称" id="engineering-name" autofocus="autofocus">
                         <span class="form-text text-danger" id="engineering-name-info" style="display: none;">已存在 “建筑工程1”</span>
                     </div>
                 </form>
@@ -398,7 +407,7 @@
                     <div class="form-group row">
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">单位工程</label>
                         <div class="col">
-                            <input type="text" class="form-control form-control-sm" placeholder="输入单位工程名称" id="tender-name" autocomplete="off">
+                            <input type="text" class="form-control form-control-sm" placeholder="输入单位工程名称" id="tender-name" autocomplete="off" autofocus="autofocus">
                         </div>
                     </div>
                     <span class="form-text text-danger" id="tender-name-info" style="display: none;">已存在 “建筑工程1”</span>
@@ -508,9 +517,9 @@
                 <form>
                     <div class="form-group">
                         <label>文件夹</label>
-                        <input type="text" class="form-control" placeholder="输入文件夹名称" id="folder-name">
+                        <input type="text" class="form-control" placeholder="输入文件夹名称" id="folder-name" autofocus="autofocus">
                         <span class="form-text text-danger" id="folder-name-info" style="display: none;">已存在 “建筑工程1”</span>
-                        <span class="form-text text-muted">Smartcost目前最多支持3层文件夹。</span>
+                        <span class="form-text text-muted">目前最多支持3层文件夹。</span>
                     </div>
                 </form>
             </div>
@@ -534,7 +543,7 @@
             <div class="modal-body">
                 <form>
                     <div class="form-group">
-                        <input type="text" class="form-control" placeholder="输入名称" id="rename-name">
+                        <input type="text" class="form-control" placeholder="输入名称" id="rename-name" autofocus="autofocus">
                         <span class="form-text text-danger" id="rename-name-info" style="display: none;">已存在 “建筑工程1”</span>
                     </div>
                 </form>
@@ -626,7 +635,7 @@
                 <div class="row">
                     <div class="col-6">
                         <h5>选择项目</h5>
-                        <div class="modal-auto-height">
+                        <div style="height: 400px">
                             <div id="replaceSpread" style="height: 100%"></div>
                         </div>
                         <div class="custom-control custom-checkbox">
@@ -715,7 +724,7 @@
                 <!--添加分享-->
                 <div class="form-group">
                     <div class="input-group input-group-sm">
-                        <input id="sharePhone" type="text" class="form-control" placeholder="输入 手机号 添加分享">
+                        <input id="sharePhone" type="text" class="form-control" placeholder="输入 手机号 添加分享" autofocus="autofocus">
                        <!-- <div class="input-group-append">
                             <button class="btn btn-primary" type="button" id="button-addon2">添加分享</button>
                         </div>-->
@@ -723,7 +732,7 @@
                 </div>
                 <table class="table table-sm" id="shareFindDiv">
                     <tbody style="display: block">
-                    <tr><th style="width: 112px;">姓名</th><th style="width: 165px;">公司</th><th style="width: 136px;">手机</th><th style="width: 160px;">邮箱</th><th style="width: 90px;">允许拷贝</th><th style="width: 90px;">允许协作</th><th style="width: 90px;">添加分享</th></tr>
+                    <tr><th style="width: 112px;">姓名</th><th style="width: 165px;">公司</th><th style="width: 136px;">手机</th><th style="width: 160px;">邮箱</th><th style="width: 90px;">允许拷贝</th><th style="width: 90px;">允许编辑</th><th style="width: 90px;">添加分享</th></tr>
                     <tr><td id="user_name">张三</td><td id="user_company">XX公司</td><td id="user_mobile">12345678900</td><td id="user_email"></td><td><input id="allowCopy" type="checkbox"></td><td><input id="allowCooperate" type="checkbox"></td><td><a id="share-confirm" href="javascript:void(0)" class="btn btn-sm btn-primary">添加分享</a></td></tr>
                     </tbody>
                 </table>
@@ -746,17 +755,21 @@
         </div>
     </div>
 </div>
-<!--弹出 新用户第一次进度条-->
+<!--进度条-->
 <div class="modal fade" id="progress" data-backdrop="static">
     <div class="modal-dialog" role="document">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 id="progress-title" class="modal-title">欢迎使用纵横建筑计价</h5>
+                <h5 id="progress-title" class="modal-title"></h5>
             </div>
             <div class="modal-body">
-                <h5 id="progress-content" class="my-3">首次加载例题,请稍候……</h5>
+                <h5 id="progress-content" class="my-3"></h5>
                 <div class="progress mb-3">
-                    <div class="progress-bar progress-bar-striped progress-bar-animated" id="progressBar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
+                    <!--<div class="progress-bar progress-bar-striped progress-bar-animated" id="progressBar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">-->
+                    <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
+                        <div class="progress-move progress-bar-striped"></div>
+                        <div class="progress-cover" id="progressCover"></div>
+                    </div>
                 </div>
             </div>
         </div>
@@ -951,6 +964,7 @@
 <script src="/lib/JSExpressionEval_src/Date.js"></script>
 <script src="/web/building_saas/glj/js/socket.io.slim.js"></script>
 <script src="/public/web/socket/connection.js"></script>
+<script src="/public/billsUtil.js"></script>
 <script src="/web/building_saas/main/js/models/importStandardInterface.js"></script>
 <script src="/web/building_saas/pm/js/pm_ajax.js"></script>
 <script src="/web/building_saas/pm/js/pm_newMain.js"></script>

+ 5 - 1
web/building_saas/pm/js/pm_import.js

@@ -415,7 +415,7 @@ const importView = (() => {
             if (tbcObj && tbcObj.checkValid()) {
                 return;
             }
-            let pr = new SCComponent.InitProgressBar($('#progress'), $('#progress-title'), $('#progress-content'), $('#progressBar'));
+            let pr = new SCComponent.InitProgressBar();
             try {
                 //建设项目设置选择的文件类型和选择的计税方法
                 xmlObj.property.fileKind = tbcObj.fileKind;
@@ -443,6 +443,10 @@ const importView = (() => {
                     xmlObj.name += `(${moment(Date.now()).format('YYYY-MM-DD HH:mm:ss')})`;
                 }
                 $('#importInterface').modal('hide');
+                //TEST======================
+                /*let importData = await importXML.transformData(xmlObj);
+                console.log(importData);*/
+                //TEST======================
                 pr.start('导入文件', '正在生成文件,请稍候……');
                 let importData = await importXML.transformData(xmlObj);
                 let blob = new Blob([JSON.stringify(importData)], {type: 'text/plain;charset=utf-8'});

+ 129 - 45
web/building_saas/pm/js/pm_newMain.js

@@ -21,9 +21,9 @@ let needfulInfoData = null;
 let featureData = null; //工程特征数据
 let projectType = {
     folder: 'Folder',
-    tender: 'Tender',
     project: 'Project',
-    engineering: 'Engineering'
+    engineering: 'Engineering',
+    tender: 'Tender'
 };
 let fileType = {
     unitPriceFile: 'UnitPriceFile',
@@ -216,55 +216,81 @@ const projTreeObj = {
             this.bindEvent(newWorkBook);
             this.loadContextMenu();
             this.loadStartMenu();
+        }else {
+            workBook.refresh();
         }
         return newWorkBook;
     },
+    // 获取新建按钮是否是disabled
+    getAddBtnDisabled: function (action) {
+        switch (action) {
+            case projectType.folder:
+            case projectType.project:
+            case projectType.tender:
+                return false;
+            case projectType.engineering:
+                const node = projTreeObj.tree.selected;
+                return !node || node.data.projType === projectType.folder;
+            default:
+                return true;
+        }
+    },
+    refreshAddMenuItems: function () {
+        const items = [{
+            el: $('#addFolderSub'),
+            action: projectType.folder,
+        }, {
+            el: $('#addProjectSub'),
+            action: projectType.project,
+        }, {
+            el: $('#addEngSub'),
+            action: projectType.engineering,
+        }, {
+            el: $('#addTenderSub'),
+            action: projectType.tender,
+        }];
+        items.forEach(item => {
+            const disabled = this.getAddBtnDisabled(item.action);
+            if (disabled) {
+                item.el.addClass('disabled');
+            } else {
+                item.el.removeClass('disabled');
+            }
+        });
+    },
     //右键菜单项
     contextMenuItems: {
         addFolder: {
             name: "新建文件夹",
             icon: 'fa-folder-o',
             disabled: function () {
-                return false;
+                return projTreeObj.getAddBtnDisabled(projectType.folder);
             },
             callback: function (key, opt) {
                 $("#add-folder-dialog").modal("show");
-                setTimeout(function () {
-                    $('#folder-name')[0].focus();
-                }, 300);
             }
         },
         addProject: {
             name: "新建建设项目",
             icon: 'fa-cubes',
             disabled: function () {
-                return false;
+                return projTreeObj.getAddBtnDisabled(projectType.project);
             },
             callback: function (key, opt) {
                 $('#add-project-dialog').modal('show');
-                setTimeout(function () {
-                    $('#project-name')[0].focus();
-                }, 300);
             },
         },
         addEngineering: {
             name: "新建单项工程",
             icon: 'fa-cube',
             disabled: function () {
-                let node = projTreeObj.tree.selected;
-                if(node && node.data.projType !== projectType.folder){
-                    return false;
-                }
-                return true;
+                return projTreeObj.getAddBtnDisabled(projectType.engineering);
             },
             callback: function (key, opt) {
                 let selectedItem = projTreeObj.tree.selected;
                 try {
                     if(selectedItem !== null  && selectedItem.data.projType !== projectType.folder){
                         $("#add-engineering-dialog").modal("show");
-                        setTimeout(function () {
-                            $('#engineering-name')[0].focus();
-                        }, 300);
                     }
 
                 } catch (error) {
@@ -276,7 +302,7 @@ const projTreeObj = {
             name: "新建单位工程",
             icon: 'fa-sticky-note-o',
             disabled: function () {
-                return false;
+                return projTreeObj.getAddBtnDisabled(projectType.tender);
             },
             callback: function (key, opt) {
                 //弹出新建单位工程之前,判断当前使用版本,且当前使用单位工程数是否已到最大值
@@ -294,7 +320,7 @@ const projTreeObj = {
                 $('#rename-name').val(projTreeObj.tree.selected.data.name ? projTreeObj.tree.selected.data.name : '');
                 $('#rename-dialog').modal('show');
                 setTimeout(function () {
-                    $('#rename-name')[0].focus();
+                    $('#rename-name').focus();
                 }, 300);
             }
         },
@@ -1153,6 +1179,9 @@ const projTreeObj = {
                     me.setFileSelectButton(i,j,nodes[i],sheet,setting);
                 }
                 sheet.getCell(i, 1,GC.Spread.Sheets.SheetArea.viewport).locked(true);
+                if (typeof nodes[i].visible !== 'undefined') {
+                    sheet.setRowVisible(nodes[i].serialNo(), nodes[i].visible);
+                }
             }
         };
         me.renderSheetFuc(sheet, fuc);
@@ -1490,25 +1519,34 @@ const projTreeObj = {
             refreshNodes = refreshNodes.concat(me.calEngineeringCost(oldProject));
             refreshNodes = refreshNodes.concat(me.calEngineeringCost(parent));
         }
-        // projTreeObj.remove(sheet, fromRow, rCout);
-
         me.renderSheetFuc(sheet, function () {
             sheet.deleteRows(fromRow, rCout);
             sheet.addRows(newNode.serialNo(),rCout);
             let oldSelection = sheet.getSelections()[0];
             me.initSelection({row: newNode.serialNo(), rowCount: oldSelection.rowCount}, oldSelection,sheet);
             sheet.setSelection(newNode.serialNo(),oldSelection.col,oldSelection.rowCount,oldSelection.colCount);
-            let children = newNode.getAllChildren();
-            children.push(newNode);
-            for(let c of children){
-                sheet.getCell(c.serialNo(), 0).cellType(me.getTreeNodeCell(me.tree));
-            }
-            refreshNodes = refreshNodes.concat(children);
+            const newNodes = [newNode];
+            const children = newNode.getAllChildren();
+            newNodes.push(...children);
+            for(let node of newNodes){
+                const row = node.serialNo();
+                sheet.getCell(row, 0).cellType(me.getTreeNodeCell(me.tree));
+                const visible = node.visible;
+                sheet.setRowVisible(row, visible);
+            }
+            refreshNodes = refreshNodes.concat(newNodes);
             me.refreshNodeData(refreshNodes);
         });
 
         function addNewNodes(node,parent,next) {
-            let newNode = me.tree.addNodeData(node.data, parent, next);
+            const nodeState = {};
+            if (typeof node.expanded !== 'undefined') {
+                nodeState.expanded = node.expanded;
+            }
+            if (typeof node.visible !== 'undefined') {
+                nodeState.visible = node.visible;
+            }
+            let newNode = me.tree.addNodeData(node.data, parent, next, nodeState);
             for(let c of node.children){
                 addNewNodes(c,newNode,null);
             }
@@ -1565,7 +1603,6 @@ const projTreeObj = {
                 }
 
             }
-            $.bootstrapLoading.progressEnd();
             //  console.log(JSON.parse(sumString));
 
             saveProjectFile(sumString);
@@ -1573,6 +1610,9 @@ const projTreeObj = {
         }catch (e){
             alert("导出失败!请查看log.");
             console.log(e)
+        }finally {
+            await  setTimeoutSync(null,500);//设置间隔
+            $.bootstrapLoading.progressEnd();
         }
 
 
@@ -1686,10 +1726,20 @@ $(document).ready(function() {
 
     //绑定新建建设项目、新建单项工程、新建文件夹、重命名Enter键事件
     bindInputs($('#project-name'), $('#engineering-name'), $('#folder-name'), $('#rename-name'));
-
+    //绑定新建窗口显示事件
+    bindModalShown($('#add-folder-dialog'), $('#add-project-dialog'), $('#add-engineering-dialog'), $('#add-tender-dialog'));
     //绑定新建建设项目、新建单项工程、新建文件夹、重命名弹窗隐藏事件
     bindModalsHidden($('#add-project-dialog'), $('#add-engineering-dialog'), $('#add-folder-dialog'), $('#rename-dialog'));
 
+    // 新建子菜单有效性刷新
+    $('#addMenuBtn').click(function () {
+        const menuDisplay = !$('#addMenu').is(':visible');
+        if (!menuDisplay) {
+            return;
+        }
+        projTreeObj.refreshAddMenuItems();
+    });
+
     // 新增建设项目操作
     $('#addProjOk').click(function () {
         if (!validRequiredData($('#projInfoStep'))) {
@@ -1780,6 +1830,21 @@ $(document).ready(function() {
         }
     });
 
+    function bindModalShown(folderDialog, projDialog, engDialog, tenderDialog) {
+        folderDialog.on('shown.bs.modal', function () {
+            $('#folder-name').focus();
+        });
+        projDialog.on('shown.bs.modal', function () {
+            $('#project-name').focus();
+        });
+        engDialog.on('shown.bs.modal', function () {
+            $('#engineering-name').focus();
+        });
+        tenderDialog.on('shown.bs.modal', function () {
+            $('#tender-name').focus();
+        });
+    }
+
     //新建建设项目、新建单项工程、新建文件夹、重命名弹窗隐藏事件
     function bindModalsHidden(projDialog, engDialog, folderDialog, renameDialog){
         projDialog.on('hidden.bs.modal', function () {
@@ -2033,9 +2098,6 @@ $(document).ready(function() {
         if (!$('#firstStep').is(':visible')) {
             showFirstStep();
         }
-        setTimeout(function () {
-            $('#tender-name')[0].focus();
-        }, 300);
     });
     //新建单位工程-文件类型选择变换(影响到基本信息数据)
     $('input[name="fileKind-tender"]').click(function () {
@@ -2719,6 +2781,24 @@ function getWorkBookWidth(){
     return workBookWidth = $(window).width() - $('.pm-side').width() - 90;
 }
 
+// 初始化节点可见性,默认收起至建设项目层
+function initNodesVisibility(sheet, nodes, visible) {
+    function recurSetVisible(nodes, visible) {
+        nodes.forEach(node => {
+            node.visible = visible;
+            if (node.children) {
+                recurSetVisible(node.children, visible);            }
+        });
+    }
+    nodes.forEach(node => {
+        const isProject = node.data.projType === projectType.project;
+        if (isProject) {
+            node.expanded = visible;
+            recurSetVisible(node.children, visible);
+        }
+    });
+}
+
 function initProjects(callback) {
     GetAllProjectData(function (datas) {
         //设置工程专业
@@ -2732,15 +2812,16 @@ function initProjects(callback) {
             projTreeObj.tree = pmTree.createNew(projTreeObj.setting, datas);
             projTreeObj.tree.selected = projTreeObj.tree.items[0];
             projTreeObj.workBook = projTreeObj.buildSheet(projTreeObj.workBook,'projSpread',projTreeObj.setting);
-            projTreeObj.workBook.getSheet(0).frozenColumnCount(2);
-            projTreeObj.workBook.getSheet(0).name('projectSheet');
+            const sheet = projTreeObj.workBook.getSheet(0);
+            sheet.frozenColumnCount(2);
+            sheet.name('projectSheet');
             sheetCommonObj.spreadDefaultStyle(projTreeObj.workBook);
             projTreeObj.sumEngineeringCost();
-            projTreeObj.showTreeData(projTreeObj.tree.items, projTreeObj.setting, projTreeObj.workBook.getActiveSheet());
+            initNodesVisibility(sheet, projTreeObj.tree.items, false);
+            projTreeObj.showTreeData(projTreeObj.tree.items, projTreeObj.setting, sheet);
             //初始选择
-            let initSel = projTreeObj.workBook.getSheet(0).getSelections()[0] ? projTreeObj.workBook.getSheet(0).getSelections()[0] : {row: 0, rowCount: 1};
-            projTreeObj.initSelection(initSel,null,projTreeObj.workBook.getActiveSheet());
-           // $.bootstrapLoading.end();
+            const initSel = sheet.getSelections()[0] ? sheet.getSelections()[0] : { row: 0, rowCount: 1 };
+            projTreeObj.initSelection(initSel, null, sheet);
             autoFlashHeight();
             projTreeObj.workBook.refresh();
             if (callback) {
@@ -2761,8 +2842,8 @@ function init(refresh = false) {
     //init spread and pmTree
     if(refresh == false) socketObject.connect('pm');//socket 连接;
     if (isFirst) {
-        let pr = new SCComponent.InitProgressBar($('#progress'), $('#progress-title'), $('#progress-content'), $('#progressBar'));
-        pr.start('欢迎使用纵横建筑计价', '首次加载例题,请稍候……');
+        let pr = new SCComponent.InitProgressBar();
+        pr.start('欢迎使用大司空计价', '首次加载例题,请稍候……');
         CommonAjax.post('/pm/api/prepareInitialData', {user_id: userID}, function () {
             initProjects(function () {
                 pr.end();
@@ -2788,7 +2869,7 @@ function refreshAllPage(){
 
 
 function whiteSpaceCollapse(v) {
-    v = v.replace(/[\r,\n,\t]/g, ' ');
+    v = v.replace(/[\r\n\t]/g, ' ');
     v = v.trim();
     return v.replace(/\s{1,}/g, ' ');
 }
@@ -3345,7 +3426,7 @@ function setProjOptions(projs, selected){
         setFileOptions(firstProj.data.ID);
         setEngOptions(firstProj.data.ID);
         for(let i = 0, len = projs.length; i < len; i++){
-            let $proj = $("<a>").val(projs[i].data.ID).text(projs[i].data.name);
+            let $proj = $(`<a class='text-ellipsis' title="${projs[i].data.name}">`).val(projs[i].data.ID).text(projs[i].data.name);
             $proj.addClass("dropdown-item");
             $proj.attr("href", "javascript:void(0);");
             $proj.click(function () {
@@ -4570,6 +4651,9 @@ $("#confirm-import").click(function() {
                 $.bootstrapLoading.progressEnd();
                 if(response.error == 1){
                     alert(response.msg);
+                    setTimeout(function () {
+                        $.bootstrapLoading.progressEnd();//不做这个的话太快,页面不会自动关闭
+                    },500)
                 }else {
                     refreshAllPage();
                 }
@@ -4644,7 +4728,7 @@ function setShareToModal(selected){
                                           <th style="width: 136px;">手机</th>
                                           <th style="width: 136px;">邮箱</th>
                                           <th style="width: 90px;">允许拷贝</th>
-                                          <th style="width: 90px;">允许协作</th>
+                                          <th style="width: 90px;">允许编辑</th>
                                           <th style="width: 90px;">取消分享</th>
                                </tr>`;
         infoArr.push(theadHtml);

+ 4 - 3
web/building_saas/pm/js/pm_share.js

@@ -407,7 +407,7 @@ const pmShare = (function () {
     }
     //互动单元格
     function getInteractionCell() {
-        let workImg = document.getElementById('work_pic'),
+        let editImg = document.getElementById('edit_pic'),
             workImgWidth = 13,
             workImgHeight = 13,
             copyImg = document.getElementById('copy_pic'),
@@ -433,10 +433,11 @@ const pmShare = (function () {
                 let acStyle = options.sheet.getActualStyle(options.row, options.col),
                     zoom = options.sheet.zoom();
                 let textLength = this.getAutoFitWidth(value, text, acStyle, zoom, {sheet: options.sheet, row: options.row, col: options.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport});
-                let nowX  = Math.floor(x) + textLength + 3,
+                const imgIndent = 5;
+                let nowX  = Math.floor(x) + textLength + imgIndent,
                     nowY = Math.floor((y + (y + h)) / 2) - 7;
                 if (node.data.allowCooperate) {
-                    ctx.drawImage(workImg, nowX, nowY, workImgWidth, workImgHeight);
+                    ctx.drawImage(editImg, nowX, nowY, workImgWidth, workImgHeight);
                     nowX += workImgWidth;
                 }
                 if (node.data.allowCopy) {

+ 31 - 8
web/building_saas/pm/js/pm_tree.js

@@ -4,11 +4,10 @@
 const pmTree = {
     createNew: function (setting, arrData) {
         function sortTreeItems(tree) {
-            var addItems = function (items) {
-                var i;
-                for (i = 0; i < items.length; i++) {
-                    tree.items.push(items[i]);
-                    addItems(items[i].children);
+            const addItems = function (items) {
+                for (const item of items) {
+                    tree.items.push(item);
+                    addItems(item.children);
                 }
             };
             tree.items.splice(0, tree.items.length);
@@ -16,7 +15,7 @@ const pmTree = {
         }
 
         var Node = (function () {
-            function Node(tree, data) {
+            function Node(tree, data, nodeState = null) {
                 this.parent = null;
                 this.nextSibling = null;
                 this.children = [];
@@ -29,6 +28,11 @@ const pmTree = {
 
                 this.row = null;
                 this.expandBtn = null;
+                if (nodeState) {
+                    for (const attr in nodeState) {
+                        this[attr] = nodeState[attr];
+                    }
+                }
             }
 
             Node.prototype.firstChild = function() {
@@ -246,9 +250,14 @@ const pmTree = {
                     node.parent.children.splice(iIndex, 1);
                 }
                 sortTreeItems(this);
+                this.sourceData = this.items.reduce((acc, cur) => {
+                    acc.push(cur.data);
+                    return acc;
+                }, []);
             };
 
             Tree.prototype.loadData = function (arrData) {
+                this.sourceData = arrData;
                 let i, that = this;
                 let nodesIndex = {};
                 function getPreNode(id){
@@ -313,11 +322,12 @@ const pmTree = {
                 sortTreeItems(this);
             };
 
-            Tree.prototype.addNodeData = function (data, parent, nextSibling) {
+            Tree.prototype.addNodeData = function (data, parent, nextSibling, nodeState = null) {
+                this.sourceData.push(data);
                 var node = null;
                 var pNode = parent ? parent : this._root;
                 if (!nextSibling || (nextSibling.parent === pNode && pNode.childIndex(nextSibling) > -1)) {
-                    node = new Node(this, data);
+                    node = new Node(this, data, nodeState);
                     this.maxNodeId(data[this.setting.tree.id]);
                     this.move(node, pNode, nextSibling);
                 }
@@ -375,6 +385,15 @@ const pmTree = {
             };
 
             Tree.prototype.check = function (_root) {
+                if (this.sourceData.length !== this.items.length) {
+                    const data = this.sourceData.filter(item => {
+                        const findData = this.items.find(node => node.data.ID === item.ID);
+                        return !findData;
+                    });
+                    console.log('丢失数据:');
+                    console.log(data);
+                    return false;
+                }
                 return isValid(_root.children);
                 function isValid(nodes) {
                     for (let node of nodes) {
@@ -403,6 +422,10 @@ const pmTree = {
                             console.log(`${node.serialNo() + 1}:${node.data.name} node索引大于next索引`);
                             return false;
                         }
+                        if (node.nextSibling && node.parent !== node.nextSibling.parent) {
+                            console.log(`${node.serialNo() + 1}:${node.data.name} 与兄弟节点 ${node.nextSibling.serialNo() + 1}:${node.nextSibling.data.name} 父节点不同`);
+                            return false;
+                        }
                         if (node.children.length) {
                             let v = isValid(node.children);
                             if (!v) {

+ 13 - 1
web/common/html/header.html

@@ -16,6 +16,18 @@
     <div class="navbar-text p-0 navbar-expand-sm">
         <!--大屏菜单-->
         <ul class="nav navbar-nav" id="fluid-menu">
+            <li class="nav-item dropdown" id="showqqgroup" >
+                <a class="nav-link text-danger" href="#"><i class="fa fa-qq" ></i> 群</a>
+                <div class="dropdown-menu p-3 dropdown-menu-right" id="qqgroup" style="width: 200px;top:36px">
+                    <p class="text-center">
+                        <a href="https://jq.qq.com/?_wv=1027&k=5XivMJY" target="_blank" class="btn btn-sm btn-outline-primary">点击加入QQ交流群</a>
+                    </p>
+                    <p class="mb-0 text-center text-muted">
+                        <img src="/web/building_saas/img/qqgroupqrcode.png">
+                        扫码加QQ交流群
+                    </p>
+                </div>
+            </li>
             <li class="nav-item dropdown">
                 <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" id="link_userName"><%= sessionUser.real_name === '' ? sessionUser.mobile : sessionUser.real_name %></a>
                 <div class="dropdown-menu dropdown-menu-right">
@@ -280,4 +292,4 @@
 <script type="text/javascript" src="/public/web/commonAlert.js"></script>
 <script type="text/javascript" src="/public/web/headerOpr.js"></script>
 <script type="text/javascript" src="/lib/jquery-editable-select/jquery.editable-select.min.js"></script>
-<!-- endinject -->
+<!-- endinject -->

+ 0 - 0
web/dest/css/img/edit.png


Некоторые файлы не были показаны из-за большого количества измененных файлов