Browse Source

Merge branch 'master' of http://192.168.1.41:3000/SmartCost/YangHuCost

TonyKang 5 years ago
parent
commit
ea021edae2
34 changed files with 461 additions and 77 deletions
  1. 2 0
      config/gulpConfig.js
  2. 1 0
      modules/all_models/fee_rates.js
  3. 2 2
      modules/all_models/material_calc.js
  4. 1 1
      modules/all_models/mix_ratio.js
  5. 20 0
      modules/common/base/base_model.js
  6. 1 0
      modules/fee_rates/facade/fee_rates_facade.js
  7. 9 3
      modules/glj/facade/glj_facade.js
  8. 1 1
      modules/glj/models/glj_list_model.js
  9. 10 13
      modules/glj/models/mix_ratio_model.js
  10. 25 15
      modules/glj/models/unit_price_model.js
  11. 5 2
      modules/main/facade/project_facade.js
  12. 2 2
      modules/main/facade/ration_facade.js
  13. 11 1
      modules/pm/controllers/pm_controller.js
  14. 24 0
      modules/pm/facade/pm_facade.js
  15. 13 7
      modules/pm/models/project_model.js
  16. 11 0
      modules/users/controllers/user_controller.js
  17. 12 0
      modules/users/models/user_model.js
  18. 1 0
      modules/users/routes/user_route.js
  19. 11 0
      public/scHintBox.html
  20. 21 1
      public/web/headerOpr.js
  21. 186 0
      public/web/syntax-detection.js
  22. 9 0
      web/building_saas/css/custom.css
  23. 6 1
      web/building_saas/main/js/views/fee_rate_view.js
  24. 1 1
      web/building_saas/main/js/views/project_view.js
  25. 5 2
      web/building_saas/main/js/views/quantity_edit_view.js
  26. 4 3
      web/building_saas/pm/html/project-management.html
  27. 8 0
      web/building_saas/report/js/rpt_main.js
  28. 4 0
      web/building_saas/report/js/rpt_print.js
  29. 12 8
      web/common/components/share/index.js
  30. 2 2
      web/common/html/header.html
  31. 30 2
      web/over_write/js/nongcun_2020.js
  32. 1 1
      web/users/html/login-sms.html
  33. 8 6
      web/users/html/login.html
  34. 2 3
      web/users/js/login.js

+ 2 - 0
config/gulpConfig.js

@@ -33,6 +33,7 @@ module.exports = {
     ],
     login_jspaths:[
         'public/web/url_util.js',
+        'public/web/syntax-detection.js',
         'web/users/js/gt.js',
         'web/users/js/login.js'
     ],
@@ -41,6 +42,7 @@ module.exports = {
         'lib/jquery-contextmenu/jquery.contextMenu.css'
     ],
     pm_jspaths:[
+        'public/web/syntax-detection.js',
         'public/web/uuid.js',
         'public/web/date_util.js',
         'public/web/id_tree.js',

+ 1 - 0
modules/all_models/fee_rates.js

@@ -44,6 +44,7 @@ let ratesSchema = new Schema({
     ID: Number,
     ParentID: Number,
     name: String,
+    originalRate: Number,
     rate: Number,
     memo: String,
     sum:Boolean,

+ 2 - 2
modules/all_models/material_calc.js

@@ -53,7 +53,7 @@ var ration_glj = new Schema({
     from:{type: String,default:'std'}//std, cpt  来自标准工料机库、补充工料机库
 },{versionKey:false,_id: false});
 
-let ration_schema = {
+let ration_schema = new Schema({
     ID: {type: String},
     projectID: Number,
     code: String,
@@ -66,7 +66,7 @@ let ration_schema = {
     stdID:Number,
     from:String,
     rationAssList: [rationAssItemSchema]
-};
+},{versionKey:false,_id: false});
 
 
 let freightSchema = {

+ 1 - 1
modules/all_models/mix_ratio.js

@@ -11,7 +11,7 @@ let Schema = mongoose.Schema;
 let collectionName = 'mix_ratio';
 let modelSchema = {
     // 自增id
-    id: Number,
+    id: {type:Number,unique: true},
     // 消耗量(有别于总消耗,此字段记录配合比的消耗量)
     consumption: {
         type: String,

+ 20 - 0
modules/common/base/base_model.js

@@ -6,6 +6,8 @@
  * @version
  */
 import MongooseHelper from "../helper/mongoose_helper";
+let mongoose = require('mongoose');
+let counterModel =  mongoose.model('counter');
 
 class BaseModel {
 
@@ -114,6 +116,24 @@ class BaseModel {
         return result;
     }
 
+    async setIDfromCounter(name,list,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]
+          }
+      }
+    }
+
     /**
      * 更新数据
      *

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

@@ -404,6 +404,7 @@ async function newFeeRateFile(userId, updateData){
                 let newFeeRate = {};
                 newFeeRate.ID =uuidV1();
                 newFeeRate.rates=template.rates;
+                (newFeeRate.rates || []).forEach(item => item.originalRate = item.rate);
                 await feeRateModel.create(newFeeRate);
                 doc.libID = libID;
                 doc.libName = template.libName;

+ 9 - 3
modules/glj/facade/glj_facade.js

@@ -81,9 +81,15 @@ async function changeUnitFile(projectData,unitFile,type,userID) {
                     n.unit_price_file_id = targetUnitPriceFile.id;
                     copyList.push(n);
                 }
-                copyList.length>0 ? await unitPriceModel.add(copyList):'';
+                copyList.length>0 ? await unitPriceModel.add(copyList):''; 
+                //也要复制一份组成物信息和材料计算信息
+                let mixRatioModel = new MixRatioModel();
+                await mixRatioModel.copyNotExist(changeUnitPriceId, targetUnitPriceFile.id,{},true);//复制组成物
+                await unitPriceModel.copyMaterialNotExist(changeUnitPriceId,targetUnitPriceFile.id,{},true);
             }
         }
+
+
         let copyResult = await unitPriceModel.copyNotExist(currentUnitPriceId, targetUnitPriceFile.id,projectId);
         // 复制成功后更改project数据
         if (!copyResult) {
@@ -98,8 +104,8 @@ async function changeUnitFile(projectData,unitFile,type,userID) {
         if (!result) {
             throw '切换单价文件失败!';
         }
-        //处理车船税记录
-        await handleVvTaxForChang(targetUnitPriceFile.id,targetUnitPriceFile.vvTaxFileID);
+        //处理车船税记录 - 如果直接从其它项目复制,不用管车船税问题
+        if(type !==1) await handleVvTaxForChang(targetUnitPriceFile.id,targetUnitPriceFile.vvTaxFileID);
         return changeUnitPriceFileInfo;
 }
 

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

@@ -705,7 +705,7 @@ class GLJListModel extends BaseModel {
             }
             let basePrice = tmp.basePrice;
             //只有标准的工料机的组成物才会有多单价、多组成物消耗量的情况 fromTable
-            if(fromTable == 'std' && ext && ext.priceField &&( tmp.priceProperty[ext.priceField]!= undefined && tmp.priceProperty[ext.priceField]!=null)){
+            if(fromTable == 'std' && ext && ext.priceField &&(tmp.priceProperty && tmp.priceProperty[ext.priceField]!= undefined && tmp.priceProperty[ext.priceField]!=null)){
                 basePrice = tmp.priceProperty[ext.priceField];
             }
             basePrice = scMathUtil.roundTo(basePrice,-6);

+ 10 - 13
modules/glj/models/mix_ratio_model.js

@@ -50,14 +50,14 @@ class MixRatioModel extends BaseModel {
     async add(data) {
         let counterModel = new CounterModel();
         if (data instanceof Array) {
-            for(let tmp in data) {
+          await this.setIDfromCounter(collectionName,data);
+            /* for(let tmp in data) {
                 data[tmp].id = await counterModel.getId(collectionName);
-            }
+            } */
         } else {
             data.id = await counterModel.getId(collectionName);
         }
-
-        return this.db.model.create(data);
+        return await this.db.model.create(data);
     }
 
     /**
@@ -79,32 +79,29 @@ class MixRatioModel extends BaseModel {
     }
 
     //复制组成物到切换后的组成物映射表
-    async copyNotExist(currentUnitPriceId, changeUnitPriceId,gljMap){
+    async copyNotExist(currentUnitPriceId, changeUnitPriceId,gljMap,newFile = false){
         let currentMap = {},targetMap = {}, insertData = [];
         //取原单价文件所有的的组成物
-        let currentList = await  this.db.find({'unit_price_file_id':currentUnitPriceId});
-        // 过滤mongoose格式
-        currentList = JSON.stringify(currentList);
-        currentList = JSON.parse(currentList);
+        let currentList = await  this.model.find({'unit_price_file_id':currentUnitPriceId}).lean();
         this.getConnectionMap(currentMap,currentList);
 
         //切换后的单价文件所有的的组成物
-        let targetList = await this.db.find({'unit_price_file_id':changeUnitPriceId});
+        let targetList = await this.model.find({'unit_price_file_id':changeUnitPriceId}).lean();
         this.getConnectionMap(targetMap,targetList);
         for(let ckey in currentMap){
             if(targetMap[ckey]){//如果切换后的单价文件已经存在,则不用复
                 continue;
             }
-            if(gljMap[ckey] && gljMap[ckey].copy  == true){//在本项目中有用到
+            if(gljMap[ckey] || newFile == true){//在本项目中有用到 或者新建的文件
                 for(let ratio of  currentMap[ckey]){
                     delete ratio._id;  // 删除原有id信息
                     delete ratio.id;
-                    ratio.unit_price_file_id = changeUnitPriceId;
+                    ratio.unit_price_file_id = changeUnitPriceId; 
                     insertData.push(ratio);
                 }
             }
         }
-        return insertData.length > 0 ? this.add(insertData) : true;
+        return insertData.length > 0 ? await this.add(insertData) : true;
     }
     getConnectionMap(map,list){
         for(let l of list){

+ 25 - 15
modules/glj/models/unit_price_model.js

@@ -165,15 +165,16 @@ class UnitPriceModel extends BaseModel {
         let counterModel = new CounterModel();
         if (data instanceof Array) {
             // 如果是批量新增
-            for(let tmp in data) {
+           await this.setIDfromCounter(collectionName,data);
+            /* for(let tmp in data) {
                 data[tmp].id = await counterModel.getId(collectionName);
-            }
+            } */
         } else {
             data.id = await counterModel.getId(collectionName);
         }
 
         this.setScene('add');
-        return this.db.model.create(data);
+        return await this.db.model.create(data);
     }
 
     /**
@@ -432,7 +433,7 @@ class UnitPriceModel extends BaseModel {
             return result;
         }
 
-        let gljList = await gljListModel.find({'project_id':projectId});
+        let gljList = await gljListModel.find({'project_id':projectId}).lean();
         let gljMap = {};//用来记录glj的映射表,本项目有使用的工料机才需要copy过去
         for(let g of gljList){
             let g_index = this.getIndex(g,['code','name','specs','unit','type']);
@@ -468,10 +469,9 @@ class UnitPriceModel extends BaseModel {
                 delete tmp.id;
                 tmp.unit_price_file_id = changeUnitPriceId;
                 insertData.push(tmp);
-                gljMap[t_index].copy = true;//复制标记,用于组成物,材料计算等的复制
             }
         }
-        let uResult = insertData.length > 0 ? this.add(insertData) : true;
+        let uResult = insertData.length > 0 ? await this.add(insertData) : true;
         let mixRatioModel = new MixRatioModel();
         let mResult = await mixRatioModel.copyNotExist(currentUnitPriceId, changeUnitPriceId,gljMap);//复制组成物
         let cResult = await this.copyMaterialNotExist(currentUnitPriceId, changeUnitPriceId,gljMap);
@@ -479,19 +479,16 @@ class UnitPriceModel extends BaseModel {
         return uResult&&mResult&&cResult;
 
     }
-    async copyMaterialNotExist(currentUnitPriceId, changeUnitPriceId,gljMap){
-        await  this.copyCalcNotExist(currentUnitPriceId, changeUnitPriceId,gljMap,original_calc_model);//复制原价计算
-        await  this.copyCalcNotExist(currentUnitPriceId, changeUnitPriceId,gljMap,freight_calc_model);//复制运费计算
+    async copyMaterialNotExist(currentUnitPriceId, changeUnitPriceId,gljMap,newFile = false){
+        await  this.copyCalcNotExist(currentUnitPriceId, changeUnitPriceId,gljMap,original_calc_model,newFile);//复制原价计算
+        await  this.copyCalcNotExist(currentUnitPriceId, changeUnitPriceId,gljMap,freight_calc_model,newFile);//复制运费计算
         return true;
     }
 
-    async  copyCalcNotExist(currentUnitPriceId, changeUnitPriceId,gljMap,model){
+    async  copyCalcNotExist(currentUnitPriceId, changeUnitPriceId,gljMap,model,newFile){
         let currentMap = {},targetMap = {}, insertData = [];
         //取原单价文件所有的原价、运费计算计录
-        let currentList = await model.find({'unit_price_file_id':currentUnitPriceId});
-        // 过滤mongoose格式
-        currentList = JSON.stringify(currentList);
-        currentList = JSON.parse(currentList);
+        let currentList = await model.find({'unit_price_file_id':currentUnitPriceId}).lean();
         currentMap = _.groupBy(currentList,"connect_key");
 
         //切换后的单价文件所有的的组成物
@@ -501,11 +498,24 @@ class UnitPriceModel extends BaseModel {
             if(targetMap[ckey]){//如果切换后已经存在,则不用复制
                 continue;
             }
-            if(gljMap[ckey] && gljMap[ckey].copy  == true){//在本项目中有用到并且复制标记为true
+            if(gljMap[ckey] || newFile == true){//在本项目中有用到 或者 新建的文件
                 for(let c of  currentMap[ckey]){
                     delete c._id;  // 删除原有id信息
                     c.ID = uuidV1();
                     c.unit_price_file_id = changeUnitPriceId;
+                    //更改下挂的定额工料机与项目工料机的关联关系
+                    if(c.ration_gljs){
+                      for(let rg of c.ration_gljs){
+                        rg.ID = uuidV1();
+                      }
+                    }
+
+                    if(c.rations){
+                      for(let r of c.rations){
+                        r.ID = uuidV1(); 
+                      }
+                    }
+
                     insertData.push(c);
                 }
             }

+ 5 - 2
modules/main/facade/project_facade.js

@@ -20,6 +20,7 @@ let  projectsModel = mongoose.model('projects');
 let async_n = require("async");
 let _ = require('lodash');
 let ration_model = require('../models/ration');
+let optionModel = mongoose.model('options');
 let bill_model = require('../models/bills');
 let consts = require('../models/project_consts');
 let projectConsts = consts.projectConst;
@@ -295,7 +296,7 @@ async function getBudgetSummayDatas(projectIDs,userID,compilationID,overWriteUrl
         let prjTypeNames = [];
         let compilationScopes = [];
         let decimal = null;
-        let isProgressiveType = false;
+        let isProgressiveType = true;
         for(let ID of projectIDs){
             projects.push(await getBillsByProjectID(ID)) ;
         }
@@ -312,9 +313,11 @@ async function getBudgetSummayDatas(projectIDs,userID,compilationID,overWriteUrl
             prjTypeNames.push(projects[i].prjTypeName);
             compilationScopes.push(projects[i].compilationScope);
             decimal = await mergeProject(mp.roots,projects[i].roots);
-            if(projects[i].progressiveType == 0)  isProgressiveType = true;
         }
 
+        let options_setting = await optionModel.findOne({user_id: userID, compilation_id: compilationID}).lean();
+        if(options_setting && options_setting.options && options_setting.options.GENERALOPTS) isProgressiveType = options_setting.options.GENERALOPTS.progressiveType == 1 ?false:true; 
+
         let SummaryAuditDetail = getReportData(names,mp.roots,prjTypeNames,compilationScopes,decimal,isProgressiveType,mp.progressiveInterval,overWriteUrl);
         let parentProject = await projectsModel.findOne({ID:mp.ParentID});
         let result = {

+ 2 - 2
modules/main/facade/ration_facade.js

@@ -978,8 +978,8 @@ function getProjectGLJNewData(tmp,projectId,ext){
       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);
+  if(gljData.from == 'std' && ext && ext.priceField &&(tmp.priceProperty && tmp.priceProperty[ext.priceField]!= undefined && tmp.priceProperty[ext.priceField]!=null)){
+    const basePrice = scMathUtil.roundTo(tmp.priceProperty[ext.priceField],-6);
     gljData.base_price = basePrice;
     gljData.market_price = basePrice;
   }

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

@@ -173,7 +173,7 @@ module.exports = {
         try{
             let data = JSON.parse(req.body.data);
             let projectID = data.projectID;
-            let defaultSettingSc = await ProjectsData.defaultSettings(req.session.sessionUser.id, req.session.sessionCompilation._id, projectID);
+            let defaultSettingSc = await ProjectsData.defaultSettings(req.session.sessionUser.id, req.session.sessionCompilation, projectID);
             if(!defaultSettingSc){
                 throw '恢复失败';
             }
@@ -611,6 +611,16 @@ module.exports = {
     getInitialShareData: async function (req, res) {
         try {
             const { count, projectID } = JSON.parse(req.body.data);
+            // 分享建设项目,仅提供给专业版用户
+            const proShareProjType = [projType.folder, projType.project];
+            const project = await projectModel.findOne({ ID: projectID }, 'projType');
+            if (project && proShareProjType.indexOf(project.projType) >= 0) {
+                const isFree = userModelObj.isFreeFromSession(req.session.compilationVersion);
+                if (isFree) {
+                    callback(req, res, 0, 'success', { isFree });
+                    return;
+                }
+            }
             const userID = req.session.sessionUser.id;
             // 最近分享
             const recentUsers = await pm_facade.getRecentShareList(userID, count);

+ 24 - 0
modules/pm/facade/pm_facade.js

@@ -691,6 +691,18 @@ async function copyUnitPriceFile(newProjectID,rootProjectID,userID,originaluUnit
           for(let m of mList){
             m.unit_price_file_id = newFID;
             m.ID = uuidV1();
+            //自采自办的情况,项目工料机要跟着改变:
+            if(m.ration_gljs){
+              for(let rg of m.ration_gljs){
+                rg.ID = uuidV1();
+              }
+            }
+            if(m.rations){
+              for(let r of m.rations){
+                r.ID = uuidV1(); 
+              }
+            }
+
             rList.push(m);
           }
         }else{
@@ -1590,6 +1602,7 @@ async function importProject(data,req,updateData) {
             if (tenderOverrun) {
                 result.error = 1;
                 result.msg = `您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。`;
+                return result;
             }
             let [constructionProjectID,projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap] = await handleMainProjectDatas(mainData,updateData,req.session.sessionUser.id);
             result.constructionProjectID = constructionProjectID;
@@ -1812,6 +1825,17 @@ async function importUnitPriceFiles(mainData,projectIDMap,unitPriceFileIDMap,use
             delete o._id;
             o.unit_price_file_id = fileID;
             o.ID = uuidV1();
+            if(o.ration_gljs){
+              for(let rg of o.ration_gljs){
+                rg.ID = uuidV1();
+              }
+            }
+            if(o.rations){
+              for(let r of o.rations){
+                r.ID = uuidV1();
+              }
+            }
+
             nList.push(o)
           }
         }else{

+ 13 - 7
modules/pm/models/project_model.js

@@ -137,6 +137,10 @@ ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId
                 Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
             }
             else if (data.updateType === 'new') {
+                let overWrite;
+                if(compilation.overWriteUrl && compilation.overWriteUrl!=""){
+                    overWrite = require("../../.."+compilation.overWriteUrl);
+                }
                 data.updateData['userID'] = userId;
                 data.updateData['compilation'] = compilationId;
                 data.updateData['createDateTime'] = new Date();
@@ -157,10 +161,7 @@ ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId
                         user_id: userId,
                         root_project_id: data.updateData.property.rootProjectID
                     };
-                    if(compilation.overWriteUrl && compilation.overWriteUrl!=""){
-                        let overWrite = require("../../.."+compilation.overWriteUrl);
-                        if(overWrite.getDefalutAssistProductionFeeRate) insertData.assistProductionFeeRate = overWrite.getDefalutAssistProductionFeeRate();
-                    }
+                    if(overWrite && overWrite.getDefalutAssistProductionFeeRate) insertData.assistProductionFeeRate = overWrite.getDefalutAssistProductionFeeRate();
                     let addResult = await unitPriceFileModel.add(insertData);
                     if (!addResult) {
                         callback(1, '新增单价文件失败.', null);
@@ -174,7 +175,7 @@ ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId
                     //单价文件
                     data.updateData.property.unitPriceFile.id=parseInt(data.updateData.property.unitPriceFile.id);
                     //小数位数
-                    data.updateData.property.decimal = defaultDecimal;
+                    data.updateData.property.decimal = overWrite && overWrite.defaultDecimal || defaultDecimal;
                     //清单工程量精度
                     data.updateData.property.billsQuantityDecimal = billsQuantityDecimal;
                     //基本信息-挪到建设项目下,多个单位工程共用
@@ -818,7 +819,8 @@ ProjectsDAO.prototype.getBasicInfo = async function (projectID) {
 };
 
 //恢复默认系统设置
-ProjectsDAO.prototype.defaultSettings = async function (userID, compilationId, projectID) {
+ProjectsDAO.prototype.defaultSettings = async function (userID, compilation, projectID) {
+    const compilationId = compilation._id;
     let project = await Projects.findOne({ID: projectID});
     if(!project){
         return false;
@@ -832,7 +834,11 @@ ProjectsDAO.prototype.defaultSettings = async function (userID, compilationId, p
     //清单工程量精度
     cloneProperty.billsQuantityDecimal = billsQuantityDecimal;
     //小数位数
-    cloneProperty.decimal = defaultDecimal;
+    let overWrite;
+    if(compilation.overWriteUrl && compilation.overWriteUrl!=""){
+        overWrite = require("../../.."+compilation.overWriteUrl);
+    }
+    cloneProperty.decimal = overWrite && overWrite.defaultDecimal || defaultDecimal;
     //呈现选项
     cloneProperty.displaySetting = displaySetting;
     //列设置

+ 11 - 0
modules/users/controllers/user_controller.js

@@ -384,6 +384,17 @@ class UserController extends BaseController {
         }
     }
 
+    async isFree(req, res) {
+        try {
+            const sessionVersion = req.session.compilationVersion;
+            const userModel = new UserModel();
+            const isFree = userModel.isFreeFromSession(sessionVersion);
+            res.json({ error: 0, msg: 'success', data: { isFree } });
+        } catch (err) {
+            res.json({ error: 1, msg: String(err), data: null });
+        }
+    }
+
 }
 
 export default UserController;

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

@@ -364,6 +364,18 @@ class UserModel extends BaseModel {
         return free
     }
 
+    /**
+     * 从session中判断用户是否是免费版
+     * @param {String} sessionVersion
+     * @return {Boolean} 
+     */
+    isFreeFromSession(sessionVersion) {
+        if (!sessionVersion) {
+            return true;
+        }
+        return sessionVersion.indexOf('免费') >= 0;
+    }
+
     /*
     * 添加联系人,互相添加
     */

+ 1 - 0
modules/users/routes/user_route.js

@@ -22,6 +22,7 @@ module.exports = function (app) {
     router.post('/info', userController.init, userController.saveData);
     router.post('/getUserByMobile', userController.init, userController.getUserByMobile);
     router.post('/getUsers', userController.init, userController.getUsers);
+    router.post('/isFree', userController.init, userController.isFree);
 
     router.post('/getVersionInfo', userController.init, userController.getVersionInfo);
     router.post('/change/isSmsLogin', userController.init, userController.changeIsSmsLogin);

+ 11 - 0
public/scHintBox.html

@@ -172,6 +172,17 @@
         },
         unWaitBox: function () {
             $('#waitBox_form').modal('hide');
+        },
+        versionBox: function (caption) {
+            const title = '提示';
+            const btnType = hintBox.btnType.yesNo;
+            const doYes = () => {
+                $("#hintBox_form").modal('hide');
+                CommonHeader.getCategoryList();
+            };
+            const doNo = () => $("#hintBox_form").modal('hide');
+            const btnTextArr = ['联系客服', '关闭'];
+            this.infoBox(title, caption, btnType, doYes, doNo, btnTextArr);
         }
     };
 

+ 21 - 1
public/web/headerOpr.js

@@ -76,7 +76,27 @@ const CommonHeader = (function () {
             }
         }
     }
-    return {getCategoryList, addEventListener, banNavigatorContextMenu};
+
+    // 验证版本后进行的操作(针对一些没有通过后端的操作)
+    async function doAfterValidateVersion(proFunc, freeFunc) {
+        try {
+            const { isFree } = await ajaxPost('/user/isFree');
+            if (isFree && freeFunc) {
+                freeFunc();
+            } else if (!isFree && proFunc) {
+                proFunc();
+            }
+        } catch (err) {
+            console.log(err);
+        }
+    }
+
+    return {
+        getCategoryList,
+        addEventListener,
+        banNavigatorContextMenu,
+        doAfterValidateVersion
+    };
 })();
 
 CommonHeader.banNavigatorContextMenu();

+ 186 - 0
public/web/syntax-detection.js

@@ -0,0 +1,186 @@
+/** 
+ * 浏览器兼容性,特性检查
+ * 参考列表: http://kangax.github.io/compat-table/es6/
+ * 同内核的不同的浏览器需要处理,比如qq浏览器登陆成功后,ie浏览器登陆页面会自动跳转到项目管理页面。因此项目管理页面也需要检测
+*/
+function checkSyntax() {
+    'use strict';
+    try {
+        // 一些在词法、语法分析阶段就会报错的代码,必须要用window.Function或者eval包裹,否则直接报错且无法被捕获
+        // 不使用eval的原因:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/eval
+        if (typeof window.Function === 'undefined') {
+            return false;
+        }
+
+        // 声明
+        Function('let a = 1;')();
+        Function('const b = 1;')();
+        Function('class A {}')();
+
+        // 模板字符串
+        Function('var a = 1; var str = `${a}`;')();
+
+        // 对象简单表示
+        Function("var foo = 'foo'; var simple = { foo, t() {} };")();
+
+        // 对象属性表示
+        Function("var attr = 'a'; var obj = {[attr]: 1}")();
+
+        // 对象super
+        Function('var proto = { p: 1 }; var obj = { superF() { return super.p } }; Object.setPrototypeOf(obj, proto)')();
+
+        // 对象getter setter
+        Function('var tObj = { get a() {}, set a(v) {} }')();
+
+        // 扩展运算符
+        Function('var obj = { foo: 1 }; var obj1 = { ...obj }; var arr = [1]; var arr1 = [...arr];')();
+
+        // 解构
+        Function('var obj = { foo: 1 }; var { foo } = obj; var arr = [1]; var [one] = arr;')();
+
+        // 可选链 ?. es2020
+        //Function('var obj = { a: { b: 1 } }; obj?.a?.b')();
+
+        // of迭代
+        Function('for (var i of [1]) {}')();
+
+        // 默认参数
+        Function('function defaultParams(a = 1) {}')();
+
+        // rest参数
+        Function('function restParams(...args) {}')();
+
+        // arrow function
+        Function('var arrowF = () => {};')();
+
+        // generator function
+        Function('function* gen() {}')();
+
+        // async function es2017
+        Function('async function tAsync() {}')();
+
+        // Symbol Promise Set Map Proxy Reflect
+        if (typeof Symbol === 'undefined' ||
+            typeof Promise === 'undefined' ||
+            typeof Set === 'undefined' ||
+            typeof Map === 'undefined' ||
+            typeof Proxy === 'undefined' ||
+            typeof Reflect === 'undefined') {
+            return false;
+        }
+        // buffer相关
+        if (typeof ArrayBuffer === 'undefined' ||
+            typeof Uint8Array === 'undefined' ||
+            typeof DataView === 'undefined') {
+            return false;
+        }
+
+        // 部分字符串方法
+        var stringPrototypeFuncs = [
+            'includes',
+            'codePointAt',
+            'normalize',
+            'repeat',
+            //'matchAll', es2020
+            'padStart',
+            'padEnd',
+            //'trimStart', es2019
+            //'trimEnd'
+        ];
+        var stringStaticFuncs = [
+            'fromCodePoint',
+            'raw'
+        ];
+        for (var i = 0; i < stringPrototypeFuncs.length; i++) {
+            var f = stringPrototypeFuncs[i];
+            if (!String.prototype[f]) {
+                throw new SyntaxError('String.prototype ' + f);
+            }
+        }
+        for (var i = 0; i < stringStaticFuncs.length; i++) {
+            var f = stringStaticFuncs[i];
+            if (!String[f]) {
+                throw new SyntaxError('String ' + f);
+            }
+        }
+
+        // 部分数组方法
+        var arrayPrototypeFuncs = [
+            'find',
+            'findIndex',
+            'fill',
+            'keys',
+            'values',
+            'entries',
+            //'flat', // es2019
+            //'flatMap',
+            'includes'
+        ];
+        var arrayStaticFuncs = [
+            'from',
+            'of'
+        ];
+        for (var i = 0; i < arrayPrototypeFuncs.length; i++) {
+            var f = arrayPrototypeFuncs[i];
+            if (!Array.prototype[f]) {
+                throw new SyntaxError('Array.prototyp ' + f);
+            }
+        }
+        for (var i = 0; i < arrayStaticFuncs.length; i++) {
+            var f = arrayStaticFuncs[i];
+            if (!Array[f]) {
+                throw new SyntaxError('Array ' + f);
+            }
+        }
+
+        // 对象部分方法
+        var objectStaticFuncs = [
+            'is',
+            'assign',
+            'getOwnPropertyDescriptors',
+            'setPrototypeOf',
+            'values',
+            'entries',
+            //'fromEntries' // es2020
+        ];
+        for (var i = 0; i < objectStaticFuncs.length; i++) {
+            var f = objectStaticFuncs[i];
+            if (!Object[f]) {
+                throw new SyntaxError('Object ' + f);
+            }
+        }
+
+    } catch (err) {
+        console.log(err);
+        return false;
+    }
+    return true;
+}
+
+function showBrowserTip() {
+    var html = '<div class="modal fade" id="browser" data-backdrop="static">' +
+        '<div class="modal-dialog modal-lg" role="document">' +
+        '<div class="modal-content">' +
+            '<div class="modal-body">' +
+                '<h5>浏览器版本过低,可能会有安全风险;</h5>' +
+                '<h5>请更新 「浏览器」 或者 使用 「纵横Z+造价工作平台」 登录。</h5>' +
+                '<div class="row my-4">' +
+                    '<div class="col-6">' +
+                        '<div class="text-center"><a href="https://www.microsoft.com/zh-cn/edge" class="btn btn-primary" target="_blank">下载 Microsoft Edge</a></div>' +
+                    '</div>' +
+                    '<div class="col-6">' +
+                        '<div class="text-center"><a href="https://smartcost.com.cn/downloadzplus" class="btn btn-primary" target="_blank">下载 纵横Z+造价工作平台</a></div>' +
+                    '</div></div></div></div></div></div>';
+    $('body').append(html);
+    $('#browser').modal('show');
+}
+
+$(document).ready(function () {
+    // 浏览器兼容性
+    var isCompat = checkSyntax();
+    if (!isCompat) {
+        $('#inputEmail').blur();
+        showBrowserTip();
+        return false;
+    }
+});

+ 9 - 0
web/building_saas/css/custom.css

@@ -448,4 +448,13 @@ input.text-right{
 .material_link.active{
   border:2px solid #ff6501 !important;
   border-right: 1px solid #fff !important;
+}
+.table-sc th {
+    font-weight: normal;
+}
+
+.compilation-content {
+    left: 50% !important;
+    transform: translateX(-50%) !important;
+    width: 123% !important;
 }

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

@@ -178,6 +178,8 @@ var feeRateObject={
                     sheet.getCell(row, 0).cellType(treeType);
                     visibleMap[data[row].ID] = treeType.collapsed;
                     feeRateObject.setRowVisible(data,row,visibleMap,sheet);
+                } else if (col === 1 && commonUtil.isDef(data[row].originalRate) && data[row].originalRate !== +val) {
+                    sheet.getCell(row, col).foreColor('red');
                 }
             }
         }
@@ -534,11 +536,14 @@ var feeRateObject={
             feeRate.updateFeeRatesByIDs(updateDatas,async function () {
                 let feerateInfo = [];
                 for(let u of updateDatas){
-                    let row = _.findIndex(me.mainFeeRateData,{'ID':u.rateID})
+                    let row = _.findIndex(me.mainFeeRateData,{'ID':u.rateID});
+                    let rateItem = me.mainFeeRateData[row];
                     for(let key in u.doc){
                         let col = _.findIndex(me.mainFeeRateSetting.header,{'dataCode':key});
                         me.mainFeeRateSheet.setValue(row, col, u.doc[key]);
                         if(key == 'rate'){
+                            const foreColor = commonUtil.isDef(rateItem.originalRate) && rateItem.originalRate !== +u.doc[key] ? 'red' : 'black';
+                            me.mainFeeRateSheet.getCell(row, col).foreColor(foreColor);
                             feerateInfo.push({rateID:u.rateID,value:u.doc[key]});
                         }
                     }

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

@@ -1315,7 +1315,7 @@ var projectObj = {
                     }*/
                 },
                 "insertGLJ": {
-                  name: "插入人材机",
+                  name: "插入工料机",
                   icon: 'fa-sign-in',
                   disabled: function () {
                       if (projectReadOnly) {

+ 5 - 2
web/building_saas/main/js/views/quantity_edit_view.js

@@ -189,13 +189,16 @@ let quantityEditObj = {
     },
     updateQuantityEXP:function (value,quantityEXP,node) {
         let quantity_detail = projectObj.project.quantity_detail;
-        quantity_detail.cleanQuantityDetail(node,true);
+        if(node.data.hasOwnProperty('isFromDetail')&&node.data.isFromDetail==1) quantity_detail.cleanQuantityDetail(node,true);
         if(node.sourceType === ModuleNames.bills){
-            if(quantityEXP!=""&& node.data.unit == "公路公里"){
+            if(quantityEXP!=""&& node.data.unit == "公路公里" && quantityEditObj.infoShowing!==true){
+              quantityEditObj.infoShowing = true
                 hintBox.infoBox('操作确认', '是否将所有单位等于"公路公里"的项目的工程量都设为该值?', 2, function () {
                     quantity_detail.updateBillQuantity(value,node,quantityEXP,quantityEXP,true);
+                    quantityEditObj.infoShowing = false
                 }, function () {
                     quantity_detail.updateBillQuantity(value,node,quantityEXP,quantityEXP);
+                    quantityEditObj.infoShowing = false
                 },['是','否']);
                 return;
             }

+ 4 - 3
web/building_saas/pm/html/project-management.html

@@ -155,7 +155,7 @@
                     <thead>
                         <th style="width: 25px;" class="text-center"></th>
                         <th style="width: 330px;" class="text-center">名称</th>
-                        <th style="width:40px;" class="text-center">使用</th>
+                        <th style="width: 55px;" class="text-center">使用</th>
                     </thead>
                     <tbody>
                     </tbody>
@@ -167,7 +167,7 @@
                     <thead>
                         <th style="width: 25px;" class="text-center"></th>
                         <th style="width: 330px;" class="text-center">名称</th>
-                        <th style="width:40px;" class="text-center">使用</th>
+                        <th style="width: 55px;" class="text-center">使用</th>
                     </thead>
                     <tbody>
                     </tbody>
@@ -329,7 +329,7 @@
                         </div>
                     </div>
                     <!--养护中计价方式为项目类别,预算用清单计价原字段,工程量清单用定额计价原字段-->
-                    <div class="form-group row">
+                    <div class="form-group row" id="val-type-group">
                         <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">项目类别</label>
                         <div class="col">
                             <div class="custom-control custom-radio custom-control-inline">
@@ -703,6 +703,7 @@
 <script src="/lib/qiniu/qiniu.min.js"></script>
 <script>GC.Spread.Sheets.LicenseKey = '<%- LicenseKey %>';</script>
 <!-- inject:js -->
+<script type="text/javascript" src="/public/web/syntax-detection.js"></script>
 <script src="/web/building_saas/js/global.js"></script>
 <script src="/public/web/uuid.js"></script>
 <script src="/public/web/PerfectLoad.js"></script>

+ 8 - 0
web/building_saas/report/js/rpt_main.js

@@ -937,6 +937,10 @@ let rptControlObj = {
         }
     },
     checkAndGetExcel: function () {
+        if (zTreeOprObj.isFreeUser) {
+            hintBox.versionBox('对不起,您当前使用的是免费公用版,不提供导出、打印报表功能,请联系我们的客服人员。');
+            return;
+        }
         if (zTreeOprObj.treeObj) {
             let chkNodes = zTreeOprObj.treeObj.getCheckedNodes(true);
             if (chkNodes.length > 0) {
@@ -1026,6 +1030,10 @@ let rptControlObj = {
     },
     getPDFPre: function () {
         let me = rptControlObj;
+        if (zTreeOprObj.isFreeUser) {
+            hintBox.versionBox('对不起,您当前使用的是免费公用版,不提供导出、打印报表功能,请联系我们的客服人员。');
+            return;
+        }
         if (rptTplObj.pdfFont['SmartSimsun'].length === 2) {
             $.bootstrapLoading.start();
             me.getPDFEx();

+ 4 - 0
web/building_saas/report/js/rpt_print.js

@@ -4,6 +4,10 @@
 
 let rptPrintHelper = {
     preview: function () {
+        if (zTreeOprObj.isFreeUser) {
+            hintBox.versionBox('对不起,您当前使用的是免费公用版,不提供导出、打印报表功能,请联系我们的客服人员。');
+            return;
+        }
         if (zTreeOprObj.checkedRptTplNodes && zTreeOprObj.checkedRptTplNodes.length > 0) {
             let refRptTplIds = [], refBillSumPrjsIds = [], refGljSumPrjsIds = [];
             rptControlObj.getTplIdsCommon(refRptTplIds, refBillSumPrjsIds, refGljSumPrjsIds, null, null, null);

+ 12 - 8
web/common/components/share/index.js

@@ -319,17 +319,21 @@ const SHARE_TO = (() => {
             $('#share-phone').val('');
             initSearchResultView();
             $('#share-hint').text('');
-            const { sharedUsers, recentUsers, contacts } = await getInitalData(projectID);
-            curSharedUsers = sharedUsers;
-            initSharedView(sharedUsers);
-            initRecentView(recentUsers);
-            initContactsView(contacts);
-            $.bootstrapLoading.end();
-            setTimeout(() => $('#sharePhone').focus(), 200);
-            $('#share').modal('show');
+            const { isFree, sharedUsers, recentUsers, contacts } = await getInitalData(projectID);
+            if (isFree) {
+                hintBox.versionBox('此功能仅在专业版中提供,免费公用版可选择单个分段进行分享。');
+            } else {
+                curSharedUsers = sharedUsers;
+                initSharedView(sharedUsers);
+                initRecentView(recentUsers);
+                initContactsView(contacts);
+                setTimeout(() => $('#share-phone').focus(), 200);
+                $('#share').modal('show');
+            }
         } catch (err) {
             console.log(err);
             alert(err);
+        } finally {
             $.bootstrapLoading.end();
         }
     }

+ 2 - 2
web/common/html/header.html

@@ -50,7 +50,7 @@
                     <a class="dropdown-item" href="#">重庆市2008定额说明</a>-->
                     <!--<a class="dropdown-item" href="/web/common/html/pdfViewer.html?type=userGuide" target="_blank">用户手册</a>
                     <a class="dropdown-item" href="/web/common/html/pdfViewer.html?type=upgradeGuide" target="_blank">升级说明</a>-->
-                    <a class="dropdown-item" href="/public/share/userGuide/userGuide.pdf" target="_blank">用户手册</a>
+                    <a class="dropdown-item" href="http://doc.zhzdwd.com/docs/yh_yhsc/yh_yhsc-1bup3dm7iacsg" target="_blank">用户手册</a>
                     <a class="dropdown-item" href="/public/share/upgradeGuide/upgradeGuide.pdf" target="_blank">升级说明</a>
                     <a class="dropdown-item" href="https://smartcost.com.cn/" target="_blank">纵横官网</a>
                     <!--  <a class="dropdown-item" href="#">动画教程</a>-->
@@ -176,7 +176,7 @@
 </div>
 <!--激活产品 & 售后服务 & 联系客服-->
 <!--办事处客服列表-->
-<div class="modal fade z-index-3000" id="activ" data-backdrop="static" style="display: none;" aria-hidden="true">
+<div class="modal fade z-index-3000" id="activ" data-backdrop="static" style="display: none; overflow: auto;" aria-hidden="true">
     <div class="modal-dialog modal-lg" role="document">
         <div class="modal-content">
             <div class="modal-header">

+ 30 - 2
web/over_write/js/nongcun_2020.js

@@ -46,7 +46,7 @@
             const selected = projectObj.project.mainTree.selected;
             const targetNode = getTargetNode(selected);
             if (!targetNode) {
-                return { errMsg: '当前位置不可添加养护工程量清单,请选中建筑安装工程费,再添加工程量清单。' };
+                return { errMsg: '当前位置不可添加养护工程量清单,请选中建筑安装工程费,再添加工程量清单。' };
             }
             return { parent: targetNode, mainTreeFragment: targetNode.children || [] };
 
@@ -100,10 +100,38 @@ if (typeof baseFigureTemplate !== 'undefined') {
     baseFigureTemplate.boq = {};
 }
 
+// 项目管理界面
+if (typeof projTreeObj !== 'undefined') {
+    // 新建分段,隐藏项目类别、养护类别、费用标准
+    $('#val-type-group').hide();
+    $('#tender-engineering-group').hide();
+    $('#tender-feeStandard-group').hide();
+
+    // 项目管理隐藏项目类别列
+    projTreeObj.setting.header = [
+        {name: '工程列表', dataCode: 'name', width: 300, vAlign: 'center', hAlign: 'left'},
+        {name: '总造价', dataCode: 'totalCost', width: 100, vAlign: 'center', hAlign: 'right', formatter: '0'},
+        {name: '单价文件', dataCode: 'unitPriceFile', width: 140, vAlign: 'center', hAlign: 'left'},
+        {name: '费率文件', dataCode: 'feeRateFile', width: 140, vAlign: 'center', hAlign: 'left'},
+        {name: '创建日期', dataCode: 'createDateTime', width: 100, vAlign: 'center', hAlign: 'center'}
+    ];
+}
 
 if (typeof module !== 'undefined') {
+    // 农村公路养护(2020),项目属性-小数位数,清单和定额的合价应默认为2。(其他编办保持不变)
+    const defaultDecimal = {
+        bills: { unitPrice: 2, totalPrice: 2 },
+        ration: { quantity: 3, unitPrice: 2, totalPrice: 2 },
+        glj: { quantity: 3, unitPriceHasMix: 2, unitPrice: 3 },
+        feeRate: 3,
+        quantity_detail: 4,
+        material:5,
+        process: 6
+    };
+
     module.exports = {
         progression: [],
-        deficiency: {}
+        deficiency: {},
+        defaultDecimal
     };
 }

+ 1 - 1
web/users/html/login-sms.html

@@ -49,7 +49,7 @@
     <!--弹出信息-->
     <div class="modal fade" id="ver" data-backdrop="static">
         <div class="modal-dialog modal-lg" role="document">
-            <div class="modal-content">
+            <div class="modal-content compilation-content">
                 <div class="modal-header">
                     <h5 class="modal-title">选择费用定额</h5>
                     <p class="m-0 text-warning"><i class="fa fa-exclamation-triangle"></i> <b>登录设置</b> 中可以修改您的登录习惯。</p>

+ 8 - 6
web/users/html/login.html

@@ -8,6 +8,7 @@
     <!-- inject:css -->
     <link rel="stylesheet" href="/lib/bootstrap/css/bootstrap.min.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">
     <!-- endinject -->
     <link rel="shortcut icon" href="/web/building_saas/css/favicon.ico">
@@ -64,7 +65,7 @@
     <!--弹出信息-->
     <div class="modal fade" id="ver" data-backdrop="static" style="top: 25%;">
         <div class="modal-dialog modal-lg" role="document">
-            <div class="modal-content">
+            <div class="modal-content compilation-content">
                 <div class="modal-header">
                     <h5 class="modal-title">选择编制办法</h5>
                     <p class="m-0 text-warning"><i class="fa fa-exclamation-triangle"></i> <b>登录设置</b> 中可以修改您的登录习惯。</p>
@@ -174,11 +175,12 @@
     <!-- JS. -->
     <!-- inject:js -->
     <script type="text/javascript" src="/public/web/scMathUtil.js"></script>
-    <script src="/lib/jquery/jquery-3.2.1.min.js"></script>
-    <script src="/public/web/url_util.js"></script>
-    <script src="/lib/popper/popper.min.js"></script>
-    <script src="/lib/bootstrap/bootstrap.min.js"></script>
-    <script src="/web/building_saas/js/global.js"></script>
+    <script type="text/javascript" src="/lib/jquery/jquery-3.2.1.min.js"></script>
+    <script type="text/javascript" src="/public/web/url_util.js"></script>
+    <script type="text/javascript" src="/lib/popper/popper.min.js"></script>
+    <script type="text/javascript" src="/lib/bootstrap/bootstrap.min.js"></script>
+    <script type="text/javascript" src="/public/web/syntax-detection.js"></script>
+    <script type="text/javascript" src="/web/building_saas/js/global.js"></script>
     <script type="text/javascript" src="/web/users/js/gt.js"></script>
     <script type="text/javascript" src="/web/users/js/login.js"></script>
     <!-- endinject -->

+ 2 - 3
web/users/js/login.js

@@ -482,12 +482,11 @@ function cleanError() {
 function setVersion(versionData) {
     let html = '';
     for (let version of versionData) {
-        let description = version.description ? version.description : '介绍内容';
-        let tmpHtml = '<div class="col-sm-6 mb-3">' +
+        //let description = version.description ? version.description : '介绍内容';
+        let tmpHtml = '<div class="col-sm-4 mb-3">' +
             '<div class="card card-block">' +
             '<div class="card-body">' +
             '<h3 class="card-title">'+ version.name +'</h3>' +
-            '<p class="card-text">' + description + '</p>' +
             '<a class="btn btn-primary" href="/boot/'+ version._id.toString() +'">开始使用</a>' +
             '</div>' +
             '</div>' +