浏览代码

Merge branch 'master' into 定额优化

zhangweicheng 5 年之前
父节点
当前提交
b52eb7f713
共有 44 个文件被更改,包括 1965 次插入544 次删除
  1. 32 0
      config/gulpConfig.js
  2. 32 10
      gulpfile.js
  3. 30 0
      modules/all_models/stdGlj_lib.js
  4. 4 1
      modules/all_models/unit_price_file.js
  5. 4 0
      modules/glj/controllers/glj_controller.js
  6. 17 34
      modules/glj/models/glj_list_model.js
  7. 12 8
      modules/glj/models/unit_price_model.js
  8. 2 1
      modules/pm/controllers/pm_controller.js
  9. 73 34
      modules/pm/facade/pm_facade.js
  10. 1 1
      modules/pm/models/project_model.js
  11. 4 3
      modules/ration_glj/controllers/ration_glj_controller.js
  12. 6 0
      modules/ration_glj/facade/ration_glj_facade.js
  13. 2 2
      modules/reports/util/rpt_excel_util.js
  14. 97 0
      modules/unit_price_file/controllers/unit_price_controller.js
  15. 161 0
      modules/unit_price_file/facade/unit_price_facade.js
  16. 14 0
      modules/unit_price_file/routes/unit_price_router.js
  17. 2 0
      public/common_constants.js
  18. 10 1
      public/web/gljUtil.js
  19. 1 1
      public/web/sheet/sheet_common.js
  20. 47 33
      public/web/socket/connection.js
  21. 60 5
      socket.js
  22. 6 6
      web/building_saas/complementary_glj_lib/html/tools-gongliaoji.html
  23. 5 0
      web/building_saas/complementary_glj_lib/js/glj.js
  24. 2 2
      web/building_saas/complementary_ration_lib/html/dinge.html
  25. 2 2
      web/building_saas/complementary_ration_lib/js/init.js
  26. 256 247
      web/building_saas/css/custom.css
  27. 33 12
      web/building_saas/css/main.css
  28. 11 1
      web/building_saas/css/style.css
  29. 3 1
      web/building_saas/glj/html/project_glj.html
  30. 4 4
      web/building_saas/main/js/models/ration_glj.js
  31. 36 29
      web/building_saas/main/js/views/glj_view.js
  32. 3 2
      web/building_saas/main/js/views/glj_view_contextMenu.js
  33. 6 6
      web/building_saas/main/js/views/options_view.js
  34. 90 47
      web/building_saas/main/js/views/project_glj_view.js
  35. 42 26
      web/building_saas/main/js/views/project_info.js
  36. 2 1
      web/building_saas/main/js/views/project_view.js
  37. 1 1
      web/building_saas/pm/js/pm_import.js
  38. 3 2
      web/building_saas/pm/js/pm_newMain.js
  39. 5 4
      web/building_saas/report/js/rpt_main.js
  40. 235 0
      web/building_saas/unit_price_file/index.html
  41. 591 0
      web/building_saas/unit_price_file/index.js
  42. 1 0
      web/common/components/share/index.js
  43. 9 9
      web/over_write/js/chongqing_2018.js
  44. 8 8
      web/users/html/index.html

+ 32 - 0
config/gulpConfig.js

@@ -296,6 +296,38 @@ module.exports = {
         'public/web/common_ajax.js',
         'web/building_saas/complementary_ration_lib/js/global.js',
         'web/building_saas/complementary_ration_lib/js/installation.js'
+    ],
+    unitPrice_css:[],
+    unitPrice_jspaths:[
+      "lib/popper/popper.min.js",
+      "lib/bootstrap/bootstrap.min.js",
+      "lib/bootstrap/bootstrap-submenu.js",
+      "web/building_saas/glj/js/socket.io.slim.js",
+      "public/web/socket/connection.js",
+      "web/building_saas/js/moment.min.js",
+      "web/building_saas/js/message.js",
+      "public/web/scMathUtil.js",
+      "public/web/gljUtil.js",
+      "public/web/PerfectLoad.js",
+      "lib/lodash/lodash.js",
+      "public/web/commonAlert.js",
+      "public/web/headerOpr.js",
+      "public/web/common_ajax.js",
+      "public/common_util.js",
+      "public/common_constants.js",
+      "public/web/treeDataHelper.js",
+      "public/web/ztree_common.js",
+      "web/building_saas/main/js/models/main_consts.js",
+      "lib/jquery-editable-select/jquery.editable-select.min.js",
+      "public/web/tree_sheet/tree_sheet_helper.js",
+      "public/web/sheet/sheet_data_helper.js",
+      "public/web/sheet/sheet_common.js",
+      "public/web/slideResize.js",
+      "web/building_saas/main/js/views/glj_view_contextMenu.js",
+      "web/building_saas/main/js/views/glj_view.js",
+      "web/building_saas/main/js/views/options_view.js",
+      'web/building_saas/main/js/views/project_glj_view.js',
+      "web/building_saas/unit_price_file/index.js",
     ]
 }
 

+ 32 - 10
gulpfile.js

@@ -26,6 +26,7 @@ let compleRation_coe_jspaths = config.compleRation_coe_jspaths;
 let compleRation_coe_csspaths = config.compleRation_coe_css;
 let compleRation_inst_csspaths = config.compleRation_inst_css;
 let compleRation_inst_jspaths = config.compleRation_inst_jspaths;
+let unitPrice_jspaths = config.unitPrice_jspaths;
 let version=config.version;
 let cssDest='web/dest/css';
 let scriptsDest='web/dest/scripts';
@@ -38,7 +39,7 @@ let commonOptions={
 
 let headerOptions={
     version:version,
-    scriptsDest:'web/dest/scripts',
+    scriptsDest:scriptsDest,
     concatName:'header.all.min',
     srcHtml:'web/src/html/common/header.html',
     htmlDest:'web/common/html',
@@ -48,7 +49,7 @@ let headerOptions={
 
 let loginOptions={
     version:version,
-    scriptsDest:'web/dest/scripts',
+    scriptsDest:scriptsDest,
     jspaths:login_jspaths,
     concatName:'login.all.min',
     srcHtml:'web/src/html/login/login.html',
@@ -61,7 +62,7 @@ let loginOptions={
 
 let pmOptions={
     version:version,
-    scriptsDest:'web/dest/scripts',
+    scriptsDest:scriptsDest,
     jspaths:pm_jspaths,
     csspaths:pm_csspaths,
     concatName:'pm.all.min',
@@ -75,7 +76,7 @@ let pmOptions={
 
 let mainOptions={
     version:version,
-    scriptsDest:'web/dest/scripts',
+    scriptsDest:scriptsDest,
     jspaths:main_jspaths,
     csspaths:main_csspaths,
     concatName:'main.all.min',
@@ -89,7 +90,7 @@ let mainOptions={
 
 let compleGljOptions = {
     version: version,
-    scriptsDest: 'web/dest/scripts',
+    scriptsDest: scriptsDest,
     jspaths: compleGlj_jspaths,
     csspaths: compleGlj_csspaths,
     concatName: 'compleGlj.all.min',
@@ -105,7 +106,7 @@ let compleGljOptions = {
 
 let compleRation_rationOptions = {
     version: version,
-    scriptsDest: 'web/dest/scripts',
+    scriptsDest: scriptsDest,
     jspaths: compleRation_ration_jspaths,
     csspaths: compleRation_ration_csspaths,
     concatName: 'compleRation_ration.all.min',
@@ -122,7 +123,7 @@ let compleRation_rationOptions = {
 
 let compleRation_gljOptions = {
     version: version,
-    scriptsDest: 'web/dest/scripts',
+    scriptsDest: scriptsDest,
     jspaths: compleRation_glj_jspaths,
     csspaths: compleRation_glj_csspaths,
     concatName: 'compleRation_glj.all.min',
@@ -139,7 +140,7 @@ let compleRation_gljOptions = {
 
 let compleRation_coeOptions = {
     version: version,
-    scriptsDest: 'web/dest/scripts',
+    scriptsDest: scriptsDest,
     jspaths: compleRation_coe_jspaths,
     csspaths: compleRation_coe_csspaths,
     concatName: 'compleRation_coe.all.min',
@@ -156,7 +157,7 @@ let compleRation_coeOptions = {
 
 let compleRation_instOptions = {
     version: version,
-    scriptsDest: 'web/dest/scripts',
+    scriptsDest: scriptsDest,
     jspaths: compleRation_inst_jspaths,
     csspaths: compleRation_inst_csspaths,
     concatName: 'compleRation_inst.all.min',
@@ -171,6 +172,16 @@ let compleRation_instOptions = {
     ]
 }
 
+let unitPriceOptions={
+  version:version,
+  scriptsDest:scriptsDest,
+  jspaths:unitPrice_jspaths,
+  concatName:'unitPrice.all.min',
+  htmlDest:'web/building_saas/unit_price_file',
+  htmlName:'index.html',
+  injectList:['web/dest/scripts/unitPrice.all.min.'+version+'.js']
+}
+
 function minify(options) {
     if(options.jspaths){
         return gulp.src(options.jspaths)
@@ -364,6 +375,17 @@ gulp.task('compleRation_inst', ['compleRation_inst_inject'], function () {
     return htmlmin(compleRation_instOptions);
 });
 
+gulp.task('unitPrice_minify', function (){
+  return minify(unitPriceOptions);
+});
+
+gulp.task('unitPrice_inject',['unitPrice_minify'],function (){
+  return inject(unitPriceOptions);
+})
+
+gulp.task('unit_price',['unitPrice_inject'], function (){
+  return htmlmin(unitPriceOptions);
+});
 
 
-gulp.task('build',['header','login','pm','main', 'compleGlj', 'compleRation_ration', 'compleRation_glj', 'compleRation_coe', 'compleRation_inst']);
+gulp.task('build',['header','login','pm','main', 'compleGlj', 'compleRation_ration', 'compleRation_glj', 'compleRation_coe', 'compleRation_inst','unit_price']);

+ 30 - 0
modules/all_models/stdGlj_lib.js

@@ -0,0 +1,30 @@
+/**
+ * Created by Zhong on 2018/3/23.
+ */
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+const oprSchema = require('../all_schemas/opr_schema');
+let gjlMapRationLibsSchema = new Schema(
+    {
+        ID: Number,
+        dispName: String
+    },
+    {_id: false},
+    {versionKey: false}
+);
+let gljMapSchema = new Schema({
+        deleted: Boolean,
+        ID: Number,
+        dispName: String,
+        appType: String,
+        creator: String,
+        createDate: String,
+        recentOpr: [oprSchema],
+        rationLibs: [gjlMapRationLibsSchema],
+        compilationId: String,
+        compilationName: String
+    },
+    {versionKey: false}
+);
+
+mongoose.model('std_glj_lib_map', gljMapSchema, 'std_glj_lib_map');

+ 4 - 1
modules/all_models/unit_price_file.js

@@ -11,7 +11,10 @@ let Schema = mongoose.Schema;
 let collectionName = 'unit_price_file';
 let modelSchema = {
     // 自增id
-    id: Number,
+    id: {
+      type: Number,
+      index: true
+    },
     // 标段id
     project_id: {
         type: Number,

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

@@ -230,6 +230,10 @@ class GLJController extends BaseController {
      * @return {void}
      */
     async deleteMixRatio(request, response) {
+        if(request.body.data){
+          let data = request.body.data;
+          request.body = JSON.parse(data);
+        }
         let id = request.body.id;
         id = parseInt(id);
         let responseData = {

+ 17 - 34
modules/glj/models/glj_list_model.js

@@ -11,13 +11,12 @@ import CounterModel from "./counter_model";
 import UnitPriceModel from "./unit_price_model";
 import UnitPriceFileModel from "./unit_price_file_model";
 import GLJTypeConst from "../../common/const/glj_type_const";
-import RationGLJFacade from "../../ration_glj/facade/ration_glj_facade";
 import STDGLJLibGLJListModel from "../../common/std/std_glj_lib_glj_list_model";
 import MixRatioModel from "./mix_ratio_model";
 import GljModel from "../../complementary_glj_lib/models/gljModel";
 const ProjectModel = require('../../pm/models/project_model').project;
 const scMathUtil = require('../../../public/scMathUtil').getUtil();
-import decimal_facade from "../../main/facade/decimal_facade";
+import unitPriceFacade from "../../unit_price_file/facade/unit_price_facade";
 let gljCollectionName = 'glj_list';
 let GLJSchemas = mongoose.model(gljCollectionName);
 let _ = require("lodash");
@@ -90,7 +89,7 @@ class GLJListModel extends BaseModel {
             // 首先获取对应标段下所有的项目工料机数据
             let condition = {project_id: projectId};
             let fields = {_id: 0};
-            gljData = await this.db.find(condition, fields);
+            gljData = await this.model.find(condition, fields).lean();
             // 没有数据则直接返回空
             if (gljData.length <= 0) {
                 throw '无数据';
@@ -99,35 +98,19 @@ class GLJListModel extends BaseModel {
             // 获取标段设置的单价文件数据
             let unitPriceModel = new UnitPriceModel();
             unitPriceList = await unitPriceModel.getDataByFileId(unitPriceFileId);
-            // 整理获取工料机ID list
-            let gljIdList = [];
-            for(let tmp of gljData) {
-                gljIdList.push(tmp.id);
-                let c_key = this.getIndex(tmp,['code','name','specs','unit','type']);
-                keyMap[tmp.id] = c_key; //工料机ID和连接key的对照表;
-            }
-            // 从定额工料机库中获取消耗量
-            condition = {
-                projectID: projectId
-               // projectGLJIDList: gljIdList 这里是取所有项目工料机,所以也是取所有定额工料机,不需要根据项目工料机ID再过滤
-            };
-            //为了提高性成计算消耗量功能改成了在前端计算
-            // 整理获取有组成物的项目工料机的数据
             let connect_keys = [];
             for(let tmp of gljData) {
-                // 有组成物的类型才查找
-                let key = keyMap[tmp.id];
+                let c_key = this.getIndex(tmp,['code','name','specs','unit','type']);
                 if(this.ownCompositionTypes.indexOf(tmp.type)!=-1){
-                    connect_keys.push(key);
-                }
-                /*if (tmp.type === GLJTypeConst.CONCRETE || tmp.type === GLJTypeConst.MORTAR ||
-                    tmp.type === GLJTypeConst.MIX_RATIO || tmp.type === GLJTypeConst.GENERAL_MACHINE|| tmp.type === GLJTypeConst.MAIN_MATERIAL){
-                    connect_keys.push(key);
-                }*/
+                  connect_keys.push(c_key);
+               }
+               keyMap[c_key] = tmp; //项目工料机连接key和工料机的对照表;
             }
+        
             // 查找组成物的消耗量
             let totalComposition = {};
             let mixRatioData = {};
+            let missMaxRatio = [];//由于共享单价文件,单价文件编辑器造成的,存在组成物和对应的单价,但是项目工料机不存的情况
             if (connect_keys.length > 0) {
                 let mixRatioModel = new MixRatioModel();
                 condition = {connect_key: {"$in": connect_keys}, unit_price_file_id: unitPriceFileId};
@@ -135,15 +118,6 @@ class GLJListModel extends BaseModel {
                 for (let tmp of mixRatioList) {
                    let t_index = tmp.connect_key;
                    let m_index = this.getIndex(tmp,['code','name','specs','unit','type']);
-                   /*
-                    let consumption=parseFloat(tmp.consumption);
-                    let r_quantity = quantityList[t_index]?quantityList[t_index]:0;
-                   if(quantityList[m_index]!==undefined){
-                       quantityList[m_index]=quantityList[m_index]+r_quantity*consumption;
-                   }else {
-                       quantityList[m_index] = r_quantity*consumption;
-                   }
-                    quantityList[m_index] = scMathUtil.roundTo(quantityList[m_index],-quantity_decimal);*/
                     if (mixRatioData[t_index] !== undefined) {
                         mixRatioData[t_index].push(tmp);
                     } else {
@@ -159,8 +133,17 @@ class GLJListModel extends BaseModel {
                     } else {
                         mixRatioConnectData[m_index] = [tmp.connect_key];
                     }
+                    //检查该组成物对应的项目工料机是否存在  
+                    if(!keyMap[m_index]) missMaxRatio.push(tmp);//这里可能会有重复,但是后面的添加操作中会去重 
                 }
             }
+
+            if(missMaxRatio.length > 0){
+              let newList =  await unitPriceFacade.getNewProjectGLJFromMissMixratio(projectId,missMaxRatio,keyMap,[]);
+              await unitPriceFacade.setIDfromCounter("glj_list",newList);
+              await this.model.insertMany(newList);
+              gljData=gljData.concat(newList);         
+            }
             // 组合单价数据
             gljData = this.combineData(gljData, unitPriceList, {}, mixRatioData, totalComposition);
             // 排序

+ 12 - 8
modules/glj/models/unit_price_model.js

@@ -294,24 +294,28 @@ class UnitPriceModel extends BaseModel {
         let tasks = [];
         let parentTask = [];
         let newValueMap = {};
+        let needCheckDatas= [];
         for(let d of data){//第一次循环生成更新提交的记录,并生成一个新值的映射表,为更新父节点使用
             let condition = {id:d.unit_price.id,unit_price_file_id:d.unit_price.unit_price_file_id};
-            let doc = {};
-            doc[d.field]=d.newval;
-            newValueMap[d.unit_price.id] = doc;
+            let doc = d.ext?d.ext:{};
+            if(d.field){//共用接口后有可能只更新其它属性,不更新价格
+              doc[d.field]=d.newval;
+              newValueMap[d.unit_price.id] = doc;
+              needCheckDatas.push(d);
+            }
             tasks.push(this.generateUpdateTask(condition,doc));
         }
-        for(let d of data){//第二次更新父节点
-           let rList = await this.checkAndUpdateParent(d.unit_price,d.field,d.project_id,newValueMap,true);
-           parentTask = parentTask.concat(rList);
-        }
+        for(let d of needCheckDatas){//第二次更新父节点
+          let rList = await this.checkAndUpdateParent(d.unit_price,d.field,d.project_id,newValueMap,true);
+          parentTask = parentTask.concat(rList);
+       }
         tasks = tasks.concat(parentTask);
         tasks.length>0?this.model.bulkWrite(tasks):'';
         return parentTask;
     }
 
     async updateParentUnitPrice(mixRatio,fieid,project_id,newValueMap,batchUpdate){//batchUpdate 批量更新标记,如果true,只生成task
-        let  decimalObject =await decimal_facade.getProjectDecimal(project_id);
+        let  decimalObject =project_id?await decimal_facade.getProjectDecimal(project_id):null
         let quantity_decimal = (decimalObject&&decimalObject.glj&&decimalObject.glj.quantity)?decimalObject.glj.quantity:3;
         let price_decimal = (decimalObject&&decimalObject.glj&&decimalObject.glj.unitPrice)?decimalObject.glj.unitPrice:2;
         //查找该工料机所有组成物

+ 2 - 1
modules/pm/controllers/pm_controller.js

@@ -258,7 +258,8 @@ module.exports = {
                 projInfo.shareState = await pm_facade.getShareState(projectID, userID);
             }
             // 获取项目所属用户
-            projInfo.owner = await userModel.findOne({_id: mongoose.Types.ObjectId(project.userID)}, 'real_name');
+            projInfo.owner = await userModel.findOne({_id: mongoose.Types.ObjectId(project.userID)}, 'real_name mobile').lean();
+            projInfo.opener = await userModel.findOne({_id: mongoose.Types.ObjectId(userID)}, 'real_name mobile').lean();
             callback('', consts.projectConst.PROJECT_INFO, project);
         }, function (err) {
             callback(err, consts.projectConst.PROJECT_INFO, {});

+ 73 - 34
modules/pm/facade/pm_facade.js

@@ -436,6 +436,7 @@ async function handleCopyProject(key, userID, compilationID, data, newProjectID)
 async function copyProject(userID, compilationID,data,newProjectID = null, delayProjectTask = false) {
     let projectMap = data.projectMap;
     let originalID = projectMap['copy'].document.ID;
+    
     if (!newProjectID) {
         newProjectID = await getCounterID("projects");
     }
@@ -493,6 +494,7 @@ async function copyProject(userID, compilationID,data,newProjectID = null, delay
     ];
     let [billMap,rationMap,projectGLJMap] = await Promise.all(IDtasks);
     //所有复制任务异步处理,提升效率  复制清单,定额,4个文件,项目工料机, 定额工料机,人工系数,工程量明细,定额安装增加费,安装增加费
+
     let copyTasks = [
         copyProjectSetting(originalID,newProjectID),
         copyBills(newProjectID,billMap),
@@ -534,12 +536,12 @@ async function copyProject(userID, compilationID,data,newProjectID = null, delay
 async function createIDsAndReturn(originalID,model) {
     let uuidMaping = {};//做ID映射
     let datas = [];
-    let result = await model.find({"projectID": originalID}, '-_id');
+    let result = await model.find({"projectID": originalID}, '-_id').lean();
     uuidMaping['-1'] = -1;
     //建立uuid-ID映射
     for(let d of result){
         uuidMaping[d.ID] = uuidV1();
-        datas.push(d._doc);
+        datas.push(d);
     }
     return{uuidMaping:uuidMaping,datas:datas};
 }
@@ -547,16 +549,16 @@ async function createIDsAndReturn(originalID,model) {
 async function getProjectGLJIDAndReturn(originalID,newProjectID) {
     let IDMap = {};
     let datas = [];
-    let result = await gljListModel.find({project_id:originalID}, '-_id');
+    let result = await gljListModel.find({project_id:originalID}, '-_id').lean();
     let gljCount = await counter.counterDAO.getIDAfterCountSync(counter.moduleName.glj_list, result.length);
     for(let i = 0; i < result.length; i++){
         let d = result[i];
         let newID = gljCount.sequence_value - (result.length - 1) + i;
         //let newID = await getCounterID("glj_list");
         IDMap[d.id] = newID;
-        d._doc.project_id = newProjectID;
-        d._doc.id = newID;
-        datas.push(d._doc);
+        d.project_id = newProjectID;
+        d.id = newID;
+        datas.push(d);
     }
     return{IDMap:IDMap,datas:datas};
 }
@@ -689,12 +691,15 @@ async function copyUnitPriceFile(newProjectID,rootProjectID,userID,originaluUnit
     }
 
     async function copySubList(srcFID,newFID,model) {
-        let mList = await model.find({unit_price_file_id: srcFID}, '-_id');
+        let mList = await model.find({unit_price_file_id: srcFID}, '-_id').lean();
         let rList = [];
+        let IDcounter = await counter.counterDAO.getIDAfterCountSync(model.modelName, mList.length);
+        let firstID = IDcounter.sequence_value - (mList.length - 1);
         for(let m of mList){
-            m._doc.unit_price_file_id = newFID;
-            m._doc.id = await getCounterID(model.modelName);
-            rList.push(m._doc);
+            m.unit_price_file_id = newFID;
+            m.id = firstID;    
+            firstID+=1
+            rList.push(m);
         }
         await insertMany(rList,model)
     }
@@ -716,26 +721,17 @@ async function copyInstallFee(originalPID,newProjectID) {
 }
 
 async function copyRationSubList(originalPID,newProjectID,billIDMap,rationIDMap,projectGLJIDMap,model) {// 定额工料机,附注条件,工程量明细,定额安装增加费,模板子目
-    let subList = await model.find({projectID:originalPID}, '-_id');
+    let first = +new Date();
+    let subList = await model.find({projectID:originalPID}, '-_id').lean();
     let newList =[];
     for(let s of subList){
-        s._doc = getCopyRationSubData(s._doc,newProjectID,billIDMap,rationIDMap,projectGLJIDMap);
-  /*      s._doc.ID = uuidV1();
-        s._doc.projectID = newProjectID;
-        s._doc.rationID&&rationIDMap[s._doc.rationID]?s._doc.rationID = rationIDMap[s._doc.rationID]:'';
-        s._doc.billID&&billIDMap[s._doc.billID]?s._doc.billID = billIDMap[s._doc.billID]:'';
-        s._doc.billsItemID&&billIDMap[s._doc.billsItemID]?s._doc.billsItemID = billIDMap[s._doc.billsItemID]:'';
-        s._doc.projectGLJID&&projectGLJIDMap[s._doc.projectGLJID]?s._doc.projectGLJID = projectGLJIDMap[s._doc.projectGLJID]:'';
-        if(s._doc.templateList && s._doc.templateList.length > 0 ){
-            for(let t of s._doc.templateList){
-                if(t.billID && billIDMap[t.billID]) t.billID =  billIDMap[t.billID];
-                if(t.fxID && billIDMap[t.fxID]) t.fxID =  billIDMap[t.fxID];
-            }
-        }*/
-        newList.push(s._doc);
+        s = getCopyRationSubData(s,newProjectID,billIDMap,rationIDMap,projectGLJIDMap);
+        newList.push(s);
 
     }
-    await insertMany(newList,model);
+    if(newList.length > 0) await insertMany(newList,model);
+    let last = +new Date();
+    console.log(model.modelName+"-------"+newList.length+"--copyRationSubList - insert时间为-------------------------------"+(last-first));
 }
 
 
@@ -926,10 +922,10 @@ async function getCounterID(collectionName){
 }
 
 async function insertMany(datas,model) {
-    while (datas.length>1000){//因为mongoose限制了批量插入的条数为1000.所以超出限制后需要分批插入
+    /* while (datas.length>1000){//因为mongoose限制了批量插入的条数为1000.所以超出限制后需要分批插入  2020-05-28 测试后发现没有限制,暂时先保留代码
         let newList = datas.splice(0,1000);//一次插入1000条
         await model.insertMany(newList);
-    }
+    } */
     await model.insertMany(datas);
 
 }
@@ -2407,6 +2403,7 @@ async function importProjects(data,req,updateData) {
     let result = {error:0};
     let stringArr = data.split("|----|");
     let datas = [];
+    let first = +new Date();
     for(let s of stringArr){
         datas.push(JSON.parse(cipher.aesDecrypt(s)));
     }
@@ -2431,13 +2428,48 @@ async function importProjects(data,req,updateData) {
             let [constructionProjectID,projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap] = await handleMainProjectDatas(mainData,updateData,req.session.sessionUser.id);
             result.constructionProjectID = constructionProjectID;
             if(datas.length > 1 ){
+                let newProjectSettings=[],bills=[],rations=[],installationFees=[],projectGLJs=[],rationGLJs=[],rationCoes=[],quantityDetails=[],rationInstallations=[],rationTemplates=[],evaluateList=[],bidList=[],contractorList=[],newCalcProgramsFiles=[],newLabourCoes=[]
                 for(let i = 1;i<datas.length;i++){
-                    await handleEachProject(datas[i],projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap)
+                  let [tnewProjectSetting,tbills,trations,tinstallationFees,tprojectGLJs,trationGLJs,trationCoes,tquantityDetails,trationInstallations,trationTemplates,tevaluateList,tbidList,tcontractorList,tnewCalcProgramsFile,tnewLabourCoe] = await handleEachProject(datas[i],projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap)
+                  if(tnewProjectSetting) newProjectSettings.push(tnewProjectSetting);
+                  if(tbills.length > 0) bills = bills.concat(tbills);
+                  if(trations.length > 0) rations = rations.concat(trations);
+                  if(tinstallationFees.length > 0) installationFees = installationFees.concat(tinstallationFees);
+                  if(tprojectGLJs.length > 0) projectGLJs = projectGLJs.concat(tprojectGLJs);
+                  if(trationGLJs.length > 0) rationGLJs = rationGLJs.concat(trationGLJs);
+                  if(trationCoes.length > 0) rationCoes = rationCoes.concat(trationCoes);
+                  if(tquantityDetails.length > 0) quantityDetails = quantityDetails.concat(tquantityDetails);
+                  if(trationInstallations.length > 0) rationInstallations = rationInstallations.concat(trationInstallations);
+                  if(trationTemplates.length > 0) rationTemplates = rationTemplates.concat(trationTemplates);
+                  if(tevaluateList.length > 0) evaluateList = evaluateList.concat(tevaluateList);
+                  if(tbidList.length > 0) bidList = bidList.concat(tbidList);
+                  if(tcontractorList.length > 0)  contractorList = contractorList.concat(tcontractorList);
+                  if(tnewCalcProgramsFile) newCalcProgramsFiles.push(tnewCalcProgramsFile);
+                  if(tnewLabourCoe) newLabourCoes.push(tnewLabourCoe); 
                 }
+
+                if(newProjectSettings.length > 0) await insertMany(newProjectSettings,projectSettingModel)
+                if(bills.length > 0) await insertMany(bills,billsModel);
+                if(rations.length > 0) await insertMany(rations,rationModel);
+                if(installationFees.length > 0) await insertMany(installationFees,installationFeeModel);
+                if(projectGLJs.length > 0) await insertMany(projectGLJs,gljListModel);
+                if(rationGLJs.length > 0) await insertMany(rationGLJs,rationGLJModel);
+                if(rationCoes.length > 0) await insertMany(rationCoes,rationCoeModel);
+                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(newCalcProgramsFiles.length > 0) await insertMany(newCalcProgramsFiles,calcProgramsModel);
+                if(newLabourCoes.length > 0) await insertMany(newLabourCoes,labourCoesModel); 
+
             }
 
         }
     }
+    let finish = +new Date();
+    console.log("导入操作总时间为-------------------------------"+(finish-first));
     return result;
 }
 
@@ -2526,7 +2558,9 @@ async function handleEachProject(data,projectIDMap,labourCoeFileIDMap,calcProgra
         delete newLabourCoe._id;
     }
 
-    if(newProjectSetting) await projectSettingModel.create(newProjectSetting);
+    return [newProjectSetting,bills,rations,installationFees,projectGLJs,rationGLJs,rationCoes,quantityDetails,rationInstallations,rationTemplates,evaluateList,bidList,contractorList,newCalcProgramsFile,newLabourCoe]
+
+ /*    if(newProjectSetting) await projectSettingModel.create(newProjectSetting);
     if(bills.length > 0) await insertMany(bills,billsModel);
     if(rations.length > 0) await insertMany(rations,rationModel);
     if(installationFees.length > 0) await insertMany(installationFees,installationFeeModel);
@@ -2540,8 +2574,8 @@ async function handleEachProject(data,projectIDMap,labourCoeFileIDMap,calcProgra
     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);
-
+    if(newLabourCoe) await labourCoesModel.create(newLabourCoe); */
+ 
 
 }
 function setMaterialList(datas,newProjectID,projectGLJIDMap){
@@ -2629,6 +2663,8 @@ async function handleMainProjectDatas(mainData,updateData,userID) {
 
 
 async function importUnitPriceFiles(mainData,projectIDMap,unitPriceFileIDMap,userID) {
+    
+  
     if(!mainData.files.unitFiles) return;
     let unitFiles = [],unitPrices =[],mixRatios=[],freights=[],originals=[];
     for(let f of mainData.files.unitFiles){
@@ -2650,11 +2686,14 @@ async function importUnitPriceFiles(mainData,projectIDMap,unitPriceFileIDMap,use
     if(unitPrices.length > 0) await insertMany(unitPrices,unitPriceModel);
     if(mixRatios.length > 0) await insertMany(mixRatios,mixRatioModel);
 
-    async function setSubList(oList,nList,fileID,model,UUID=false) {
+    async function setSubList(oList,nList,fileID,model) {
+      let IDcounter = await counter.counterDAO.getIDAfterCountSync(model.modelName, oList.length);
+      let firstID = IDcounter.sequence_value - (oList.length - 1);
         for(let o of oList){
             delete o._id;
             o.unit_price_file_id = fileID;
-            UUID == true?o.ID = uuidV1():o.id = await getCounterID(model.modelName);
+            o.id = firstID;
+            firstID+=1
             nList.push(o)
         }
     }

+ 1 - 1
modules/pm/models/project_model.js

@@ -677,7 +677,7 @@ ProjectsDAO.prototype.getTenderByUnitPriceFileId = async function (unitPriceFile
 
     //let condition = {projType: 'Tender', "property.unitPriceFile.id": unitPriceFileId};
     let condition = {"property.unitPriceFile.id": unitPriceFileId,deleteInfo:null};
-    result = await Projects.find(condition,['name','property']);
+    result = await Projects.find(condition,['ID','name','property']);
 
     return result;
 };

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

@@ -63,11 +63,12 @@ async function getGLJDataPaging(req, res) {
     };
     let data = JSON.parse(req.body.data);
     try {
-        let gljLibId = await ration_glj_facade.getGLJLibByEngineerID(data.engineerID);
+        const compilationId = req.session.sessionCompilation._id;
+        const gljLib = await ration_glj_facade.getGLJLib({ compilationId });
         let info = {
-            gljLibId,
+            gljLibId: gljLib.ID,
             userID: req.session.sessionUser.id,
-            compilationId: req.session.sessionCompilation._id
+            compilationId
         };
         result.data = await ration_glj_facade.getGLJDataPaging(info, data.condition);
         if (req.session.sessionCompilation.priceProperties) {

+ 6 - 0
modules/ration_glj/facade/ration_glj_facade.js

@@ -7,6 +7,7 @@ module.exports = {
     deleteByRation: deleteByRation,
     getQuantityByProjectGLJ: getQuantityByProjectGLJ,
     getLibInfo: getLibInfo,
+    getGLJLib,
     getGLJData: getGLJData,
     getGLJDataPaging: getGLJDataPaging,
     getGLJDataByCodes:getGLJDataByCodes,
@@ -53,6 +54,7 @@ import gljType from "../../common/const/glj_type_const.js";
 const complementaryGljModel = mongoose.model('complementary_glj_lib');
 const stdGljModel = mongoose.model('std_glj_lib_gljList');
 const gljClassModel = mongoose.model('std_glj_lib_gljClass');
+const stdGLJLibModel = mongoose.model('std_glj_lib_map');
 const projectDao = require('../../pm/models/project_model').project;
 const compleClassModel = mongoose.model('complementary_glj_section');
 
@@ -535,6 +537,10 @@ async function getLibInfo(req) {
     return data;
 }
 
+async function getGLJLib(query) {
+    return stdGLJLibModel.findOne(query);
+}
+
 async function getGLJLibByEngineerID  (engineerID) {
     let engineeringLibModel = new EngineeringLibModel() ;
     let engineeringInfo = await engineeringLibModel.findDataByCondition({'_id': engineerID});

+ 2 - 2
modules/reports/util/rpt_excel_util.js

@@ -808,9 +808,9 @@ function writeSheet(pageData, sheetData, paperSize, sharedStrList, stylesObj, ap
     let colStr = private_getCellIdxStr(xPos.length - 3);
     rst.push('<dimension ref="A1:' + colStr + '' + yPos.length + '"/>');
     if (isFirstSheet) {
-        rst.push('<sheetViews><sheetView tabSelected="1" workbookViewId="0">');
+        rst.push('<sheetViews><sheetView showGridLines="1" tabSelected="1" workbookViewId="0">');
     } else {
-        rst.push('<sheetViews><sheetView workbookViewId="0">');
+        rst.push('<sheetViews><sheetView showGridLines="1" workbookViewId="0">');
     }
     //rst.push('<selection sqref="A1:' + colStr + '1"/>');
     rst.push('<selection sqref="A1:A1"/>');

+ 97 - 0
modules/unit_price_file/controllers/unit_price_controller.js

@@ -0,0 +1,97 @@
+
+let mongoose = require("mongoose")
+let logger = require("../../../logs/log_helper").logger;
+let config = require("../../../config/config.js");
+let unitPriceFileModel = mongoose.model('unit_price_file');
+let projectModel = mongoose.model('projects');
+const ProjectDao = require('../../pm/models/project_model').project;
+let unitPriceModel = mongoose.model('unit_price');
+let mixRatioModel = mongoose.model('mix_ratio');
+let glj_type_util = require('../../../public/cache/std_glj_type_util');
+let ration_facade = require('../../main/facade/ration_facade');
+let unit_price_facade = require('../facade/unit_price_facade');
+let _ = require("lodash");
+
+module.exports={
+    index:async function(req,res){
+      let unitPriceFileID = req.params.unitPriceFileID;
+      let unitPriceFile = await unitPriceFileModel.findOne({id:unitPriceFileID}).lean();
+      let project = await projectModel.findOne({ID:unitPriceFile.root_project_id}).lean();
+      let tenderData = await ProjectDao.getTenderByUnitPriceFileId(unitPriceFileID);
+      let unitpriceList = await unitPriceModel.find({unit_price_file_id:unitPriceFileID},{supplyLocation:0}).lean();
+      let mixRatioList = await mixRatioModel.find({unit_price_file_id:unitPriceFileID}).lean();
+      let mixRatioMap = _.groupBy(mixRatioList,"connect_key");
+      let projectID = null;
+
+      let gljTypeMap = glj_type_util.getStdGljTypeCacheObj().innerGljTypeObj;
+      let usedTenderList = [];
+      if (tenderData !== null && tenderData.length > 0) {
+          for (let tmp of tenderData) {
+              usedTenderList.push(tmp.name);
+          }
+          projectID = tenderData[0].ID;
+      }
+      let usedTenderString = "人材机单价的变化,将自动影响以下单位工程造价:<br>"+usedTenderList.join("<br>");
+      res.render('building_saas/unit_price_file/index.html',
+        {
+          userAccount: req.session.userAccount,
+          userID: req.session.sessionUser.id,
+          unitPriceFileID:unitPriceFileID,
+          projectID:projectID,
+          versionName: req.session.compilationVersion,
+          unitFileName:unitPriceFile.name,
+          rootProjectName:project.name,
+          taxType:project.property.taxType,
+          region:project.property.region,
+          overWriteUrl:req.session.sessionCompilation.overWriteUrl,
+          usedTenderList:usedTenderList,
+          usedTenderString:usedTenderString,
+          unitpriceList:JSON.stringify(unitpriceList),
+          gljTypeMap:JSON.stringify(gljTypeMap),
+          mixRatioMap:JSON.stringify(mixRatioMap),
+          LicenseKey:config.getLicenseKey(process.env.NODE_ENV)
+        });
+    },
+    addMixRatio:async function(req,res){
+      let result={
+        error:0
+    };
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let gljList = data.gljList, parentInfo = data.parentInfo,mixRatios = [],gljCodes=[];
+
+        if(gljList.length > 0){
+          for(let newProjectGLJ of gljList){
+              let mixRatio = {
+                  glj_id:newProjectGLJ.glj_id,
+                  consumption:0,
+                  unit_price_file_id:parentInfo.unit_price_file_id,
+                  connect_key:parentInfo.connect_key,
+                  type: newProjectGLJ.type,
+                  code: newProjectGLJ.code,
+                  specs:newProjectGLJ.specs,
+                  name:newProjectGLJ.name,
+                  unit:newProjectGLJ.unit,
+                  model:newProjectGLJ.model
+              };
+              mixRatios.push(mixRatio);
+              gljCodes.push(newProjectGLJ.code);
+          }
+          let [unitPriceMap,newUnitPriceList] = await unit_price_facade.getUnitPriceData(gljList,gljCodes,parentInfo.unit_price_file_id); 
+          await unit_price_facade.setIDfromCounter("mix_ratio",mixRatios);    
+          await mixRatioModel.insertMany(mixRatios);
+          result.data = {mixRatios:mixRatios,newUnitPriceList:newUnitPriceList};
+        }
+       
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+      res.json(result);
+    }
+}
+
+
+// engineerID = req.params.engineerID;

+ 161 - 0
modules/unit_price_file/facade/unit_price_facade.js

@@ -0,0 +1,161 @@
+module.exports = {
+  getUnitPriceData:getUnitPriceData,
+  setIDfromCounter:setIDfromCounter,
+  getNewProjectGLJFromMissMixratio:getNewProjectGLJFromMissMixratio
+};
+
+let mongoose = require("mongoose");
+let std_glj_lib_gljList_model = mongoose.model('std_glj_lib_gljList');
+let unitPriceModel = mongoose.model('unit_price');
+let counterModel =  mongoose.model('counter');
+
+async function setIDfromCounter(name,list,map,keyfield){//map,keyfield
+
+  let update = {$inc: {sequence_value: list.length}};
+  let condition = {_id: name};
+  let options = {new: true};
+
+  // 先查找更新
+  let counter = await counterModel.findOneAndUpdate(condition, update, options);
+  let firstID = counter.sequence_value - (list.length - 1);
+  for(let a of list){
+      a.id = firstID;
+      firstID+=1
+      if(map && keyfield){
+        let key = a[keyfield];
+        map[key]?map[key].push(a):map[key]=[a]
+      }
+  }
+}
+
+function getProjectGLJNewData(tmp,projectId,ext){
+  let gljData = {
+      glj_id: tmp.ID,
+      repositoryId:tmp.repositoryId,
+      project_id: projectId,
+      code: tmp.code,
+      name: tmp.name,
+      specs: tmp.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,
+      base_price: tmp.basePrice,
+      market_price: tmp.basePrice,
+      from:tmp.from?tmp.from:"std"
+  };
+  if(gljData.from == 'std' && ext && ext.priceField &&( tmp.priceProperty[ext.priceField]!= undefined && tmp.priceProperty[ext.priceField]!=null)){
+    basePrice = scMathUtil.roundTo(tmp.priceProperty[ext.priceField],-6);
+    gljData.base_price = basePrice;
+    gljData.market_price = basePrice;
+  }
+  return gljData;
+}
+
+//根据缺少项目工料机的组成物信息,反向生成对应的项目工料机
+async function getNewProjectGLJFromMissMixratio(projectID,lessMix,projectGLJMap,newProjectGLJList,ext){
+  let lessIDList=[];
+  let uniqMap ={};//去重
+  let lessStdMix = [];//防止组成物中改了名称等,但是通过glj_id取出来的是还没改前的原始数据
+  if(lessMix.length > 0){
+    for(let lm of lessMix){
+      let parentglj = projectGLJMap[lm.connect_key];
+      if(!parentglj) throw `含有组成物工料机${lm.connect_key},没有找到,添加定额失败`;
+      if((parentglj.from == "std" || lm.from == "std") && lm.code!="80CCS"){//车船税特殊处理
+        if(!uniqMap[lm.glj_id]){
+          lessIDList.push(lm.glj_id);
+          uniqMap[lm.glj_id] = lm;
+        }
+        lessStdMix.push(lm);
+      }else {//来自组成物的直接设置    
+        lm.from = 'cpt';
+        lm.gljType = lm.type;
+        let t_mg = getProjectGLJNewData(lm,projectID);
+        newProjectGLJList.push(t_mg);
+        projectGLJMap[getIndex(lm)] = t_mg;
+      }
+    }
+  }
+
+  if(lessIDList.length > 0){
+    let less_stds =  await std_glj_lib_gljList_model.find({'ID':{'$in':lessIDList}}).lean();
+    let less_stds_map = {};
+    for(let les of  less_stds){
+      less_stds_map[les.ID] = les;
+    }
+    for(let t_l_m of lessStdMix){
+      let t_nglj = less_stds_map[t_l_m.glj_id];
+      t_nglj.from = 'std';
+      //防止组成物中改了名称等,但是通过glj_id取出来的是还没改前的原始数据
+      t_nglj.name = t_l_m.name;
+      t_nglj.code = t_l_m.code;
+      t_nglj.gljType = t_l_m.type;
+      t_nglj.specs = t_l_m.specs;
+      t_nglj.unit = t_l_m.unit;
+      let t_np = getProjectGLJNewData(t_nglj,projectID,ext);
+      newProjectGLJList.push(t_np);
+      projectGLJMap[getIndex(t_l_m)] = t_np;
+    }
+  }
+  return newProjectGLJList;
+}
+
+//找到并返回单价文件信息,如果没有自动插入
+async function getUnitPriceData(newProjectGLJList,gljCodes,unitPriceFileId){
+  let unitPriceMap = {};
+  let newUnitPriceList = [];
+  let unitPriceList = await unitPriceModel.find({unit_price_file_id: unitPriceFileId,'code':{'$in':gljCodes}}).lean();
+  for(let u of unitPriceList){
+    unitPriceMap[getIndex(u)]=u;
+  }
+
+  for(let np of newProjectGLJList){
+    let pkey = getIndex(np);
+    if(unitPriceMap[pkey]) continue;
+
+    let insertData = {
+      code: np.code,
+      base_price: np.base_price,
+      market_price: np.market_price,
+      unit_price_file_id: unitPriceFileId,
+      name: np.name,
+      specs:np.specs?np.specs:'',
+      original_code:np.original_code,
+      unit:np.unit?np.unit:'',
+      type: np.type,
+      short_name: np.shortName !== undefined ? np.shortName : '',
+      glj_id: np.glj_id,
+      is_add:0,
+      grossWeightCoe:np.grossWeightCoe,
+      purchaseStorageRate:np.purchaseStorageRate,
+      offSiteTransportLossRate:np.offSiteTransportLossRate,
+      handlingLossRate:np.handlingLossRate
+    };
+    if(np.from=='cpt') insertData.is_add=1;//如果是来自补充工料机,则都添加新增标记
+    if(insertData.code != insertData.original_code) insertData.is_add=1;//添加的时候如果是复制整块来的,可能在源项目中是新增的工料机,这里也要添上(暂时可能还用不到)
+    newUnitPriceList.push(insertData);
+    unitPriceMap[pkey] = insertData;
+  }
+
+
+  if(newUnitPriceList.length > 0){
+    await setIDfromCounter("unit_price",newUnitPriceList);
+    await unitPriceModel.insertMany(newUnitPriceList);
+  } 
+
+  return [unitPriceMap,newUnitPriceList];
+}
+
+function getIndex(obj, pops){
+  let t_index = '';
+  let k_arr = [];
+  if(!pops) pops = ['code','name','specs','unit','type'];
+  for (let p of pops) {
+      let tmpK = (obj[p] == undefined || obj[p] == null || obj[p] == '') ? 'null' : obj[p];
+      k_arr.push(tmpK);
+  }
+  t_index = k_arr.join("|-|");
+  return t_index;
+}

+ 14 - 0
modules/unit_price_file/routes/unit_price_router.js

@@ -0,0 +1,14 @@
+let express = require('express');
+let unitPirceController = require('../controllers/unit_price_controller');
+
+module.exports = function (app) {
+
+    var Router = express.Router();
+    Router.get('/index/:unitPriceFileID', unitPirceController.index);
+    Router.post('/addMixRatio',unitPirceController.addMixRatio);
+    // Router.post('/addGLJ',unitPirceController.addGLJ);
+    // Router.post('/replaceGLJ',unitPirceController.replaceGLJ);
+    
+
+    app.use('/unitPrice',Router);
+}

+ 2 - 0
public/common_constants.js

@@ -139,6 +139,7 @@
         BILLS: 'bills',
         RATION: 'ration',
     };
+    const DEFAULT_REGION = '全省';
     return {
         fixedFlag,
         billType,
@@ -148,5 +149,6 @@
         supplyType,
         supplyText,
         SourceType,
+        DEFAULT_REGION,
     };
 });

+ 10 - 1
public/web/gljUtil.js

@@ -595,13 +595,22 @@ let gljUtil = {
     },
     setProperty:function(Obj,updateData) {
         for(let ukey in updateData){
-            if(_.isObject(updateData[ukey]) && _.isObject(Obj[ukey])){
+            if(_.isObject(updateData[ukey]) && _.isObject(Obj[ukey])&&!_.isArray(updateData[ukey])){
                 setProperty(Obj[ukey],updateData[ukey]);
             }else {
                 Obj[ukey] = updateData[ukey];
             }
         }
     },
+    sortProjectGLJ:function(jsonData) {
+      if (jsonData.length > 0) {
+          jsonData = _.sortByAll(jsonData, [function (item) {
+            let unit_price = item.unit_price?item.unit_price:item;
+            return _.indexOf(gljTypeSeq,unit_price.type);
+          }, 'code']);
+      }
+      return jsonData
+  },
     fixedFlag : {
         // 分部分项工程
         SUB_ENGINERRING: 1,

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

@@ -371,7 +371,7 @@ var sheetCommonObj = {
             }
             if(setting.owner==='gljTree'){
                 if(setting.header[col].cellType === "checkBox"){
-                    val==1?val:0;
+                    val = val==1?val:0;
                     this.setCheckBoxCell(row,col,sheet,val);
                 }
                 if(setting.header[col].dataCode === 'gljType' && data[row].gljType){

+ 47 - 33
public/web/socket/connection.js

@@ -1,29 +1,40 @@
 /**
  * Created by zhangweicheng on 2017/8/7.
  */
-socketObject={
-  roomInfo : null,
-  messages:[],
-  connect:function (from) {
+socketObject = {
+  roomInfo: null,
+  messages: [],
+  connect: function (from, payload) {
       // 连接socket服务器
       var hostName = window.location.hostname;
       let me = this;
-      let port = window.location.protocol ==='http:'?3300:3301;
-      socket = io(window.location.protocol+'//'+hostName+':'+port);
+      let port = window.location.protocol === 'http:' ? 3300 : 3301;
+      socket = io(window.location.protocol + '//' + hostName + ':' + port);
       socket.on('connect', function () {
-          if(from == 'pm'){
-              me.roomInfo={
-                  userID:userID
-              };
-          }else {
-              me.roomInfo={
-                  feeRate:me.getFeeRateRoomID(),
-                  unitFile:me.getUnitFileRoomID()
+          if (from == 'pm') {
+              me.roomInfo = {
+                  userID: userID
               };
+        }else if(from == 'unitPrice'){
+          me.roomInfo = {
+            unitFile:unitPriceFileID
+          }
+        } else {
+            me.roomInfo={
+                feeRate:me.getFeeRateRoomID(),
+                unitFile:me.getUnitFileRoomID()
+            };
+              if (payload && payload.projectReadOnly === false) {
+                  me.roomInfo.projectID = `projectID${projectObj.project.ID()}`;
+              }
           }
-          socket.emit('join', me.roomInfo);
-          if(me.messages.length > 0){//发送缓存消息;
-              for(let m of me.messages){
+          const emitData = {
+              roomData: me.roomInfo,
+              payload
+          };
+          socket.emit('join', emitData);
+          if (me.messages.length > 0) {//发送缓存消息;
+              for (let m of me.messages) {
                   socket.emit(m.message, m.data);
               }
           }
@@ -32,7 +43,7 @@ socketObject={
 
       //=========================================================
       //造价书页面接收消息部分
-      socket.on('feeRateChange', function(data) {
+      socket.on('feeRateChange', function (data) {
           //data = JSON.parse(data);
 
           $("#message").html('费率文件已被修改,<a href="javascript:void(0);" id="load-data" onclick="window.location.reload()">点击加载并重新进行造价计算</a>');
@@ -49,34 +60,37 @@ socketObject={
           $("#notify").show();
       });
       socket.on('changeFileNotify', function (data) {//收到文件改变的消息
-          if(data.projectID==projectObj.project.ID()){//如果是同个项目,则给出提示,否则忽略
-                let preString ="";
-                if(data.name=='feeRate'){
-                    preString = "费率文件";
-                }
-                if(data.name=='unitFile'){
-                    preString = "单价文件";
-                }
-              $("#message").html(preString+'已被修改,<a href="javascript:void(0);" id="load-data" onclick="window.location.reload()">点击加载并重新进行造价计算</a>');
+          if (data.projectID == projectObj.project.ID()) {//如果是同个项目,则给出提示,否则忽略
+              let preString = "";
+              if (data.name == 'feeRate') {
+                  preString = "费率文件";
+              }
+              if (data.name == 'unitFile') {
+                  preString = "单价文件";
+              }
+              $("#message").html(preString + '已被修改,<a href="javascript:void(0);" id="load-data" onclick="window.location.reload()">点击加载并重新进行造价计算</a>');
               $("#notify").show();
           }
       });
+      socket.on('handleAvatarList', function ({ editingUsers }) {
+          projectInfoObj.handleAvatarList(editingUsers);
+      });
 
 
       //=============================================================================================
       //项目管理页面接收消息部分
 
       socket.on('refreshProjectIcon', function (data) {//收到刷新图标消息
-           if(data.projectID && typeof projTreeObj !== 'undefined') projTreeObj.refreshProjectIcon(data.projectID);
+          if (data.projectID && typeof projTreeObj !== 'undefined') projTreeObj.refreshProjectIcon(data.projectID);
       });
       socket.on('fileDataChange', function (data) {//收到单价文件、费率文件内容修改、文件切换、另存(暂时能共用,以后有需要可分离)推送消息
-          if(data.projectID && typeof projTreeObj !== 'undefined') projTreeObj.refreshWhenFileDateChange(data.projectID);
+          if (data.projectID && typeof projTreeObj !== 'undefined') projTreeObj.refreshWhenFileDateChange(data.projectID);
       });
   },
-  getFeeRateRoomID:function (){
-      return  projectObj.project.FeeRate.getActivateFeeRateFileID();
+  getFeeRateRoomID: function () {
+      return projectObj.project.FeeRate.getActivateFeeRateFileID();
   },
-  getUnitFileRoomID:function () {
-      return projectObj.project.projectGLJ.datas.constData.roomId?projectObj.project.projectGLJ.datas.constData.roomId:roomId;
+  getUnitFileRoomID: function () {
+      return projectObj.project.projectGLJ.datas.constData.roomId ? projectObj.project.projectGLJ.datas.constData.roomId : roomId;
   }
 }

+ 60 - 5
socket.js

@@ -9,16 +9,58 @@ import socket from "socket.io";
 
 const socketIO = socket(3300);
 
+const userCache = {};
+
+function getEditingUsers(projectID) {
+    const users = userCache[projectID];
+    if (!users) {
+        return [];
+    }
+    const rst = [];
+    const existMap = {};
+    for (const user of users) {
+        if (existMap[user._id]) {
+            continue;
+        }
+        existMap[user._id] = true;
+        rst.push(user);
+    }
+    return rst;
+}
+
 // socket.io相关操作
 socketIO.on('connection', function(socket) {
     let roomInfo = {};
+    let curUser;
+    let curProjectID;
     console.log("new connection");
     // 加入房间
     socket.on('join', function(data) {
-        console.log(data);
-        for(let key in data){
-            roomInfo[key] = data[key];
-            socket.join(data[key]);
+        const { roomData, payload } = data;
+        if (payload && payload.user) {
+            curUser = payload.user;
+        }
+        if (roomData) {
+            if (roomData.projectID) {
+                curProjectID = roomData.projectID;
+                if (!userCache[roomData.projectID]) {
+                    userCache[roomData.projectID] = [];
+                }
+                // 由于用户可以重复打开项目,因此不做唯一性处理
+                userCache[roomData.projectID].push(curUser);
+            }
+            for(let key in roomData){
+                roomInfo[key] = roomData[key];
+                socket.join(roomData[key], () => {
+                    if (key === 'projectID') {
+                        const editingUsers = getEditingUsers(curProjectID);
+                        if (editingUsers.length > 1) { // 只有一个用户数据,说明只有用户自己打开项目,不需要推送数据,减少触发。
+                            socket.broadcast.to(roomInfo.projectID).emit('handleAvatarList', {editingUsers});
+                            socket.emit('handleAvatarList', {editingUsers});
+                        }
+                    }
+                });
+            }
         }
     });
     // 数据更改通知
@@ -60,6 +102,19 @@ socketIO.on('connection', function(socket) {
 
 
     socket.on('disconnect', function () {
+        // 由于用户可以重复打开项目,因此不做唯一性处理,只删除一个数据,不删除所有的同用户数据
+        if (curProjectID && userCache[curProjectID]) {
+            const index = userCache[curProjectID].findIndex(user => user._id === curUser._id);
+            if (index >= 0) {
+                userCache[curProjectID].splice(index, 1);
+                if (!userCache[curProjectID].length) {
+                    delete userCache[curProjectID];
+                }
+                const editingUsers = getEditingUsers(curProjectID);
+                socket.broadcast.to(roomInfo.projectID).emit('handleAvatarList', {editingUsers});
+                socket.emit('handleAvatarList', {editingUsers});
+            }
+        }
         console.log("client disconnect =========="+JSON.stringify(roomInfo));
     });
-});
+});

+ 6 - 6
web/building_saas/complementary_glj_lib/html/tools-gongliaoji.html

@@ -74,7 +74,7 @@
                             <!--<input type="radio" class="glj-radio" name="glj" value="allGljs">所有&nbsp;-->&nbsp;
                             <input type="radio" class="glj-radio" name="glj" value="stdGljs">标准&nbsp;&nbsp;
                             <input type="radio" class="glj-radio" name="glj" value="complementaryGljs">补充&nbsp;&nbsp;
-                            <div  class="modal-auto-height" id="componentTreeDiv" style="overflow: hidden">
+                            <div  class="modal-auto-height" id="componentTreeDiv" style="height: 435px; overflow: hidden">
                                 <!--<div class="print-list">-->
                                 <div style="width: 100%; height: 100%; overflow: auto">
                                     <ul id="componentTree" class="ztree"></ul>
@@ -96,7 +96,7 @@
                                         </div>
                                     </div>
                                 </div>
-                                <div class="modal-auto-height col-12" style="overflow: hidden" id="componentSheet">
+                                <div class="modal-auto-height col-12" style="height: 420px; overflow: hidden" id="componentSheet">
                                 </div>
                             </div>
                         </div>
@@ -258,19 +258,19 @@
         };
         $(document).ready(function(){
             //解决spreadjs sheet初始化没高度宽度
-            $('#modalCon').width($(window).width()*0.5);
+            /* $('#modalCon').width($(window).width()*0.5);
             $('#componentTreeDiv').height($(window).height() - 300);
             $("#componentSheet").height($("#componentTreeDiv").height() - 15);
-            $("#componentSheet").width($('#modalCon').width() * 0.63);
+            $("#componentSheet").width($('#modalCon').width() * 0.63); */
             pageOprObj.initPage($("#GLJListSheet")[0], $('#gljComponentSheet')[0], $("#componentSheet")[0]);
         });
         //组成物弹出窗大小设置
-        $(window).resize(function () {
+        /* $(window).resize(function () {
             $('#modalCon').width($(window).width()*0.5);
             $('#componentTreeDiv').height($(window).height() - 300);
             $("#componentSheet").height($("#componentTreeDiv").height()-15);
             $("#componentSheet").width($('#modalCon').width()* 0.63);
-        });
+        }); */
   	</SCRIPT>
 </body>
 <script type="text/javascript">

+ 5 - 0
web/building_saas/complementary_glj_lib/js/glj.js

@@ -3,6 +3,11 @@
  */
 
 $(document).ready(function () {
+    $('#component').on('shown.bs.modal', function () {
+        if (componentOprObj.workBook) {
+            componentOprObj.workBook.refresh();
+        }
+    });
     let moduleName = 'compleGLj';
     SlideResize.loadHorizonWidth(moduleName, [$('#rightResize'), $('#leftResize')], [$('#leftContent'), $('#midContent'), $('#rightContent')], function () {
         refreshALlWorkBook();

+ 2 - 2
web/building_saas/complementary_ration_lib/html/dinge.html

@@ -523,7 +523,7 @@
                     &nbsp;
                     <input type="radio" class="glj-radio" name="glj" value="stdGljs">标准&nbsp;&nbsp;
                     <input type="radio" class="glj-radio" name="glj" value="complementaryGljs">补充&nbsp;&nbsp;
-                    <div  class="modal-auto-height" id="gljSelTreeDiv" style="overflow: hidden">
+                    <div  class="modal-auto-height" id="gljSelTreeDiv" style="height: 435px; overflow: hidden">
                         <div style="width: 100%; height: 100%; overflow: auto">
                             <ul id="selGljTree" class="ztree"></ul>
                         </div>
@@ -543,7 +543,7 @@
                                 </div>
                             </div>
                         </div>
-                        <div class="modal-auto-height col-12" style="overflow: hidden" id="gljSelSheet">
+                        <div class="modal-auto-height col-12" style="height: 420px; overflow: hidden" id="gljSelSheet">
                         </div>
                     </div>
                 </div>

+ 2 - 2
web/building_saas/complementary_ration_lib/js/init.js

@@ -65,7 +65,7 @@ const initialization = (() => {
             $.bootstrapLoading.end();
         });
         //解决spreadjs sheet初始化没高度宽度
-        $('#modalCon').width($(window).width()*0.5);
+        /* $('#modalCon').width($(window).width()*0.5);
         $('#gljSelTreeDiv').height($(window).height() - 300);
         $("#gljSelSheet").height($("#gljSelTreeDiv").height()-21.6);
         $("#gljSelSheet").width($('#modalCon').width() * 0.63);
@@ -74,6 +74,6 @@ const initialization = (() => {
             $('#gljSelTreeDiv').height($(window).height() - 300);
             $("#gljSelSheet").height($("#gljSelTreeDiv").height()-21.6);
             $("#gljSelSheet").width($('#modalCon').width()* 0.63);
-        });
+        }); */
     });
 })();

+ 256 - 247
web/building_saas/css/custom.css

@@ -1,146 +1,146 @@
 
 .text-green{
-    color: #172a30
+  color: #172a30
 }
 label.title{
-    display: inline-block;
-    width: 100px;
+  display: inline-block;
+  width: 100px;
 }
 .modal-feeRate {max-width: 550px}
 
 div.resize{
-    height: 10px;
-    background: #efefef;
-    width: 100%;
-    cursor: s-resize;
+  height: 10px;
+  background: #efefef;
+  width: 100%;
+  cursor: s-resize;
 }
 div.resize-y{
-    height: 5px;
-    background: #efefef;
-    width: 100%;
-    cursor: s-resize;
+  height: 5px;
+  background: #efefef;
+  width: 100%;
+  cursor: s-resize;
 }
 div.resize-x{
-    width: 4px;
-    height: 100%;
-    background: #efefef;
-    cursor: w-resize;
-    float: left;
+  width: 4px;
+  height: 100%;
+  background: #efefef;
+  cursor: w-resize;
+  float: left;
 }
 /*.zlfb-check{
-    margin-left: 20px;
+  margin-left: 20px;
 }*/
 legend.legend{
-    display:block;
-    width:auto;
-    font-size:0.9rem;
-    top:-15px;
-    background: white;
+  display:block;
+  width:auto;
+  font-size:0.9rem;
+  top:-15px;
+  background: white;
 }
 
 .toolsbar_feeRate {
-    border-bottom: 1px solid #ccc
+  border-bottom: 1px solid #ccc
 }
 
 .li_sub {
-    margin-left: 14px;
+  margin-left: 14px;
 }
 
 .filterType{
-    width: 122px;
+  width: 122px;
 }
 
 .filterType ul{
-    width: 110px;
+  width: 110px;
 }
 .a_color{
-    color: #007bff
+  color: #007bff
 }
 
 .filterType a{
-    padding: 1px;
-    padding-top: 7px;
-    padding-bottom: 7px;
+  padding: 1px;
+  padding-top: 7px;
+  padding-bottom: 7px;
 }
 
 #gljPriceTenderCoe::-webkit-outer-spin-button,
 #gljPriceTenderCoe::-webkit-inner-spin-button {
-         -webkit-appearance: none;
-     }
+       -webkit-appearance: none;
+   }
 #gljPriceTenderCoe {
-    -moz-appearance: textfield;
+  -moz-appearance: textfield;
 }
 
 .modal-quantity-edit-height {
-    height: 200px;
-    overflow-y: auto;
+  height: 200px;
+  overflow-y: auto;
 }
 
 .message-box {
-    position:absolute;
-    background:#000;
-    padding:8px 10px;
-    line-height: 18px;
-    border-radius:4px;
-    text-align:left;
-    font:0.9rem Calibri;
-    box-shadow:2px 2px 6px #ccc;
-    color:#fff;
+  position:absolute;
+  background:#000;
+  padding:8px 10px;
+  line-height: 18px;
+  border-radius:4px;
+  text-align:left;
+  font:0.9rem Calibri;
+  box-shadow:2px 2px 6px #ccc;
+  color:#fff;
 }
 .triangle-border {
-    position:absolute;
-    left:10px;
-    overflow:hidden;
-    width:0;
-    height:0;
-    border-width:6px;
-    border-style:solid dashed dashed dashed;
+  position:absolute;
+  left:10px;
+  overflow:hidden;
+  width:0;
+  height:0;
+  border-width:6px;
+  border-style:solid dashed dashed dashed;
 }
 .triangle-border_dropdown {
-    position:absolute;
-    left:0px;
-    overflow:hidden;
-    width:0;
-    height:0;
-    border-width:4px;
-    z-index: 10;
-    border-style:solid dashed dashed dashed;
+  position:absolute;
+  left:0px;
+  overflow:hidden;
+  width:0;
+  height:0;
+  border-width:4px;
+  z-index: 10;
+  border-style:solid dashed dashed dashed;
 }
 .tb-border_dropdown {
-    top:7px;
-    border-color:#000 transparent transparent transparent;
+  top:7px;
+  border-color:#000 transparent transparent transparent;
 }
 
 .tb-background_dropdown {
-    bottom:-11px;
-    border-color:#000 transparent transparent transparent;
+  bottom:-11px;
+  border-color:#000 transparent transparent transparent;
 }
 
 .tb-border {
-    bottom:-12px;
-    border-color:#000 transparent transparent transparent;
+  bottom:-12px;
+  border-color:#000 transparent transparent transparent;
 }
 .tb-background {
-    bottom:-11px;
-    border-color:#000 transparent transparent transparent;
+  bottom:-11px;
+  border-color:#000 transparent transparent transparent;
 }
 
 .tb-border_up {
-    top:-12px;
-    border-color:transparent transparent #000 transparent;
+  top:-12px;
+  border-color:transparent transparent #000 transparent;
 }
 .tb-background_up {
-    top:-11px;
-    border-color:transparent transparent #000 transparent;
+  top:-11px;
+  border-color:transparent transparent #000 transparent;
 }
 
 
 .elf-options:hover{
-    background-color: #CCCCCC;
+  background-color: #CCCCCC;
 }
 
 .inline-div{
-    display:inline;
+  display:inline;
 }
 
 /*快捷切换单位工程*/
@@ -150,326 +150,335 @@ legend.legend{
 
 
 /*.menu a:hover {
-    color: #fff;
-    background: #40DE5A;
+  color: #fff;
+  background: #40DE5A;
 }*/
 
 
 .menu ul ul {
-    visibility: hidden;
-    position: absolute;
-    top: -1px;
-    left: 100px;
+  visibility: hidden;
+  position: absolute;
+  top: -1px;
+  left: 100px;
 }
 
 .menu ul li:hover ul, .menu ul a:hover ul {
-    visibility: visible;
+  visibility: visible;
 }
 
 .menu ul :hover ul ul {
-    visibility: hidden;
+  visibility: hidden;
 }
 
 .menu ul :hover ul :hover ul {
-    visibility: visible;
+  visibility: visible;
 }
 
 .ui-datepicker-next, .ui-datepicker-next:hover{
-    background-image: url("/lib/jquery-ui/images/ui-icons_444444_256x240.png");
-    background-repeat: no-repeat;
-    background-position: -28px -12px;
+  background-image: url("/lib/jquery-ui/images/ui-icons_444444_256x240.png");
+  background-repeat: no-repeat;
+  background-position: -28px -12px;
 }
 .ui-datepicker-prev, .ui-datepicker-prev:hover{
-    background-image: url("/lib/jquery-ui/images/ui-icons_444444_256x240.png");
-    background-repeat: no-repeat;
-    background-position: -90px -12px;
+  background-image: url("/lib/jquery-ui/images/ui-icons_444444_256x240.png");
+  background-repeat: no-repeat;
+  background-position: -90px -12px;
 }
 
 .feerateInput {
-    padding: 0;
+  padding: 0;
 }
 
 #toolToastWrap{
-    display:-webkit-box;
-    display:-ms-flexbox;
-    display:flex;
-    -webkit-box-pack:center;
-    -ms-flex-pack:center;
-    justify-content:center;
-    pointer-events:none;
+  display:-webkit-box;
+  display:-ms-flexbox;
+  display:flex;
+  -webkit-box-pack:center;
+  -ms-flex-pack:center;
+  justify-content:center;
+  pointer-events:none;
 }
 
 #toolToast{
-    padding:11px 20px;
-    line-height:18px;
-    font-size:14px;
-    position:relative;
-    word-wrap:break-word;
-    color:#fff;
-    text-align:center;
-    background:rgba(0,0,0,.75);
-    background-size:cover;
-    -webkit-user-select:none;
-    -moz-user-select:none;
-    -ms-user-select:none;
-    user-select:none;
-    display:-webkit-box;
-    display:-ms-flexbox;
-    display:flex;
-    -webkit-box-align:center;
-    -ms-flex-align:center;
-    align-items:center;
-    max-width:686px;
-    box-sizing:border-box;
-    box-shadow:0 0 0 0 rgba(0,0,0,.15),0 2px 5px 0 rgba(0,0,0,.25);
-    pointer-events:auto;
-    border-radius:2px
+  padding:11px 20px;
+  line-height:18px;
+  font-size:14px;
+  position:relative;
+  word-wrap:break-word;
+  color:#fff;
+  text-align:center;
+  background:rgba(0,0,0,.75);
+  background-size:cover;
+  -webkit-user-select:none;
+  -moz-user-select:none;
+  -ms-user-select:none;
+  user-select:none;
+  display:-webkit-box;
+  display:-ms-flexbox;
+  display:flex;
+  -webkit-box-align:center;
+  -ms-flex-align:center;
+  align-items:center;
+  max-width:686px;
+  box-sizing:border-box;
+  box-shadow:0 0 0 0 rgba(0,0,0,.15),0 2px 5px 0 rgba(0,0,0,.25);
+  pointer-events:auto;
+  border-radius:2px
 }
 
 #toolToastBtn{
-    color:#0188fb;
-    margin:0 2px;
-    white-space:nowrap;
-    cursor:pointer;
-    font-weight:700
+  color:#0188fb;
+  margin:0 2px;
+  white-space:nowrap;
+  cursor:pointer;
+  font-weight:700
 }
 
 #toolToastBtn:hover{
-    color:#4060c9
+  color:#4060c9
 }
 
 #toolToastBtn:active{
-    color:#354ea1
+  color:#354ea1
 }
 
 .select_input{
-    border:none;
+  border:none;
 }
 
 /*.es-list>li:hover{
-    background: lightgrey;
+  background: lightgrey;
 }*/
 
 .es-list-selected{
-    background: lightgrey;
+  background: lightgrey;
 }
 
 .es-list>li{
-    font-size:13px;
+  font-size:13px;
 }
 .es-list{
-    white-space:nowrap;
+  white-space:nowrap;
 }
 
 /*.dropdown-toggle::after{
-    vertical-align:.5em
+  vertical-align:.5em
 }*/
 #esInput{
-    font-size:13px;
-    color: black;
+  font-size:13px;
+  color: black;
 }
 .ration_glj_spread{
-    width: 83%;
-    float: left;
+  width: 83%;
+  float: left;
 }
 
 .item_spread{
-    width: 29.8%;
-    float: left;
-    background: #F1F1F1;
-    word-wrap:break-word
+  width: 29.8%;
+  float: left;
+  background: #F1F1F1;
+  word-wrap:break-word
 }
 
 input.text-right{
-    text-align: right;
+  text-align: right;
 }
 
 .cus-width{
-    width: 100px;
-    margin-left: 10px;
+  width: 100px;
+  margin-left: 10px;
 }
 .more{
-    padding-left:.25rem!important
+  padding-left:.25rem!important
 }
 .bottom-tznrTools {
-    height: 30px;
-    line-height: 30px;
-    background:#efefef;
-    bottom:30px;
-    left:0px;
-    z-index: 999
+  height: 30px;
+  line-height: 30px;
+  background:#efefef;
+  bottom:30px;
+  left:0px;
+  z-index: 999
 }
 
 .zmhs-link{
-    padding:0.4em 0.4em !important;
+  padding:0.4em 0.4em !important;
 }
 
 /*修改tooltip默认最大宽度 */
 .tooltip-inner{
-    max-width: 400px !important;
+  max-width: 400px !important;
 }
 .applySuccess{
-    display: none;
-    color: #43CD80;
-    margin-left: 8px
+  display: none;
+  color: #43CD80;
+  margin-left: 8px
 }
 .font_blue{
-    color: #3FB2E5;
+  color: #3FB2E5;
 }
 
 .nomargin{
-    margin: 0px;
+  margin: 0px;
 }
 .border_bottom{
-    border-bottom:  1px solid #ccc
+  border-bottom:  1px solid #ccc
 }
 .export-check{
-    overflow: auto;
-    height: 400px;
+  overflow: auto;
+  height: 400px;
 }
 .shake-input {
-    animation: shake-input 0.2s 4;
+  animation: shake-input 0.2s 4;
 }
 @keyframes shake-input {
-    0% {
-        transform: translateX(0);
-    }
-    30% {
-        transform: translateX(-2%);
-    }
-    100% {
-        transform: translateX(4%)
-    }
+  0% {
+      transform: translateX(0);
+  }
+  30% {
+      transform: translateX(-2%);
+  }
+  100% {
+      transform: translateX(4%)
+  }
 }
 /*占位底色*/
 .occupied {
-    background: #f1f1f1;
+  background: #f1f1f1;
 }
 /*书签批注*/
 .annotate-color-1::before{
-    color: #E2F2C5 !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color: #E2F2C5 !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-2::before{
-    color: #F9E2CF !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color: #F9E2CF !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-3::before{
-    color:#F2EFD9 !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color:#F2EFD9 !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-4::before{
-    color:#F5D1DA !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color:#F5D1DA !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-5::before{
-    color:#E3E3E3 !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color:#E3E3E3 !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-6::before{
-    color:#B6F3F2 !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color:#B6F3F2 !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .annotate-color-7::before{
-    color:#ECE0F5 !important;
-    -webkit-text-stroke:.5px #ced4da;
+  color:#ECE0F5 !important;
+  -webkit-text-stroke:.5px #ced4da;
 }
 .z-index-3000 {
-    z-index: 3000;
+  z-index: 3000;
 }
 .progress-bar {
-    position: relative;
-    width: 100%;
+  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;
+  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);
-    }
+  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;
+  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;
+  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);
-    }
+  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;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
 }
 .border-radius {
-    border-radius: .2rem !important;
+  border-radius: .2rem !important;
 }
 .pm-i {
-    width: 18px;
+  width: 18px;
 }
 .calcbase-btn {
-    width: 24px;
-    padding-left: 0;
-    padding-right: 0;
+  width: 24px;
+  padding-left: 0;
+  padding-right: 0;
 }
 .over-height {
-    max-width: 550px !important;
+  max-width: 550px !important;
 }
 .hide-area {
-    display: none;
+  display: none;
 }
 .middle-modal-width {
-    max-width: 650px;
+  max-width: 650px;
 }
 .middle-modal-height {
-    height: 500px;
+  height: 500px;
 }
 
 @media screen and (max-width: 1366px), (max-height: 768px) {
-    .middle-modal-width {
-        max-width: 500px;
-    }
-    .middle-modal-height {
-        height: 350px;
-    }
+  .middle-modal-width {
+      max-width: 500px;
+  }
+  .middle-modal-height {
+      height: 350px;
+  }
 }
 .textarea-inherit {
-    width: 100%;
-    overflow: auto;
-    word-break: break-all;
+  width: 100%;
+  overflow: auto;
+  word-break: break-all;
 }
 /* 初始样式,防止projspread初始化完后背景从白突然变灰 */
 .poj-list {
-    height: 1000px; 
-    background: #f7f7f9;
+  height: 1000px; 
+  background: #f7f7f9;
+}
+.default-cursor {
+  cursor: default !important;
+}
+
+.unit_price_header{
+padding-top:6px;
+margin-left: 50px;
+margin-right: 100px !important;
 }

+ 33 - 12
web/building_saas/css/main.css

@@ -57,8 +57,11 @@ height: calc(1.5em + .5rem + 2px);
 font-size: .875rem;
 }
 .btn-xs{
-  padding:0rem .5rem;
-  font-size:.875rem;
+padding:0rem .5rem;
+font-size:.875rem;
+}
+.table-sc thead tr{
+background-color: #f1f1f1
 }
 /*自定义css*/
 .login-body,.login-html{
@@ -766,34 +769,52 @@ background:#999;
 color:#fff;
 border-radius: 30px
 }
-.book-list li .avatar.bg-1{
+.avatar-list {
+padding:0;
+margin:0 0 0 5px;
+list-style: none;
+}
+.avatar-list li{
+  float: left;
+  margin-right: 5px;
+}
+.avatar-list li .avatar{
+height: 24px;
+line-height: 24px;
+width:24px;
+border-radius: 20px;
+color:#fff;
+font-size: 10px;
+text-align: center;
+}
+.book-list li .avatar.bg-1,.avatar-list li .avatar.bg-1{
 background:rgb(16,109,156)
 }
-.book-list li .avatar.bg-2{
+.book-list li .avatar.bg-2,.avatar-list li .avatar.bg-2{
 background:rgb(90,146,173)
 }
-.book-list li .avatar.bg-3{
+.book-list li .avatar.bg-3,.avatar-list li .avatar.bg-3{
 background:rgb(150,164,139)
 }
-.book-list li .avatar.bg-4{
+.book-list li .avatar.bg-4,.avatar-list li .avatar.bg-4{
 background:rgb(216,202,175)
 }
-.book-list li .avatar.bg-5{
+.book-list li .avatar.bg-5,.avatar-list li .avatar.bg-5{
 background:rgb(201,192,211)
 }
-.book-list li .avatar.bg-6{
+.book-list li .avatar.bg-6,.avatar-list li .avatar.bg-6{
 background:rgb(61,89,171)
 }
-.book-list li .avatar.bg-7{
+.book-list li .avatar.bg-7,.avatar-list li .avatar.bg-7{
 background:rgb(243,203,211)
 }
-.book-list li .avatar.bg-8{
+.book-list li .avatar.bg-8,.avatar-list li .avatar.bg-8{
 background:rgb(181,196,177)
 }
-.book-list li .avatar.bg-9{
+.book-list li .avatar.bg-9,.avatar-list li .avatar.bg-9{
 background:rgb(150,84,84)
 }
-.book-list li .avatar.bg-0{
+.book-list li .avatar.bg-0,.avatar-list li .avatar.bg-0{
 background:rgb(191,191,191)
 }
 .book-list li .book-body{

+ 11 - 1
web/building_saas/css/style.css

@@ -1,4 +1,6 @@
-/*
+
+*/
+/*
 01 -  Global styles
 */
 html,
@@ -101,6 +103,14 @@ background-color: #f5f5f5;
 color: #000000;
 font-size: 18px;
 }
+.btn-danger{
+  background: #ff6501;
+  border-color: #ff6501;
+}
+.btn-danger:hover{
+  background-color: #d95e0e;
+  border-color: #d95e0e;
+}
 /*02 Header*/
 div#header-sticky-wrapper {
 position: absolute;

+ 3 - 1
web/building_saas/glj/html/project_glj.html

@@ -7,7 +7,9 @@
     <div class="form-inline py-1">
         <label class="mx-2">使用单价文件:<span id="current-name"></span>(<label class="a_color" id="pop-used-list" data-original-title="" title="">与<span id="used-project-count">0</span>个单位工程同步</label>)
             <a class="btn btn-sm ml-1" href="#" data-toggle="modal" data-target="#change-unitFile"><i class="fa fa-exchange"></i> 选择其他</a>
-            <a class="btn btn-sm ml-1" href="#" data-toggle="modal" data-target="#unitFile-save-as"><i class="fa fa-files-o"></i> 另存单独用</a></label>
+            <a class="btn btn-sm ml-1" href="#" data-toggle="modal" data-target="#unitFile-save-as"><i class="fa fa-files-o"></i> 另存单独用</a>
+            <a class="btn btn-sm ml-1" id="editUnitFile" href="/unitPrice/index/123" target="_"> 编辑单价文件</a>
+        </label>
         <select class="form-control form-control-sm" style="width: auto; font-size: .875rem ;color: #007bff" id="adjustType">
             <option value="priceInfo" selected>造价信息差额调整法</option>
             <option value="priceCoe">价格指数调整法</option>

+ 4 - 4
web/building_saas/main/js/models/ration_glj.js

@@ -517,9 +517,8 @@ let ration_glj = {
         ration_glj.prototype.getGLJDataPaging = function (condition, cb) {
             gljOprObj.loadingPagination = true;
             const property = projectObj.project.projectInfo.property;
-            const engineerID = property.engineering_id;
             const actionType = $('#actionType').val();
-            CommonAjax.post('/rationGlj/getGLJDataPaging', {engineerID, condition}, function (data) {
+            CommonAjax.post('/rationGlj/getGLJDataPaging', { condition }, function (data) {
                 gljOprObj.curPageTotal = data.total;
                 data.complementaryGLJs.forEach(glj => {
                     glj.isComplementary = true;
@@ -529,7 +528,7 @@ let ration_glj = {
                     : 'complementaryGLJs';
                 const newData = data[gljType];
                 // 添加组成物,类型为主材时,需要排除自身
-                if (actionType === 'addMix' && projectGljObject.selectedProjectGLJ.type === gljType.MAIN_MATERIAL) {
+                if ((actionType === 'addMix' || actionType === 'unitPriceAddMix') && projectGljObject.selectedProjectGLJ.type === gljType.MAIN_MATERIAL) {
                     const pIndex = gljOprObj.getIndex(projectGljObject.selectedProjectGLJ, gljKeyArray);
                     const delIndex = newData.findIndex(item => gljOprObj.getIndex(item, gljLibKeyArray) === pIndex);
                     if (!~delIndex) {
@@ -559,7 +558,8 @@ let ration_glj = {
                 // 设置人材机类型名称
                 gljOprObj.setTypeName(gljOprObj.distTypeTree.comboDatas, newData);
                 if (data.priceProperties && data.priceProperties.length > 0) {
-                    let tmp = _.find(data.priceProperties, {region: property.region, taxModel: parseInt(property.taxType)});
+                    // region: region目前跟单位工程绑定,这里暂时固定写死commonConstants.DEFAULT_REGION,以后应改成跟建设项目绑定的region,
+                    let tmp = _.find(data.priceProperties, {region: commonConstants.DEFAULT_REGION, taxModel: parseInt(property.taxType)});
                     if (tmp) {
                         let dataCode = tmp.price.dataCode;
                         let allData = data.stdGLJ.concat(data.complementaryGLJs);

+ 36 - 29
web/building_saas/main/js/views/glj_view.js

@@ -341,7 +341,8 @@ var gljOprObj = {
     },
     loadPageData: function (sheet, reset, index) {
         let condition = this.getPagingCondition(false, reset, false, index);
-        projectObj.project.ration_glj.getGLJDataPaging(condition, function (data) {
+        let getPagingFun =  typeof unitPriceObj != 'undefined' ? unitPriceObj.getGLJDataPaging:projectObj.project.ration_glj.getGLJDataPaging;
+        getPagingFun(condition, function (data) {
             sheetCommonObj.appendData(sheet, condition.index, 0, gljOprObj.gljLibSheetSetting, data);
             gljOprObj.initSelection({row: gljOprObj.gljLibSheet.getActiveRowIndex()});
         });
@@ -358,6 +359,7 @@ var gljOprObj = {
         style.borderRight = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
         style.borderBottom = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
         let colorOpts = optionsOprObj.getOption(optionsOprObj.optionsTypes.COLOROPTS);
+        if(!colorOpts) colorOpts = {SELECTED:{backColor:"#FFFACD"}};
         let selectedColor = colorOpts.SELECTED.backColor,
             recColor =  rcolor?rcolor:'White';
         style.backColor = selected ? selectedColor : recColor;
@@ -390,35 +392,38 @@ var gljOprObj = {
         }
     },
     onCheckBoxClick: function (sender, args) {
+      if(typeof projectObj != 'undefined'){  
         let selected = projectObj.project.mainTree.selected;
         if(selected.sourceType == ModuleNames.ration_glj){//选中的是工料机时不可编辑
             return ;
         }
-        let checkboxValue = args.sheet.getCell(args.row, args.col).value();
-        if(args.sheetName == 'rationInstallSheet' && checkboxValue){
-            return;
-        }
         if(gljOprObj.isInstallationNode(selected)==true || OVER_HEIGHT.isOverHeight(selected)){
-            return;
-        }
-        let newval = checkboxValue? 0:1;
-        args.sheet.getCell(args.row, args.col).value(newval);
-        if (args.sheetName == 'ration_glj') {
-            gljOprObj.updateIsEstimate(args,newval);
-        }  else if (args.sheetName == 'quantity_detail') {
-            projectObj.project.quantity_detail.isSummationUpdate(args, gljOprObj.detailData, newval);
-        } else if (args.sheetName == 'glj_lib') {
-            if (gljOprObj.gljLibSheetSetting.header[args.col].readOnly) {
-                args.sheet.getCell(args.row, args.col).value(checkboxValue);
-                return;
-            }
-            if(gljOprObj.gljLibSheetSetting.header[args.col].dataCode === 'select'){
-                gljOprObj.setGLJSelection(args, newval);
-            }
-        }else if(args.sheetName == 'rationInstallSheet'){
-            args.newValue = newval;
-            installationFeeObj.onRationInstallValueChange(sender,args);
+          return;
         }
+      }
+
+      let checkboxValue = args.sheet.getCell(args.row, args.col).value();
+      if(args.sheetName == 'rationInstallSheet' && checkboxValue){
+          return;
+      }
+      let newval = checkboxValue? 0:1;
+      args.sheet.getCell(args.row, args.col).value(newval);
+      if (args.sheetName == 'ration_glj') {
+          gljOprObj.updateIsEstimate(args,newval);
+      }  else if (args.sheetName == 'quantity_detail') {
+          projectObj.project.quantity_detail.isSummationUpdate(args, gljOprObj.detailData, newval);
+      } else if (args.sheetName == 'glj_lib') {
+          if (gljOprObj.gljLibSheetSetting.header[args.col].readOnly) {
+              args.sheet.getCell(args.row, args.col).value(checkboxValue);
+              return;
+          }
+          if(gljOprObj.gljLibSheetSetting.header[args.col].dataCode === 'select'){
+              gljOprObj.setGLJSelection(args, newval);
+          }
+      }else if(args.sheetName == 'rationInstallSheet'){
+          args.newValue = newval;
+          installationFeeObj.onRationInstallValueChange(sender,args);
+      }
     },
     onCellDoubleClick:function (sender, args) {
         // 含组成物的材料市场价改为只读,改为双击无反应不提示
@@ -1184,7 +1189,7 @@ var gljOprObj = {
                 unit: selected.unit,
                 gljType: selected.type
             };
-        } else if (actionType === 'addMix') {
+        } else if (actionType === 'addMix'|| actionType === 'unitPriceAddMix') {
             condition.queryExtend = projectGljObject.getQueryExtForMixRatio();
         }
         if (init) {
@@ -1223,7 +1228,7 @@ var gljOprObj = {
         }
         if ($('#actionType').val() == 'replace' || $('#actionType').val() == 'm_replace') {
             me.filterLibGLJByType();
-        }else if($('#actionType').val() == 'addMix'){
+        }else if($('#actionType').val() == 'addMix' || $('#actionType').val() == 'unitPriceAddMix'){
             projectGljObject.filterLibGLJForMixRatio();
         }
         //文本筛选
@@ -1246,7 +1251,7 @@ var gljOprObj = {
         }
     },
     setGLJSelection: function (args, newVal) {
-        if ($('#actionType').val() == 'add' || $('#actionType').val() == 'insert'|| $('#actionType').val() == 'addMix') {
+        if ($('#actionType').val() == 'add' || $('#actionType').val() == 'insert'|| $('#actionType').val() == 'addMix'|| $('#actionType').val() == 'unitPriceAddMix') {
             this.addGLJsSelection(args, newVal);
         } else {
             this.replaceGLJSelection(args, newVal);
@@ -1755,7 +1760,7 @@ $(function () {
             selected,
             connect_key;
         const actionType = $('#actionType').val();
-        const addActions = ['add', 'insert', 'addMix'];
+        const addActions = ['add', 'insert', 'addMix','unitPriceAddMix'];
         const replaceActions = ['m_replace', 'replace'];
         if (addActions.includes(actionType)) {//插入,添加,添加组成物(项目人材机页面)
             gljOprObj.GLJSelection = [];
@@ -1779,7 +1784,7 @@ $(function () {
             gljOprObj.gljLibSheet.setActiveCell(index, 0);
             gljOprObj.initSelection({row: index});
             gljOprObj.gljLibSpresd.focus(true);
-        } else if (actionType === 'add' || actionType === 'addMix') {
+        } else if (actionType === 'add' || actionType === 'addMix'|| actionType === 'unitPriceAddMix') {
             gljOprObj.locateZTree(null);
             sheetCommonObj.appendData(gljOprObj.gljLibSheet, 0, 0, gljOprObj.gljLibSheetSetting, gljOprObj.AllRecode);
             gljOprObj.gljLibSheet.showRow(0, GC.Spread.Sheets.VerticalPosition.top);
@@ -1825,6 +1830,8 @@ $(function () {
             gljOprObj.doMReplaceGLJ();
         }else if($('#actionType').val() == 'addMix'){
             projectGljObject.addMixRatio();
+        }else if($('#actionType').val() == 'unitPriceAddMix'){
+          unitPriceObj.addMixRatio();
         }
         $("#glj_tree_div").modal('hide');
     })

+ 3 - 2
web/building_saas/main/js/views/glj_view_contextMenu.js

@@ -435,7 +435,7 @@ var gljContextMenu = {
     }
 }
 
-function getGLJData(actionType) {
+function getGLJData(actionType,getLibFunc) {
     $('#actionType').val(actionType);
     // 清除选中人材机缓存数据
     gljOprObj.GLJSelection = [];
@@ -450,7 +450,8 @@ function getGLJData(actionType) {
     const reset = true;
     const condition = gljOprObj.getPagingCondition(init, reset, location, 0);
     console.time('getGLJData');
-    projectObj.project.ration_glj.getGLJDataPaging(condition, function (result) {
+    if(!getLibFunc)getLibFunc = projectObj.project.ration_glj.getGLJDataPaging;
+    getLibFunc(condition, function (result) {
         gljOprObj.initClassTree('std', gljOprObj.treeData.std);
         $('#modalCon').width($(window).width()*0.5);
         $("input[name='glj']").get(0).checked=true;

+ 6 - 6
web/building_saas/main/js/views/options_view.js

@@ -10,7 +10,7 @@ let optionsOprObj = {
         let me = this;
         me.options = projectOptins;
         let gOpts = me.options[me.optionsTypes.GENERALOPTS];
-        if(isDef(gOpts)){
+        if(gljUtil.isDef(gOpts)){
             for(let attr in gOpts){
                 me[attr].prop('checked', gOpts[attr]);
             }
@@ -24,19 +24,19 @@ let optionsOprObj = {
     },
     //更新optionsOprObj对象options数据
     updateOptions: function (options, updateObj) {
-        if(isDef(options[updateObj.type])){
+        if(gljUtil.isDef(options[updateObj.type])){
             options[updateObj.type][updateObj.opt] = updateObj.value;
         }
     },
     getOptsByType: function (options, type) {
-        return isDef(options[type]) ? options[type] : null;
+        return gljUtil.isDef(options[type]) ? options[type] : null;
     },
     getOption: function (type, optionName) {
-        if(!isDef(optionName)){
-            return isDef(this.options[type]) ? this.options[type] : null;
+        if(!gljUtil.isDef(optionName)){
+            return this.options&&gljUtil.isDef(this.options[type]) ? this.options[type] : null;
         }
         else {
-            return isDef(this.options[type][optionName])
+            return this.options&&gljUtil.isDef(this.options[type][optionName])
                 ? this.options[type][optionName]
                     : optionName === this.optionsTypes.GENERALOPTS
                     ? true

+ 90 - 47
web/building_saas/main/js/views/project_glj_view.js

@@ -135,6 +135,7 @@ let projectGljObject={
         $("#current-name").text(me.usedUnitPriceInfo.name);
         let usedCount = me.usedTenderList.length <= 0 ? 1 : me.usedTenderList.length;
         $("#used-project-count").text(usedCount);
+        $("#editUnitFile").attr("href",`/unitPrice/index/${me.usedUnitPriceInfo.id}`)
     },
     getUsedTenderInfo:function() {
         return "人材机单价的变化,将自动影响以下单位工程造价:<br>"+projectGljObject.usedTenderList.join("<br>");
@@ -177,7 +178,7 @@ let projectGljObject={
             });
         }
     },
-    addMixRatio:function () {
+    addMixRatio:async function () {
         let me = this, projectGLJ = projectObj.project.projectGLJ;
         let tdatas = me.mixRatioData;
         if(me.subList.length > 0) tdatas = me.subList;
@@ -186,15 +187,85 @@ let projectGljObject={
             let t_index = gljOprObj.GLJSelection.indexOf(m_key);
             t_index != -1?gljOprObj.GLJSelection.splice(t_index,1):'';
         }
-        projectGLJ.addMixRatio(gljOprObj.GLJSelection,function (mixRatios) {
-            me.showMixRatioData();//这里添加的组成物的消耗量默认都是0,所以对父工料机的价格不会有影响,不用触发计算
-            projectGLJ.loadData(function () {
-                me.showProjectGljData();
-                gljOprObj.showRationGLJSheetData();
-                me.onUnitFileChange(me.selectedProjectGLJ);
-            });
+        await me.addMixRatioFromLib(gljOprObj.GLJSelection,()=>{
+          me.showMixRatioData();//这里添加的组成物的消耗量默认都是0,所以对父工料机的价格不会有影响,不用触发计算
+          projectGLJ.loadData(function () {
+              me.showProjectGljData();
+              gljOprObj.showRationGLJSheetData();
+              me.onUnitFileChange(me.selectedProjectGLJ);
+          });
         });
     },
+    addMixRatioFromLib:async function(selections,callback){
+      let gljList = [],allGLJ = gljOprObj.AllRecode;
+      let url = "/glj/add-ratio";
+      let result = null;
+      if(selections.length == 0) {
+          return;
+      }
+      try {
+        for(let glj of allGLJ){
+        let i_key = gljUtil.getIndex(glj,gljLibKeyArray);
+        if(_.includes(selections,i_key)){
+            let pglj = {
+                glj_id: glj.ID,
+                name: glj.name,
+                code: glj.code,
+                original_code: glj.code,
+                unit: glj.unit,
+                specs: glj.specs,
+                base_price: glj.basePrice,
+                market_price: glj.basePrice,
+                shortName: glj.shortName,
+                type: glj.gljType,
+                model:glj.model,
+                adjCoe: glj.adjCoe,
+                from:'std',
+                repositoryId:glj.repositoryId,
+                materialType:glj.materialType,
+                materialCoe:glj.materialCoe,
+                grossWeightCoe:glj.grossWeightCoe,
+                purchaseStorageRate:glj.purchaseStorageRate,
+                offSiteTransportLossRate:glj.offSiteTransportLossRate,
+                handlingLossRate:glj.handlingLossRate
+            };
+            if(typeof projectObj !== 'undefined')  pglj.project_id = projectObj.project.ID();
+            if (glj.hasOwnProperty("compilationId")) {
+                pglj.from = "cpt";
+                if (glj.code.indexOf('-') != -1) {//这条工料机是用户通过修改名称、规格、型号等保存到补充工料机库的
+                    pglj.original_code = glj.code.split('-')[0];//取-前的编号作为原始编号
+                }
+            }
+            gljList.push(pglj);
+          }
+        }
+        gljList = _.sortByAll(gljList, ['type', 'code']);
+        if(gljList.length == 0) return;
+        let parentInfo ={};
+        if(typeof unitPriceObj !== 'undefined'){
+          url = "/unitPrice/addMixRatio";
+          pdata = unitPriceObj.getSelectedUnitPrice();
+          parentInfo = {
+            unit_price_file_id:pdata.unit_price_file_id,
+            connect_key:gljUtil.getIndex(pdata)
+          };
+        }else{
+          parentInfo = {
+            unit_price_file_id:projectObj.project.property.unitPriceFile.id,
+            connect_key:gljOprObj.getIndex(projectGljObject.selectedProjectGLJ,gljKeyArray)
+          };
+        }
+
+        $.bootstrapLoading.start();
+        result= await ajaxPost(url,{gljList:gljList,parentInfo:parentInfo})
+        if(callback) callback(result);
+      } catch (error) {
+        alert(error);
+        console.log(error);
+      }
+      $.bootstrapLoading.end();
+      return result;
+    },
     showMixRatioData:function () {
         let me = this,gljId = null,gljType = null;
         if(!me.projectGljSpread) return;
@@ -313,9 +384,11 @@ let projectGljObject={
           let style = gljOprObj.getSelStyle(true,{});
           me.projectGljSheet.setStyle(newSel.row, -1, style);
           let orow = oldSel.row==''||oldSel.row==-1?0:oldSel.row;
-          let tstyle = gljOprObj.getSelStyle(false,{},me.projectGljSheetData[orow].bgColour);
-          me.projectGljSheet.setStyle(orow, -1, tstyle);
-          me.projectGljRowChang();
+          if(me.projectGljSheetData[orow]){
+            let tstyle = gljOprObj.getSelStyle(false,{},me.projectGljSheetData[orow].bgColour);
+            me.projectGljSheet.setStyle(orow, -1, tstyle);
+            me.projectGljRowChang();
+          }
         }else{
           me.projectGljSheet.repaint();
         }
@@ -376,14 +449,15 @@ let projectGljObject={
         let me = projectGljObject;
         let canChange = true;
         let changeInfo=[];
-        if (info.action == GC.Spread.Sheets.RangeChangedAction.clear) {
+       /*  if (info.action == GC.Spread.Sheets.RangeChangedAction.clear) {
             info.newValue = 0;
             me.onMixRatioValueChange(sender,info);
             info.sheet.getCell(info.row, info.col).text(0);
             return ;
-        }
+        } */
         for(let c of info.changedCells){
             let value = info.sheet.getCell(c.row, c.col).text();
+            if(_.isEmpty(value)) value = 0;
             if (!me.checkData(c.col,me.mixRatioSetting,value)) {
                 alert('输入的数据类型不对,请重新输入!');
                 canChange = false;
@@ -392,12 +466,8 @@ let projectGljObject={
                 changeInfo.push({row:c.row,col:c.col,value:value});
             }
         }
-        if(canChange == false){//数据类型不对
-            me.showMixRatioData();
-        }
-        if(changeInfo.length > 0){
-            me.batchUpdateConsumption(changeInfo);
-        }
+        if(canChange == false) me.showMixRatioData(); //数据类型不对
+        if(changeInfo.length > 0) me.batchUpdateConsumption(changeInfo);
     },
     batchUpdateProjectGLJ:function(changeInfo,sheetName){
         let projectGLJ = projectObj.project.projectGLJ;
@@ -822,18 +892,7 @@ let projectGljObject={
             return false;
         }
         value = scMathUtil.roundToString(value,getDecimal("glj.quantity"));
-        let [parentMarketPrice, parentBasePrice] = me.getCompositionSumPrice('modify', row, value);
-        let updateData ={id: recode.mix_ratio_id, field: 'mix_ratio.' + dataCode, value: value, market_price: parentMarketPrice, base_price: parentBasePrice};
-        let prow = parentSheet.getActiveRowIndex();//取父机械或组成物的下标
-        let prowData = parentSheet.name() == 'projectGljSheet'?me.projectGljSheetData[prow]:me.materialTree.items[prow].data;
-        composition.updateConsumption(updateData,recode,prowData.id,function (sid) {
-            /* if(parentSheet.name() == 'projectGljSheet'){ 之前是单行刷新,父工料机与组成物对应的工料机分开刷,发现这样比整个刷新慢所以先整个刷新,当以后数据量大的时候再测试
-             me.refreshProjectGljRowByID(sid);
-             }*/
-            projectObj.project.projectGLJ.calcQuantity();
-            me.refreshParentData(prow,prowData.id,sid);
-            me.onUnitFileChange(recode);
-        });
+        me.batchUpdateConsumption([{row:row,col:col,value}]);
     },
     refreshParentData:function (row,pid,sid) {
         let me = this;
@@ -1035,22 +1094,6 @@ let projectGljObject={
     },
     checkData : function(col,setting, value) {
         return sheetCommonObj.checkData(col,setting, value);
-     /*   let result = true;
-        let validator = setting.header[col].validator !== undefined ? setting.header[col].validator : null;
-        if (validator === null) {
-            return result;
-        }
-        switch (validator) {
-            case 'number':
-                let regular = /^\d+(\.\d+)?$/;
-                result = regular.test(value);
-                break;
-            case 'boolean':
-                let booleanValue = [true, false];
-                result = booleanValue.indexOf(value) >= 0;
-                break;
-        }
-        return result;*/
     },
     getProjectGLJSelected:function () {
         let me = projectGljObject;

+ 42 - 26
web/building_saas/main/js/views/project_info.js

@@ -3,20 +3,42 @@
  */
 
 var projectInfoObj = {
-    getSubShareInfo: function (proj) {
-        const { allowCopy, allowCooperate } = proj.shareState;
-        let str = '(';
-        if (allowCopy) {
-            str += '可拷贝 ';
+    // 头部同时编辑的用户头像
+    handleAvatarList: function (users) {
+        const opener = projectObj.project.projectInfo.opener;
+        if (opener) {
+            users = users.filter(user => user._id !== opener._id);
         }
-        if (allowCooperate) {
-            if (allowCopy) {
-                str += ' ';
-            }
-            str += '可编辑';
+        const avatarListHtml = users.reduce((acc, user) => {
+            const avatarSpan = SHARE_TO.getAvatarHTML(user.mobile, user.real_name);
+            const li = 
+                `<li data-toggle="tooltip" data-placement="bottom" title="${user.real_name}" data-original-title="${user.real_name}">
+                    ${avatarSpan}
+                </li>`
+            return acc += li;
+        }, '');
+        $('#avatar-list').html(avatarListHtml);
+        $('#avatar-list [data-toggle="tooltip"]').tooltip(); 
+    },
+    getReceiveInfo: function (projectReadOnly, projectCooperate, owner) {
+        if (!projectReadOnly && !projectCooperate) {
+            return '';
+        }
+        const action = projectCooperate ? '可编辑' : '只能查看';
+        const ownerName = owner && owner.real_name || '';
+        return `
+            <span class="pl-2" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="来自 ${ownerName} 的分享">
+                <a href="javascript:;" class="btn btn-xs btn-primary default-cursor"><i class="fa fa-share-alt"></i> ${action}</a>
+            </span>`;
+    },
+    getShareButton: function (projectReadOnly, projectCooperate, shareTip) {
+        if (projectReadOnly || projectCooperate) {
+            return '';
         }
-        str += ')';
-        return str === '()' ? '' : str;
+        return `
+            <span id="share-tip" class="ml-2" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="${shareTip}">
+                <a id="init-share" href="javascript:;" class="btn btn-xs btn-primary"><i class="fa fa-share-alt"></i> 分享</a>
+            </span>`;
     },
     getFullPathHtml: function (proj) {
         let fullPath = [], i, pm = '<span class="text-truncate"><a href="/pm">项目管理</a></span>', angleRight = '<span class="text-truncate"><i class="fa fa-angle-right fa-fw"></i></span>';
@@ -25,26 +47,22 @@ var projectInfoObj = {
             let engName = pathArr[pathArr.length - 2] || '',
                 projectName = pathArr[pathArr.length - 3] || '',
                 folderName = pathArr[pathArr.length - 4] || '';
-            const subShareInfo = projectInfoObj.getSubShareInfo(proj);
-            const receiveTip = `<span class="alert alert-success py-0 px-2 m-0" id="share-info"><i class="fa fa-share-alt"></i>来自 ${proj.owner && proj.owner.real_name || ''} 的分享${subShareInfo}</span>`;
+            const receiveInfo = this.getReceiveInfo(projectReadOnly, projectCooperate, proj.owner);
+            const shareButton = this.getShareButton(projectReadOnly, projectCooperate, proj.shareTip);
             let newHtml = `   <span data-toggle="tooltip" data-placement="bottom" data-original-title="${folderName}"><i class="fa fa-folder-open-o"></i>...</span>
                 <span class="text-muted px-1">\</span>
                 <span data-toggle="tooltip" data-placement="bottom" data-original-title="${projectName}"><i class="fa fa-cubes"></i>...</span>
                 <span class="text-muted px-1">\</span>
                 <span data-toggle="tooltip" data-placement="bottom" data-original-title="${engName}"><i class="fa fa-cube"></i>...</span>
                 <span class="text-muted px-1">\</span>
-                 <span><i class="fa fa-sticky-note-o"></i></span>
+                <span><i class="fa fa-sticky-note-o"></i></span>
                 <span class="text-truncate"  data-toggle="tooltip" data-placement="bottom" data-original-title="${proj.name}">&nbsp;${proj.name}</span>
-                ${projectReadOnly || projectCooperate ? receiveTip : `<span id="share-tip" class="ml-2" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="${proj.shareTip}"><a id="init-share" href="javascript:;" class="btn btn-xs btn-primary"><i class="fa fa-share-alt"></i> 分享</a></span>`}
+                ${receiveInfo}${shareButton}
+                <span>
+                    <ul class="avatar-list mb-0" id="avatar-list">
+                    </ul>
+                </span>
                 `;
-            /* ${projectReadOnly ?
-                    '<span data-toggle="tooltip" data-placement="bottom" data-original-title="当前的工程状态为“只读”,如果要进行编辑,请在项目管理-分享界面,使用“拷贝工程”功能。">(只读)</span>'
-                    : ''}
-                ${projectCooperate ?
-                    '<span data-toggle="tooltip" data-placement="bottom" data-original-title="当前的工程状态为“协作”,可直接编辑分享人的原始数据。">(协作)</span>'
-                    : ''}
-                
-                ${projectReadOnly || projectCooperate ? '' : `<span id="share-tip" class="ml-2" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="${proj.shareTip}"><a id="init-share" href="javascript:;" class="btn btn-xs btn-primary"><i class="fa fa-share-alt"></i> 分享</a></span>`} */
             fullPath.push(newHtml);
             fullPath.push(`<input id="rootProjectName" value="${projectName}" type="hidden">`);
 
@@ -62,8 +80,6 @@ var projectInfoObj = {
             billsQuanDecimal.datas = data.property.billsQuantityDecimal || [billsDecimalView.angleDecimal];
             basicInfoView.orgDatas = data.property.basicInformation ? basicInfoView.toViewDatas(data.property.basicInformation) : [];
             projFeatureView.orgDatas = data.property.projectFeature ? projFeatureView.toViewDatas(data.property.projectFeature) : [];
-            console.log(`me.orgDatas`);
-            console.log(projFeatureView.orgDatas);
             $('#fullpath').html(this.getFullPathHtml(data));
             // 分享给
             $('#init-share').click(() => SHARE_TO.initModal(projectObj.project.ID()));

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

@@ -1013,7 +1013,8 @@ var projectObj = {
                 //if(!projectReadOnly){
                     that.loadMainSpreadContextMenu();
                 //}
-                socketObject.connect();//连接socket服务器
+                
+                socketObject.connect('main', { projectReadOnly: !!projectReadOnly, user: { ...projectObj.project.projectInfo.opener } });//连接socket服务器
                 let endTime = +new Date();
                 console.log(`其它时间——${endTime - endShowTime}`);
                 console.log("加载完成-----"+endTime);

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

@@ -248,7 +248,7 @@ const importView = (() => {
         let curEngineering = tbcObj.engineeringList.find(data => query.engineeringName === data.lib.name &&
             query.feeStandard === data.lib.feeName);
         return {
-            region: '全省',   //地区
+            region: commonConstants.DEFAULT_REGION,   //地区
             valuationType: tbcObj.valuationType,    //计价方式
             valuation: tbcObj.valuation.id, //计价规则
             valuationName: tbcObj.valuation.name,

+ 3 - 2
web/building_saas/pm/js/pm_newMain.js

@@ -3821,7 +3821,7 @@ function AddTender() {
             };
         let selectedItem = projTreeObj.tree.selected;
         //地区
-        let region = $('#regionDiv').find('select').val() || '全省';
+        let region = $('#regionDiv').find('select').val() || commonConstants.DEFAULT_REGION;
         let tenderInfo = {
             gljAdjustType: 'priceInfo', //默认为造价信息差额调整法
             valuation: valuation,
@@ -4529,7 +4529,8 @@ function set_file_table(target, poj_tenders, fileList, type){
         let fileId = type === fileType.unitPriceFile ? fileList[i].id : fileList[i].ID;
         let usedObj = getUsedObj(poj_tenders, fileId, type);
         let usedHtml = usedObj.usedCount > 0 ?  '<td class="text-center"><a href="javascript:void(0);">' + usedObj.usedCount + '</a></td>' : '<td class="text-center">' + usedObj.usedCount + '</td>';
-        let hoverHtml = '<p style="display: none; height: 14px;"><a class="btn btn-sm" href="javascript:void(0);" data-toggle="modal" data-target="#del-wj">删除</a><a class="btn btn-sm" href="javascript:void(0);">重命名</a></p></div>';
+        let unitPriceEditHtml = type === fileType.unitPriceFile? `<a class="btn btn-sm" href="/unitPrice/index/${fileId}" target="_">编辑</a>`:"";
+        let hoverHtml = `<p style="display: none; height: 14px;"><a class="btn btn-sm" href="javascript:void(0);" data-toggle="modal" data-target="#del-wj">删除</a><a class="btn btn-sm" href="javascript:void(0);">重命名</a>${unitPriceEditHtml}</p></div>`;
         let renHtml = '<div class="input-group" style="display: none;">'
             + '<input class="form-control form-control-sm" value="">'
 

+ 5 - 4
web/building_saas/report/js/rpt_main.js

@@ -7,6 +7,7 @@ const PRE_PAGE_OFFSET = 150;
 const NEXT_PAGE_OFFSET = 160;
 const FIRST_PAGE_OFFSET = 50;
 const LAST_PAGE_OFFSET = 60;
+const WAIT_TIME_EXPORT = 300000;
 
 let fontSuffixMapObj = {"表标题": "title", "列标题": "column", "正文内容": "content", "合计": "summary", "表眉/表脚": "header_footer"};
 
@@ -409,7 +410,7 @@ let rptControlObj = {
             params.custCfg = zTreeOprObj.reportPageCfg;
             params.option = "normal";
             $.bootstrapLoading.start();
-            CommonAjax.postEx("report_api/createExcelFilesInOneBook", params, 20000, true, function(result){
+            CommonAjax.postEx("report_api/createExcelFilesInOneBook", params, WAIT_TIME_EXPORT, true, function(result){
                     if (result) {
                         $.bootstrapLoading.end();
                         let uuIdUrls = [];
@@ -449,7 +450,7 @@ let rptControlObj = {
             params.custCfg = zTreeOprObj.reportPageCfg;
             params.option = "normal";
             $.bootstrapLoading.start();
-            CommonAjax.postEx("report_api/createExcelFiles", params, 20000, true, function(result){
+            CommonAjax.postEx("report_api/createExcelFiles", params, WAIT_TIME_EXPORT, true, function(result){
                     if (result) {
                         $.bootstrapLoading.end();
                         let uuIdUrls = [];
@@ -507,7 +508,7 @@ let rptControlObj = {
             params.orientation = ((zTreeOprObj.checkedRptTplNodes.length > 1)?null:me.getCurrentOrientation());
             params.custCfg = zTreeOprObj.reportPageCfg;
             params.option = "normal";
-            CommonAjax.postEx("report_api/createPdfFiles", params, 20000, true, function(result){
+            CommonAjax.postEx("report_api/createPdfFiles", params, WAIT_TIME_EXPORT, true, function(result){
                     if (result) {
                         let uuIdUrls = [];
                         for (let uuIdObj of result) {
@@ -555,7 +556,7 @@ let rptControlObj = {
                 rpt_names.push(tplNode.name);
             }
             params.rpt_ids = rptIds.join(",");
-            CommonAjax.postEx("report_api/getMultiReports", params, 30000, true,
+            CommonAjax.postEx("report_api/getMultiReports", params, WAIT_TIME_EXPORT, true,
                 function(result){
                     $.bootstrapLoading.end();
                     if ($('#ini_PDF_cover').is(':visible')) {

+ 235 - 0
web/building_saas/unit_price_file/index.html

@@ -0,0 +1,235 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <meta http-equiv="x-ua-compatible" content="ie=edge">
+    <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="/lib/bootstrap/css/bootstrap-submenu.css">
+    <link rel="stylesheet" href="/lib/spreadjs/sheets/css/gc.spread.sheets.sc.css" type="text/css">
+    <link rel="stylesheet" href="/web/building_saas/css/main.css">
+    <link rel="stylesheet" href="/web/building_saas/css/custom.css">
+    <link rel="stylesheet" href="/lib/font-awesome/font-awesome.min.css">
+    <link rel="stylesheet" href="/lib/jquery-contextmenu/jquery.contextMenu.css" type="text/css">
+    <link rel="stylesheet" href="/lib/jquery-editable-select/jquery.editable-select.min.css" type="text/css">
+    <!--zTree-->
+    <link rel="stylesheet" href="/lib/ztree/css/zTreeStyle.css" type="text/css">
+    <!--SpreadJs-->
+   <!-- <link rel="stylesheet" href="/lib/jquery-ui/jquery-ui.css" type="text/css">
+    <link rel="stylesheet" href="/lib/spreadjs/sheets/css/gc.spread.sheets.sc.css" type="text/css">-->
+    <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">
+        .ztree * {
+            font-family: Calibri;
+            font-size: 0.9rem;
+        }
+    </style>
+
+</head>
+<script type="text/javascript">
+  let unitPriceFileID = parseInt('<%- unitPriceFileID %>'); 
+  let userID = '<%- userID %>';
+  let projectID = '<%- projectID %>';
+  let taxType = '<%- taxType %>';
+  let region = '<%- region %>';
+  let unitPriceList = JSON.parse(`<%- unitpriceList %>`);
+  let gljTypeMap = JSON.parse(`<%- gljTypeMap %>`);
+  let mixRatioMap = JSON.parse(`<%- mixRatioMap %>`);
+</script>
+<body oncontextmenu="return false;">  <!--屏蔽input鼠标右键-->
+<!--<div id="toolToastWrap" style="left: 20px; right: 30px; position: fixed; z-index: 10001; top: 100px;">
+    <div id="toolToast" class="toolToast">
+        <span id="tool-toast-content">右键不支持粘贴外部内容,请使用Ctrl+V粘贴。<span id="toolToastBtn">我知道了</span></span>
+    </div>
+</div>-->
+
+    <div class="header">
+         <div class="top-msg clearfix">
+            <div class="alert alert-warning alert-dismissible" role="alert" id="notify" style="display: none">
+                <button type="button" class="close" aria-label="Close" onclick="$('#notify').hide();">
+                  <span aria-hidden="true">&times;</span>
+                </button>
+                <strong id="message"></strong>-
+            </div>
+        </div>
+        <nav class="navbar navbar-expand-lg p-0 d-flex <%= versionName.includes('免费') ? 'free-version' : 'pro-version' %>">
+          <div class="unit_price_header header-logo ">
+            <h5>单价文件编辑器</h5>
+          </div>
+          <div class="navbar-text navbar-crumb px-1 mr-auto" id="fullpath">   
+            <span class="text-muted px-1"></span>
+            <span data-toggle="tooltip" data-placement="bottom" data-original-title="<%= rootProjectName%>"><i class="fa fa-cubes"></i>...</span>
+            <span class="text-muted px-1"></span>
+             <span><i class="fa fa-puzzle-piece"></i></span>
+            <span class="text-truncate" data-toggle="tooltip" data-placement="bottom" data-original-title="<%= unitFileName%>">&nbsp;<%= unitFileName%></span>
+            &nbsp;(<span class="a_color" data-toggle="tooltip" id="pop-used-list"  style="float: none;" data-original-title="" title="<%= usedTenderString%>">与<span id="used-project-count" style="float: none;">1</span>个单位工程同步</span>)
+          </div>
+        </nav>
+    </div>
+    <div class="main">
+        <div class="content" style="margin-left: 0px;">
+          <div class="tab-content">
+              <!--造价书-->
+              <div class="container-fluid">
+                  <div class="row" id="mainRow">
+                      <!--col-lg-12 p-0-->
+                      <div class="main-content" style="width: 100%; display: inline-block" id="main">
+                          <div class="top-content" id="top_div" style="overflow:hidden;">
+                              <div class="main-data-top" id="mainSpread"></div>
+                          </div>
+                          <div class="resize-y" id="mainVerticalResize"></div>
+                          <div class="bottom-content" id="bottom_div">
+                              <ul class="nav nav-tabs" role="tablist" id="bottom_div_ul">  
+                                  <li class="nav-item" id="GLJ_div">
+                                      <a class="nav-link sub-item active" id="linkGLJ" data-toggle="tab" href="#subSpread" role="tab">组成物计算</a>
+                                  </li>                                
+                              </ul>
+                              <!-- Tab panes -->
+                              <div class="tab-content" id="tabCon">
+                                  <div class="tab-pane active" id="subItems" role="tabpanel">
+                                     <div class="main-data-bottom ovf-hidden" id="subSpread" tabindex="0"></div>
+                                  </div>
+                              </div>
+                          </div>
+                      </div>
+                      </div>
+                  </div>
+              </div>
+            </div>
+
+
+        </div>
+    </div>
+   
+   <!--工料机选择窗口-->
+   <div class="modal fade" id="glj_tree_div" data-backdrop="static">
+    <div class="modal-dialog modal-lg" role="document" id="modalCon">
+        <div class="modal-content" style="width: 1020px; left:50%; transform: translate(-50%,0%);">
+            <div class="modal-header">
+                <h5 class="modal-title">请选择人材机</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+                <input type="hidden" id="actionType">
+            </div>
+            <div class="modal-body" style="padding-left: 0; padding-right: 3px; margin-left: 0;">
+                    <div style="width: 32%; float: left;">
+                        &nbsp;
+                        <input type="radio" class="glj-radio" name="glj" value="stdGLJ">标准&nbsp;&nbsp;
+                        <input type="radio" class="glj-radio" name="glj" value="complementaryGLJs">补充&nbsp;&nbsp;
+                        <div  class="modal-auto-height" id="componentTreeDiv" style=" height: 435px; overflow: hidden;">
+                            <!--<div class="print-list">-->
+                            <div style="width: 100%; height: 100%; overflow: auto">
+                                <ul id="gljTree" class="ztree"></ul>
+                            </div>
+                            <!--</div>-->
+                        </div>
+                    </div>
+                    <div style="width:67%; padding-left: 3px; float: left;">
+                        <div id="gljRadios">
+                            <div class="row">
+                                <div class="col-7" style="margin-top: 5px;">
+                                    <!--<input type="radio" class="glj-radio" name="glj" value="allGljs" checked>所有&nbsp;&nbsp;
+                                    <input type="radio" class="glj-radio" name="glj" value="stdGLJ">标准&nbsp;&nbsp;
+                                    <input type="radio" class="glj-radio" name="glj" value="complementaryGLJs">补充&nbsp;&nbsp;-->
+                                </div>
+                                <div class="input-group col-5" style="margin-bottom: 5px;">
+                                    <input type="text" class="form-control form-control-sm" placeholder="请输入筛选编码或名称" value="" id="gljSearchKeyword">
+                                    <!--  <span class="input-group-btn"><button class="btn btn-secondary btn-sm" type="button" id="gljSearch"><i class="fa fa-search" aria-hidden="true"></i></button></span>-->
+                                </div>
+                            </div>
+                            <!-- <div class="form-group"><input id="searchGlj" type="text" class="form-control-sm" placeholder="查询工料机"></div>-->
+                        </div>
+                        <div class="modal-auto-height" style="overflow: hidden;height: 420px" id="gljLibSheet">
+
+                        </div>
+                    </div>
+            </div>
+            <div class="modal-footer" style="position: relative;">
+                <a href="/complementaryGlj" target="_blank" class="btn btn-primary" style="position: absolute; left: 20px">新增人材机</a>
+                <a href="javascript:void(0);" id="glj_selected_conf" class="btn btn-primary">确定</a>
+                <a href="javascript:void(0);" id="replace_next_btn" class="btn btn-primary">下一步</a>
+                <button type="button" id="componentsCacnel" class="btn btn-secondary" data-dismiss="modal">取消</button>
+            </div>
+        </div>
+    </div>
+  </div>
+   
+ 
+   
+    <img src="/web/dest/css/img/folder_open.png" id="folder_open_pic" style="display: none">
+    <img src="/web/dest/css/img/folder_close.png" id="folder_close_pic" style="display: none">
+    <img src="/web/dest/css/img/project.png" id="proj_pic" style="display: none">
+    <img src="/web/dest/css/img/engineering.png" id="eng_pic" style="display: none">
+    <img src="/web/dest/css/img/tender.png" id="tender_pic" style="display: none">
+
+    <img src="/web/dest/css/img/blockLib.png" id="blockLib_pic" style="display: none">
+    <img src="/web/dest/css/img/folder_open.png" id="folder_pic" style="display: none">
+    <img src="/web/dest/css/img/tender.png" id="block_pic" style="display: none">
+
+        <!-- JS. -->
+    <script src="/lib/jquery/jquery-3.2.1.min.js"></script>
+    <script type="text/javascript" src="/lib/jquery-ui/jquery-ui.min.js"></script>
+    <script src = "/lib/spreadjs/sheets/gc.spread.sheets.all.11.1.2.min.js"></script>
+    <script src="/lib/spreadjs/sheets/interop/gc.spread.excelio.11.1.2.min.js"></script>
+    <script>GC.Spread.Sheets.LicenseKey =  '<%- LicenseKey %>';</script>
+    <script type="text/javascript" src="/lib/jquery-ui/jquery-ui-datepickerCN.js"></script>
+    <script type="text/javascript" src="/lib/jquery-contextmenu/jquery.contextMenu.js"></script>
+    <script type="text/javascript" src="/lib/jquery-contextmenu/jquery.ui.position.js"></script>
+    <script src="/lib/spreadjs/views/gc.spread.views.dataview.10.0.0.min.js" type="text/javascript"></script>
+    <script type="text/javascript" src="/lib/ztree/jquery.ztree.core.js"></script>
+    <script type="text/javascript" src="/lib/ztree/jquery.ztree.excheck.js"></script>
+    <!--<script src="/lib/spreadjs/views/common/gc.spread.common.10.0.0.min.js" type="text/javascript"></script>-->
+    <script src="/lib/spreadjs/views/plugins/gc.spread.views.gridlayout.10.0.0.min.js" type="text/javascript"></script>
+    <script src="/lib/js-xlsx/xlsx.core.min.js"></script>
+    <script src="/lib/lz-string/lz-string.min.js"></script>
+    <script type="text/javascript" src="/lib/jspdf/jspdf.min.js"></script>
+    <!-- inject:js -->
+    <script src="/lib/popper/popper.min.js"></script>
+    <script src="/lib/bootstrap/bootstrap.min.js"></script>
+    <script src="/lib/bootstrap/bootstrap-submenu.js"></script>
+    <script src="/web/building_saas/glj/js/socket.io.slim.js"></script>
+    <script src="/public/web/socket/connection.js"></script>
+    <script type="text/javascript" src="/web/building_saas/js/moment.min.js"></script>
+    <script type="text/javascript" src="/web/building_saas/js/message.js"></script>
+    <script type="text/javascript" src="/public/web/scMathUtil.js"></script>
+    <script type="text/javascript" src="/public/web/gljUtil.js"></script>
+    <script type="text/javascript" src="/public/web/PerfectLoad.js"></script>
+    <script type="text/javascript" src="/lib/lodash/lodash.js"></script>
+    <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="/public/web/common_ajax.js"></script>
+    <script type="text/javascript" src="/public/common_util.js"></script>
+    <script src="/public/common_constants.js"></script>
+    <script type="text/javascript" src="/public/web/treeDataHelper.js"></script>
+    <script type="text/javascript" src="/public/web/ztree_common.js"></script>
+    <script type="text/javascript" src="/web/building_saas/main/js/models/main_consts.js"></script>
+    <script type="text/javascript" src="/lib/jquery-editable-select/jquery.editable-select.min.js"></script>
+    <script type="text/javascript" src="/public/web/tree_sheet/tree_sheet_helper.js"></script>
+    <script type="text/javascript" src="/public/web/sheet/sheet_data_helper.js"></script>
+    <script type="text/javascript" src="/public/web/sheet/sheet_common.js"></script>
+    <script type="text/javascript" src="/public/web/slideResize.js"></script>
+    <script type="text/javascript" src="/web/building_saas/main/js/views/glj_view_contextMenu.js"></script>
+    <script type="text/javascript" src="/web/building_saas/main/js/views/glj_view.js"></script>
+    <script type="text/javascript" src="/web/building_saas/main/js/views/options_view.js"></script>
+    <script type="text/javascript" src='/web/building_saas/main/js/views/project_glj_view.js'></script>
+    <script type="text/javascript" src="/web/building_saas/unit_price_file/index.js"></script>
+    <!-- endinject -->
+
+    <% if (overWriteUrl != undefined) { %>
+      <script type="text/javascript" src="<%= overWriteUrl%>"></script>
+    <% } %>
+        
+   
+</body>
+</html>

+ 591 - 0
web/building_saas/unit_price_file/index.js

@@ -0,0 +1,591 @@
+
+gljType = gljUtil.gljType;
+let unitPriceObj = {
+  unitPriceMap:{},
+  selectedData : null,
+  setUntiPriceMap:function(){
+    for(let u of unitPriceList){
+      this.unitPriceMap[gljUtil.getIndex(u)] = u;
+    }
+  },
+  mainSpread:null,
+  mainSetting:{
+    header: [
+        {headerName: "编号", headerWidth: 80, dataCode: "code", dataType: "String"},
+        {headerName: "名称", headerWidth: 160, dataCode: "name", dataType: "String"},
+        {headerName: "单位", headerWidth: 45, dataCode: "unit", hAlign: "center", dataType: "String"},
+        {headerName: "规格", headerWidth: 120, dataCode: "specs", hAlign: "left", dataType: "String"},
+        {headerName: "类型", headerWidth: 45, dataCode: "short_name", hAlign: "center", dataType: "String"},
+        {headerName: "定额价", headerWidth: 70, dataCode: "basePrice", hAlign: "right", dataType: "Number",validator:"number"},//decimalField:'glj.unitPrice',
+        {headerName: "预算价", headerWidth: 70, dataCode: "marketPrice", hAlign: "right", dataType: "Number",validator:"number"},//,decimalField:"glj.unitPrice"
+        {headerName: "是否\n新增", headerWidth: 50, dataCode: "is_add", hAlign: "center", dataType: "String",cellType:'checkBox'}
+    ],
+    view: {
+        lockColumns: ["code","name","specs","unit","short_name","basePrice"],
+        colHeaderHeight:36
+    },
+    getStyle:function (data,row,activeRow) {
+      if(row == activeRow){//选中黄色显示
+          return {backColor:"#FFFACD"};
+      }
+      return null;
+    }
+  },
+  subSpread:null,
+  subSetting:{
+    header:[
+        {headerName: "编号", headerWidth: 80, dataCode: "code", dataType: "String"},
+        {headerName: "名称", headerWidth: 240, dataCode: "name", dataType: "String"},
+        {headerName: "规格", headerWidth: 190, dataCode: "specs", dataType: "String"},
+        {headerName: "单位", headerWidth: 45, dataCode: "unit", hAlign: "center", dataType: "String"},
+        {headerName: "类型", headerWidth: 45, dataCode: "short_name", hAlign: "center", dataType: "String"},
+        {headerName: "定额价", headerWidth: 80, dataCode: "basePrice", hAlign: "right", dataType: "Number",validator:"number"},//,decimalField:'glj.unitPrice'
+        {headerName: "预算价", headerWidth: 80, dataCode: "marketPrice", hAlign: "right", dataType: "Number",validator:"number"},//,decimalField:"glj.unitPrice"
+        {headerName: "消耗量", headerWidth: 80, dataCode: "consumption", hAlign: "right", dataType: "Number",validator:"number",tofix:3}
+    ],
+    view: {
+        lockColumns: [0,1,2,3,4,5,6],
+        rowHeaderWidth: 25
+    }
+  },
+  initMainSpread:function () {
+    if(this.mainSpread) return this.mainSpread.refresh();
+    this.mainSpread = SheetDataHelper.createNewSpread($("#mainSpread")[0]);
+    sheetCommonObj.spreadDefaultStyle(this.mainSpread);
+    this.mainSheet = this.mainSpread.getSheet(0);
+    sheetCommonObj.initSheet(this.mainSheet, this.mainSetting, 30);
+    this.mainSheet.bind(GC.Spread.Sheets.Events.SelectionChanged,this.onMainSelectionChange);
+    this.mainSpread.bind(GC.Spread.Sheets.Events.RangeChanged, this.onMainRangeChange);
+    this.mainSheet.bind(GC.Spread.Sheets.Events.ValueChanged, this.onMainValueChange);
+    this.mainSheet.bind(GC.Spread.Sheets.Events.EditStarting,this.onMainEditStarting);
+  },
+  showMainDatas:function(){
+    unitPriceList = gljUtil.sortProjectGLJ(unitPriceList);
+    this.setData(unitPriceList);
+    let sel = this.mainSheet.getSelections()[0];
+    let oldData = this.selectedData;
+    sheetCommonObj.showData(this.mainSheet, this.mainSetting,unitPriceList);
+    this.mainSheet.setRowCount(unitPriceList.length);
+    sel.row =  oldData?_.findIndex(unitPriceList,{'id':oldData.id}):sel.row ;
+    this.mainSheet.setSelection(sel.row==-1?0:sel.row,sel.col,sel.rowCount,sel.colCount);
+  
+  },
+  getShortNameByType : function (type) {
+    return gljTypeMap["typeId" + type]?gljTypeMap["typeId" + type].shortName:'';
+  },
+  setData:function(list){
+    for(let l of list){
+      l.bgColour = "white";
+      l.basePrice = this.getPrice('base_price',l);
+      l.marketPrice = this.getPrice('market_price',l);
+      l.short_name = this.getShortNameByType(l.type);
+      if(l.base_price == l.market_price){//如果定额价等于市场价时,改底色。 优先度低于有组成物时的底色
+        l.bgColour = "#C4CAFB";
+      }
+      let lindex = gljUtil.getIndex(l);
+      if (gljUtil.notEditType.indexOf(l.type) >= 0 && mixRatioMap[lindex] && mixRatioMap[lindex].length>0) {//有组成物时
+        l.bgColour = "#E0E0E0";
+      }
+    }
+  },
+  getSelectedUnitPrice:function () {
+    let me = this,data = null;
+    let sheet = me.mainSpread.getActiveSheet();
+    let sel = sheet.getSelections()[0];
+        let srow = sel.row == -1||sel.row == ""?0:sel.row;
+        if(unitPriceList.length>srow){
+            data = unitPriceList[srow];
+        }
+    return data;
+  },
+  onMainSelectionChange:function(sender,args){
+    let me = unitPriceObj;
+    let newSel = args.newSelections[0];
+    let oldSel = args.oldSelections?args.oldSelections[0]:{};
+    args.sheet.suspendPaint();
+    args.sheet.suspendEvent();
+    if(newSel.row != oldSel.row){
+      let style = me.getSelStyle(true,{});
+      args.sheet.setStyle(newSel.row, -1, style);
+      let orow = oldSel.row==''||oldSel.row==-1?0:oldSel.row;
+      if(unitPriceList[orow]){
+        let tstyle = me.getSelStyle(false,{},unitPriceList[orow].bgColour);
+        args.sheet.setStyle(orow, -1, tstyle);
+        me.showSubDatas();
+        me.selectedData = unitPriceList[newSel.row];
+      }
+    }else{
+      args.sheet.repaint();
+    }
+    args.sheet.resumeEvent();
+    args.sheet.resumePaint();
+
+  },
+  onMainEditStarting:function(sender, args){
+    let me = unitPriceObj;
+    let row = args.row;
+    let col = args.col;
+    if(me.mainEditChecking(row,col)==false){
+        args.cancel = true;
+    }
+  },
+  mainEditChecking:function(row,col,isPaste=false){
+    let me = unitPriceObj;
+    if(row>=unitPriceList.length) return false;
+    let data = unitPriceList[row];
+    let setting = me.mainSetting;
+    let dataCode = setting.header[col].dataCode;
+    if(isPaste == false &&dataCode=='is_add'){//除了粘贴,拖动填充等操作,其它的都不能编辑
+      return false;
+    }
+    if(dataCode=='marketPrice'){
+      let uindex = gljUtil.getIndex(data);
+      if(mixRatioMap[uindex] && mixRatioMap[uindex].length > 0) return false;//有组成物的不能编辑
+      if(data.calcMaterial == 1) return false //有材料计算时不能修改
+    }
+    return true;
+  },
+  onMainValueChange:function(e,info){
+    let me = unitPriceObj;
+    let value = info.newValue;
+    if(info.newValue === undefined){
+      return;
+    }
+    if(value && !sheetCommonObj.checkData(info.col,me.mainSetting,value)) {
+      alert('输入的数据类型不对,请重新输入!');
+      return me.showMainDatas();
+    }
+    me.batchUpdateUnitPrice([{row:info.row,col:info.col,value:value}]);
+  },
+  onMainRangeChange:function(sender,info){
+    let me = unitPriceObj;
+    let canChange = true;
+    let changeInfo = [];
+    for(let c of info.changedCells){
+      let value=  info.sheet.getCell(c.row, c.col).text();
+      changeInfo.push({row:c.row,col:c.col,value:value});
+      if(me.mainEditChecking(c.row,c.col,true)==false){//如果不能编辑
+        canChange = false;
+        break;
+      }
+      if (!sheetCommonObj.checkData(c.col,me.mainSetting,value)) {
+          alert('输入的数据类型不对,请重新输入!');
+          canChange = false;
+          break;
+      }
+    }
+    if(canChange == false) return me.showMainDatas();
+    me.batchUpdateUnitPrice(changeInfo);
+  },
+  batchUpdateUnitPrice:async function(changeInfo){
+    let me = unitPriceObj;
+    let updateData = [];
+    let newValueMap = {};
+    let refreshList = [];
+    try {
+      for(let ci of changeInfo){
+        let dataCode = me.mainSetting.header[ci.col].dataCode;
+        let recode = unitPriceList[ci.row];
+        let ndata = newValueMap[recode.id]?newValueMap[recode.id]:{unit_price:recode};
+        if(dataCode=='basePrice'||dataCode=='marketPrice'){
+            let editField = dataCode === 'basePrice'?"base_price":"market_price";
+            let newValue = ci.value;
+            if(recode && recode[editField]!=newValue){
+                newValue= scMathUtil.roundForObj(ci.value,3); 
+                ndata['field'] = editField;
+                ndata['newval'] = newValue;
+                newValueMap[recode.id]=ndata;
+            }
+        }else{
+          let ext = {};
+          if(dataCode === 'is_add'){
+            value = ci.value == true?1:0;
+          }
+          ext[dataCode] = value;
+          ndata['ext'] = ext;
+          newValueMap[recode.id]=ndata;
+        }
+      }
+      for(let key in newValueMap){
+        updateData.push(newValueMap[key]);
+        refreshList.push(newValueMap[key].unit_price)
+      }
+      if(updateData.length > 0){
+        $.bootstrapLoading.start();
+        let result = await ajaxPost("/glj/batchUpdatePrices",updateData);
+        for(let r of refreshList){
+          let rdata = newValueMap[r.id];
+          if(rdata.field) r[rdata.field] = rdata.newval;
+          if(rdata.ext){
+            for(let ekey in rdata.ext){
+              r[ekey] = rdata.ext[ekey];
+            }
+          }
+           
+        }
+        for(let r of result){
+          let pdata = r.updateOne.filter;
+          let set = r.updateOne.update.$set;
+          for(let skey in set){
+              let pindex = gljUtil.getIndex(pdata);
+              me.unitPriceMap[pindex][skey] = set[skey];
+          }
+        }
+      }
+    } catch (error) {
+        console.log(error);
+        alert(error);
+    }
+    $.bootstrapLoading.end();
+    me.onUnitPriceChange();
+
+  },
+
+  initSubSpread:function () {
+    if(this.subSpread) return this.subSpread.refresh();
+    this.subSpread = SheetDataHelper.createNewSpread($("#subSpread")[0]);
+    sheetCommonObj.spreadDefaultStyle(this.subSpread);
+    this.subSheet = this.subSpread.getSheet(0);
+    sheetCommonObj.initSheet(this.subSheet, this.subSetting, 30);
+    this.subSheet.bind(GC.Spread.Sheets.Events.RangeChanged, this.onSubRangeChange);
+    this.subSheet.bind(GC.Spread.Sheets.Events.ValueChanged, this.onSubValueChange);
+    this.initSubRightClick();
+  },
+  showSubDatas:function(){
+    let parentData = this.getSelectedUnitPrice();
+    this.mixRatioList = mixRatioMap[gljUtil.getIndex(parentData)];
+    this.mixRatioList = this.mixRatioList?this.mixRatioList:[];
+    this.setMixRatioData(this.mixRatioList);
+    let sel = this.subSheet.getSelections()[0];
+    this.subSheet.setRowCount(0);
+    sheetCommonObj.showData(this.subSheet, this.subSetting,this.mixRatioList);
+    this.subSheet.setRowCount(this.mixRatioList.length);
+    this.subSheet.setSelection(sel.row==-1?0:sel.row,sel.col,sel.rowCount,sel.colCount);
+  },
+  onSubValueChange:function(sender,info){
+    let me = unitPriceObj;
+    let value = info.newValue;
+    if(info.newValue === undefined){
+      return;
+    }
+    if(value && !sheetCommonObj.checkData(info.col,me.mainSetting,value)) {
+      alert('输入的数据类型不对,请重新输入!');
+      return me.showMainDatas();
+    }
+    me.batchUpdateConsumption([{row:info.row,col:info.col,value:value}]);
+    
+  },
+  onSubRangeChange:function(sender,info){
+    let me = unitPriceObj;
+    let changeInfo=[];
+    let canChange = true;
+    for(let c of info.changedCells){
+      let value = info.sheet.getCell(c.row, c.col).text();
+      if(_.isEmpty(value)) value = 0;
+      if (!sheetCommonObj.checkData(c.col,me.subSetting,value)) {
+          alert('输入的数据类型不对,请重新输入!');
+          canChange = false;
+          break;
+      }else {
+          changeInfo.push({row:c.row,col:c.col,value:value});
+      }
+    }
+    if(canChange == false) me.showSubDatas();
+    me.batchUpdateConsumption(changeInfo);
+  },
+  calcParentPrice:function(list,updateMap,deleteID){
+    let parentMarketPrice = 0;
+    for(let sub of list){
+      if(deleteID && sub.id == deleteID) continue
+      let marketPrice = scMathUtil.roundForObj(sub.marketPrice,6);
+      let basePrice = scMathUtil.roundForObj(sub.basePrice,6);
+      let consumption =  updateMap[sub.id]?updateMap[sub.id].consumption:scMathUtil.roundForObj(sub.consumption,3);
+      parentMarketPrice = scMathUtil.roundForObj(marketPrice*consumption + parentMarketPrice,6);
+     // parentBasePrice = scMathUtil.roundForObj(basePrice*consumption + parentBasePrice,getDecimal("process"));
+    }
+    parentMarketPrice = scMathUtil.roundForObj(parentMarketPrice,2);
+    return parentMarketPrice;
+  },
+
+  deleteMixRatio:async function(row){
+    let me = this, deleteRecode = me.mixRatioList[row];
+    let parentMarketPrice = me.calcParentPrice(me.mixRatioList,{},deleteRecode.id);
+    let parentData = me.getSelectedUnitPrice();
+    let updateData = {id: deleteRecode.id,connect_key:gljUtil.getIndex(parentData)};
+    updateData.parent = {connect_key:gljUtil.getIndex(parentData),query:{id:parentData.id},doc:{market_price: parentMarketPrice}};
+    try {
+      $.bootstrapLoading.start();
+      await ajaxPost("/glj/delete-ratio",{id: updateData.id,parent:updateData.parent,unitPrice:updateData.unitPrice});
+      let mlist =  mixRatioMap[updateData.connect_key];
+      _.remove(mlist,{id:updateData.id});
+      if(updateData.parent){
+        let pglj = me.unitPriceMap[updateData.parent.connect_key];
+        if(pglj) gljUtil.setProperty(pglj,updateData.parent.doc);
+      }
+
+    } catch (error) {
+      alert(error);
+      console.log(error);
+    }
+
+    $.bootstrapLoading.end();
+    me.onUnitPriceChange();
+  },
+  batchUpdateConsumption:async function(changeInfo){
+    let me = unitPriceObj;
+    let updateMap = {},updateData = [],parentBasePrice = 0,parentMarketPrice = 0;
+    let parentKey = '',unit_price_file_id=null;
+    for(let c of changeInfo){
+      let record = me.mixRatioList[c.row];
+      let value =  scMathUtil.roundForObj(c.value,3);
+      updateMap[record.id] = {consumption: value,record:record};
+      updateData.push({type:'mix_ratio',connect_key:record.connect_key,query:{id:record.id},doc:{consumption:scMathUtil.roundToString(value,3)}});
+      parentKey = record.connect_key;
+      unit_price_file_id = record.unit_price_file_id;
+    }
+    
+    parentMarketPrice = me.calcParentPrice(me.mixRatioList,updateMap);
+    
+    let parentData = me.getSelectedUnitPrice();
+    if(parentData){
+      updateData.push({type:'unitPrice',connect_key:gljUtil.getIndex(parentData),query:{id:parentData.id},doc:{market_price:parentMarketPrice}});
+    }
+    if(updateData.length > 0){
+      try {
+        $.bootstrapLoading.start();
+        let result = await ajaxPost("/glj/batchUpdateConsumption",updateData);
+        for(let up of updateData){
+          if(up.type == 'unitPrice'){
+            let nu = me.unitPriceMap[up.connect_key];
+            if(nu) gljUtil.setProperty(nu,up.doc);
+          }else{
+            let nlist = mixRatioMap[up.connect_key];
+            for(let nc of nlist){
+                if(nc.id == up.query.id){
+                  gljUtil.setProperty(nc,up.doc);
+                }
+            } 
+          }
+        }
+      } catch (error) {
+        alert(error);
+        console.log(error);
+      }
+    }
+    $.bootstrapLoading.end();
+    me.onUnitPriceChange();
+  },
+  setMixRatioData:function(mixRatioList){
+    for(let m of mixRatioList){
+      m.short_name = this.getShortNameByType(m.type);
+      let mu = this.unitPriceMap[gljUtil.getIndex(m)];
+      if(mu){
+        m.basePrice = this.getPrice("base_price",mu);
+        m.marketPrice = this.getPrice("market_price",mu);
+      }else{
+        console.log("组成物的单价信息未找到---"+m.code);
+      }
+    }
+  },
+  getPrice:function(pricefield,unitprice){
+    let quantity = 3;
+    let unitPriceHasMix = 2;
+    let unitPrice = 3;
+    let process_decimal = 6;
+    let uIndex = gljUtil.getIndex(unitprice);
+    if(mixRatioMap[uIndex] && mixRatioMap[uIndex].length > 0){
+      let total = unitprice[pricefield];
+      if(pricefield == "market_price"){
+        total = 0;
+        for(let m of mixRatioMap[uIndex]){
+          let mu = unitPriceObj.unitPriceMap[gljUtil.getIndex(m)];
+          let price_m =  unitPriceObj.getPrice(pricefield,mu);
+          let temP = scMathUtil.roundForObj(price_m * scMathUtil.roundForObj(m.consumption,quantity),process_decimal);
+          total = scMathUtil.roundForObj(temP+total,process_decimal);
+        }
+      }
+      return scMathUtil.roundForObj(unitprice[pricefield],unitPriceHasMix);
+    }else{
+      return scMathUtil.roundForObj(unitprice[pricefield],unitPrice);
+    }
+  },
+  getSelStyle: function (selected,settingStyle,rcolor) {
+    let style = new GC.Spread.Sheets.Style();
+    if(settingStyle){
+        for(let key in settingStyle){
+            style[key] = settingStyle[key];
+        }
+    }
+    style.borderLeft = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
+    style.borderTop = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
+    style.borderRight = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
+    style.borderBottom = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
+    let selectedColor = "#FFFACD",
+        recColor = rcolor?rcolor:'White';
+    style.backColor = selected ? selectedColor : recColor;
+    return style;
+  },
+  initSubRightClick:function(){
+    let me = this;
+    $.contextMenu({
+      selector: '#subSpread',
+      build: function ($trigger, e) {
+          me.rightClickTarget = SheetDataHelper.safeRightClickSelection($trigger, e, me.subSpread);
+          return me.rightClickTarget.hitTestType === GC.Spread.Sheets.SheetArea.viewport ||
+              me.rightClickTarget.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
+      },
+      items: {
+          "deleteMixRatio": {
+              name: "删除",
+              icon: 'fa-trash-o',
+              disabled: function () {
+                  return me.rightClickTarget.row === undefined;
+              },
+              callback: function (key, opt) {
+                  let row = me.rightClickTarget.row;
+                  me.deleteMixRatio(row);
+              },
+              visible: function(key, opt){
+                  let projectGLJ = me.getSelectedUnitPrice();
+                  return  projectGLJ;
+              }
+          },
+          "addMixRatio":{
+              name: '添加',
+              icon: 'fa-sign-in',
+              disabled: function () {
+                let projectGLJ = me.getSelectedUnitPrice();
+                return !_.includes(compositionTypes,projectGLJ.type);
+              },
+              callback: function (key, opt) {
+                   projectGljObject.selectedProjectGLJ = me.getSelectedUnitPrice();
+                   getGLJData('unitPriceAddMix',me.getGLJDataPaging);
+              },
+              visible: function(key, opt){
+                  let projectGLJ = me.getSelectedUnitPrice();
+                  return  projectGLJ ;
+              }
+          }
+      }
+     });
+  },
+  addMixRatio:async function(){
+     let me = this;
+    for(let mix of me.mixRatioList){
+      let m_key = gljUtil.getIndex(mix);
+      _.remove(gljOprObj.GLJSelection,n =>{
+        return m_key == n;
+      });   
+    }
+
+    let result = await projectGljObject.addMixRatioFromLib(gljOprObj.GLJSelection)
+    if(result.mixRatios && result.mixRatios.length > 0){
+      let mList = mixRatioMap[result.mixRatios[0].connect_key];
+      if(mList){
+        mixRatioMap[result.mixRatios[0].connect_key] = mixRatioMap[result.mixRatios[0].connect_key].concat(result.mixRatios);
+      }
+    }
+    for(let u of result.newUnitPriceList){
+      me.unitPriceMap[gljUtil.getIndex(u)] = u;
+      unitPriceList.push(u);
+    }
+    me.onUnitPriceChange();
+  },
+
+  onUnitPriceChange:async function(){
+    unitPriceObj.showMainDatas();
+    unitPriceObj.showSubDatas();
+    try {
+      await ajaxPost("/project/markUpdateProject",{updateInfo:{unitFileID:unitPriceFileID,isInclude:true},type:"unitFile"});
+      socket.emit('unitFileChangeNotify', {projectID:_.isEmpty(projectID)?null:parseInt(projectID),'unitFileID':unitPriceFileID,userID:userID});
+    } catch (error) {
+      console.log(error)
+      alert(error)
+    }
+  }, 
+  getGLJDataPaging : function (condition, cb) {
+    gljOprObj.loadingPagination = true;
+    CommonAjax.post('/rationGlj/getGLJDataPaging', {condition}, function (data) {
+        gljOprObj.curPageTotal = data.total;
+        data.complementaryGLJs.forEach(glj => {
+            glj.isComplementary = true;
+        });
+        const gljType = condition.type === gljOprObj.pagingType.stdGLJ
+            ? 'stdGLJ'
+            : 'complementaryGLJs';
+        const newData = data[gljType];
+        // 添加组成物,类型为主材时,需要排除自身
+        if (projectGljObject.selectedProjectGLJ.type === gljType.MAIN_MATERIAL) {
+            const pIndex = gljOprObj.getIndex(projectGljObject.selectedProjectGLJ, gljKeyArray);
+            const delIndex = newData.findIndex(item => gljOprObj.getIndex(item, gljLibKeyArray) === pIndex);
+            if (!~delIndex) {
+                newData.splice(delIndex, 1);
+            }
+        }
+        if (condition.init) {
+            gljOprObj.treeData = data.treeData;
+            gljOprObj.distTypeTree = gljOprObj.getComboData(data.distTypeTree);
+        }
+        // 需要重置当前页面数据(点击了分类树、搜索等等)
+        if (condition.reset) {
+            gljOprObj.stdGLJ = data.stdGLJ;
+            gljOprObj.complementaryGLJs = data.complementaryGLJs;
+            gljOprObj.AllRecode = [...gljOprObj.stdGLJ, ...gljOprObj.complementaryGLJs];
+        } else {
+            gljOprObj[gljType].splice(condition.index, 0, ...newData);
+            gljOprObj.AllRecode.splice(condition.index, 0, ...newData);
+        }
+        // 根据缓存选中数据,设置人材机是否选中
+        newData.forEach(item => {
+            const connectKey = gljOprObj.getIndex(item, gljLibKeyArray);
+            if (gljOprObj.GLJSelection.includes(connectKey)) {
+                item.select = 1;
+            }
+        });
+        // 设置人材机类型名称
+        gljOprObj.setTypeName(gljOprObj.distTypeTree.comboDatas, newData);
+        if (data.priceProperties && data.priceProperties.length > 0) {
+            let tregion = '全省';
+            if(!_.isEmpty(region)) tregion = region;
+            let tmp = _.find(data.priceProperties, {region: tregion, taxModel: parseInt(taxType)});
+            if (tmp) {
+                let dataCode = tmp.price.dataCode;
+                let allData = data.stdGLJ.concat(data.complementaryGLJs);
+                for (let glj of allData) {
+                    if (glj.priceProperty && gljUtil.isDef(glj.priceProperty[dataCode])) {
+                        glj.basePrice = glj.priceProperty[dataCode];
+                    }
+                }
+            }
+        }
+        cb(data[gljType]);
+        gljOprObj.loadingPagination = false;
+    }, function () {
+        if ($.bootstrapLoading.isLoading()) {
+            $.bootstrapLoading.end();
+        }
+    });
+  }
+}
+
+
+
+function initPageHeight(){
+  let headerHeight = $(".header").height();
+  $(".main-data-top").height($(window).height()*0.6-headerHeight);
+  $(".main-data-bottom").height($(window).height()-headerHeight-$(".main-data-top").height()-$(".nav-item").height());
+}
+function initPage(){
+  $('[data-toggle="tooltip"]').tooltip({html: true});
+  initPageHeight();
+  unitPriceObj.initMainSpread();
+  unitPriceObj.initSubSpread();
+  unitPriceObj.showMainDatas();
+  unitPriceObj.showSubDatas();
+  socketObject.connect("unitPrice");
+}
+
+unitPriceObj.setUntiPriceMap();
+initPage();
+$(window).resize(initPage);
+

+ 1 - 0
web/common/components/share/index.js

@@ -433,5 +433,6 @@ const SHARE_TO = (() => {
     return {
         initModal,
         handleEventListener,
+        getAvatarHTML,
     }
 })();

+ 9 - 9
web/over_write/js/chongqing_2018.js

@@ -49,16 +49,16 @@ if(typeof materialComponent !== 'undefined'){
 // CSL, 2018-08-21 计算程序、基数 的覆盖。---------------------------------------------------------------------------------
 let isCQ2018 = true;
 
-if(typeof baseMaterialTypes !== 'undefined'){
-    baseMaterialTypes.push(gljType.OTHER_MATERIAL);
-    // allMaterialTypes.delete(gljType.EQUIPMENT);  新规定里重庆材料类型里需要包含设备费。
-    baseMachineTypes.delete(gljType.MACHINE_COMPOSITION);
-    baseMachineTypes.push(gljType.INSTRUMENT, gljType.FUEL_POWER_FEE, gljType.DEPRECIATION_FEE,
-        gljType.INSPECTION_FEE, gljType.MAINTENANCE, gljType.DISMANTLING_FREIGHT_FEE,
-        gljType.VERIFICATION_FEE, gljType.OTHER_FEE, gljType.OTHER_MACHINE_USED);
-    baseMachineMasterTypes.push(gljType.INSTRUMENT);
-}
+if(typeof baseMaterialTypes !== 'undefined') baseMaterialTypes.push(gljType.OTHER_MATERIAL);
 
+if(typeof baseMachineMasterTypes !== 'undefined') baseMachineMasterTypes.push(gljType.INSTRUMENT);
+if(typeof baseMachineTypes !== 'undefined'){
+  _.pull(baseMachineTypes,gljType.MACHINE_COMPOSITION);
+  //baseMachineTypes.delete(gljType.MACHINE_COMPOSITION);
+  baseMachineTypes.push(gljType.INSTRUMENT, gljType.FUEL_POWER_FEE, gljType.DEPRECIATION_FEE,
+      gljType.INSPECTION_FEE, gljType.MAINTENANCE, gljType.DISMANTLING_FREIGHT_FEE,
+      gljType.VERIFICATION_FEE, gljType.OTHER_FEE, gljType.OTHER_MACHINE_USED);
+}
 
 function overwriteRationCalcBases (taxType){
     if (rationCalcBases){

+ 8 - 8
web/users/html/index.html

@@ -8,7 +8,7 @@
       <link rel="stylesheet" href="/lib/bootstrap/css/bootstrap.min.css">
       <link rel="stylesheet" href="/web/building_saas/css/all.css">
       <link rel="stylesheet" href="/web/building_saas/css/style.css">
-      <title>大司空云计价 - 正版软件永久免费</title>
+      <title>大司空云计价 - 远程办公,用免费正版市政计价软件</title>
       <link rel="shortcut icon" href="/web/building_saas/css/favicon.ico" />
    </head>
    <body>
@@ -20,10 +20,10 @@
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarLexar" aria-controls="navbarLexar" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
                </button>
-               <div class="collapse navbar-collapse" id="navbarLexar">      
+               <div class="collapse navbar-collapse" id="navbarLexar">
                <ul class="navbar-nav ml-auto">
                      <li class="nav-item active">
-                        <a class="btn btn-primary my-3" href="/login">登录软件</a>
+                        <a class="btn btn-danger my-3" href="/login">登录软件</a>
                      </li>
                   </ul>
                </div>
@@ -35,10 +35,10 @@
          <div class="container text-center">
             <h1 class="text-white mb-4 f-50 ">大司空市政云计价,正版软件永久免费</h1>
             <p class="lead text-white mb-5">跨平台,打开浏览器即可使用,全新在线计价体验。
-            <!-- <div class="btn_hero mb-5">
-               <a href="#" class="btn btn-primary btn-round mr-2">GET THIS APP</a>
-               <a href="#" class="btn btn-outline-primary btn-round ml-2">TRY IT FOR FREE</a>
-            </div> -->
+            <div class="btn_hero">
+               <a href="/login" class="btn btn-danger mr-2">登录软件</a>
+               <a href="https://smartcost.com.cn/contact2" target="_blank" class="btn btn-primary ml-2">联系客服</a>
+            </div>
             <div class="home-desk mt-5">
                <div class="container pt-5">
                   <img src="/web/users/images/home-desk.png" alt="" class="img-fluid mx-auto  d-block">
@@ -351,7 +351,7 @@
                      <div class="footer_bottom text-center">
                         Copyright@珠海纵横创新软件有限公司 all rights reserved <a href="http://www.miitbeian.gov.cn" target="_blank">粤ICP备14032472号</a>
                      </div>
-                     <div style="position:fixed;right:-100px"><script type="text/javascript">var cnzz_protocol = (("https:" == document.location.protocol) ? "https://" : "http://");document.write(unescape("%3Cspan id='cnzz_stat_icon_1278513319'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "v1.cnzz.com/stat.php%3Fid%3D1278513319%26show%3Dpic1' type='text/javascript'%3E%3C/script%3E"));</script></div>
+                     <div><script type="text/javascript">var cnzz_protocol = (("https:" == document.location.protocol) ? "https://" : "http://");document.write(unescape("%3Cspan id='cnzz_stat_icon_1278513319'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "v1.cnzz.com/stat.php%3Fid%3D1278513319%26show%3Dpic1' type='text/javascript'%3E%3C/script%3E"));</script></div>
                   </div>
                </div>
             </div>