소스 검색

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

TonyKang 5 년 전
부모
커밋
6d52b307b7
54개의 변경된 파일777개의 추가작업 그리고 262개의 파일을 삭제
  1. 3 0
      importserver.js
  2. 1 0
      modules/all_models/ration.js
  3. 1 0
      modules/all_models/stdRation_ration.js
  4. 25 0
      modules/all_models/system_setting.js
  5. 23 0
      modules/import/controllers/import_controller.js
  6. 3 0
      modules/import/routes/import_route.js
  7. 21 8
      modules/main/controllers/project_controller.js
  8. 26 3
      modules/main/facade/project_facade.js
  9. 2 0
      modules/main/facade/ration_facade.js
  10. 53 0
      modules/main/middleware/system_setting.js
  11. 29 0
      modules/main/models/project.js
  12. 1 0
      modules/main/routes/project_route.js
  13. 3 2
      modules/main/routes/ration_route.js
  14. 18 2
      modules/pm/controllers/pm_controller.js
  15. 84 2
      modules/pm/facade/pm_facade.js
  16. 3 2
      modules/pm/routes/pm_route.js
  17. 8 0
      modules/users/controllers/login_controller.js
  18. 3 3
      modules/users/controllers/user_controller.js
  19. 1 0
      public/scHintBox.html
  20. 7 6
      public/web/common_ajax.js
  21. 6 6
      public/web/ration_glj_units.js
  22. 13 1
      public/web/sheet/sheet_common.js
  23. 7 12
      public/web/tree_sheet/tree_sheet_helper.js
  24. 63 0
      public/web/upload_cdn.js
  25. 18 0
      web/building_saas/css/custom.css
  26. 5 2
      web/building_saas/main/html/main.html
  27. 3 3
      web/building_saas/main/html/tender_price.html
  28. 18 4
      web/building_saas/main/js/models/calc_program.js
  29. 82 17
      web/building_saas/main/js/models/exportSEIInterface.js
  30. 54 20
      web/building_saas/main/js/models/exportStdInterfaceBase.js
  31. 0 2
      web/building_saas/main/js/models/overHeight.js
  32. 9 8
      web/building_saas/main/js/models/project.js
  33. 2 2
      web/building_saas/main/js/models/ration.js
  34. 2 6
      web/building_saas/main/js/views/calc_base_view.js
  35. 39 16
      web/building_saas/main/js/views/export_view.js
  36. 1 0
      web/building_saas/main/js/views/fee_rate_view.js
  37. 5 1
      web/building_saas/main/js/views/index_view.js
  38. 9 3
      web/building_saas/main/js/views/main_tree_col.js
  39. 1 1
      web/building_saas/main/js/views/project_property_basicInfo.js
  40. 1 1
      web/building_saas/main/js/views/project_property_bills_quantity_decimal.js
  41. 1 1
      web/building_saas/main/js/views/project_property_projFeature.js
  42. 7 14
      web/building_saas/main/js/views/project_view.js
  43. 10 9
      web/building_saas/main/js/views/std_ration_lib.js
  44. 9 2
      web/building_saas/main/js/views/tender_price_view.js
  45. 6 6
      web/building_saas/pm/html/project-management.html
  46. 3 2
      web/building_saas/pm/js/pm_ajax.js
  47. 38 51
      web/building_saas/pm/js/pm_import.js
  48. 38 32
      web/building_saas/pm/js/pm_newMain.js
  49. 2 2
      web/building_saas/pm/js/pm_share.js
  50. 5 5
      web/common/html/header.html
  51. 1 1
      web/users/html/login-sms.html
  52. 1 1
      web/users/html/login-ver.html
  53. 1 1
      web/users/html/login.html
  54. 2 2
      web/users/html/user-buy.html

+ 3 - 0
importserver.js

@@ -43,6 +43,9 @@ app.use(bodyParser.json({limit: '100mb'}));
 fileUtils.getGlobbedFiles('./modules/import/routes/*.js').forEach(function(modelPath) {
     require(path.resolve(modelPath))(app);
 });
+//config.setupCache();
+let cfgCacheUtil = require("./config/cacheCfg");
+cfgCacheUtil.setupDftCache();
 
 //app.use(express.static(_rootDir+"/web"));
 //app.use(express.static(_rootDir+"/lib"));

+ 1 - 0
modules/all_models/ration.js

@@ -75,6 +75,7 @@ let rationSchema = new Schema({
     referenceRationID:String,//如果是通过模板关联子目生成的定额,这里记录对应的主定额ID
     // 工作内容 (选择自清单)
     jobContentText: String,
+    manageFeeRate:String,//管理费率
 
     //工料机特有属性
     projectGLJID:Number,  //项目工料机ID

+ 1 - 0
modules/all_models/stdRation_ration.js

@@ -46,6 +46,7 @@ const rationItemSchema = new Schema({
     feeType: Number,
     jobContent: String,
     annotation: String,
+    manageFeeRate:String,//管理费费率
     rationGljList: [rationGljItemSchema],
     rationCoeList: Array,
     rationAssList: [rationAssItemSchema],

+ 25 - 0
modules/all_models/system_setting.js

@@ -0,0 +1,25 @@
+/**
+ * Created by zhang on 2020/1/2.
+ */
+let mongoose = require("mongoose");
+
+let Schema = mongoose.Schema;
+let collectionName = 'system_setting';
+let modelSchema = {
+    // ID
+    ID: {
+        type: String,
+        index: true
+    },
+    // 专业用户
+    professional: {
+        project: Number,
+        ration:Number
+    },
+    // 免费用户
+    normal: {
+        project: Number,
+        ration:Number
+    },
+};
+mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));

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

@@ -6,6 +6,8 @@
  */
 let logger = require("../../../logs/log_helper").logger;
 let pm_facade = require('../../pm/facade/pm_facade');
+const Project = require('../../main/models/project');
+const project_facade = require('../../main/facade/project_facade.js');
 let controller = {
     importProject:async function (req){
         let data = req.body;
@@ -26,6 +28,27 @@ let controller = {
         let data = JSON.parse(req.body.dataString);
         result.data = await pm_facade.copyProject(req.body.userID,req.body.compilationID,data);
         return result
+    },
+    async importInterface (req) {
+        const result = {
+            error: 0
+        };
+        result.data = await pm_facade.importInterface(req.body.key, req.body.session);
+        return result;
+    },
+    async getDataForInterface (req) {
+        const result = {
+            error: 0
+        };
+        result.data = await Project.getDataSync(req.body.project_id);
+        return result;
+    },
+    async loadSEIProjectData (req) {
+        const result = {
+            error: 0
+        };
+        result.data = await project_facade.loadSEIProjectData(req.body.projectID);
+        return result;
     }
 };
 

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

@@ -9,6 +9,9 @@ module.exports = function (app) {
     importRouter.post('/importProject',importController.action);
     importRouter.post('/exportProject',importController.action);
     importRouter.post('/copyProject',importController.action);
+    importRouter.post('/importInterface',importController.action);
+    importRouter.post('/getDataForInterface',importController.action);
+    importRouter.post('/loadSEIProjectData',importController.action);
     importRouter.get('/test',function (req,res) {
         res.json("hello word");
     })

+ 21 - 8
modules/main/controllers/project_controller.js

@@ -4,6 +4,7 @@
 var Project = require('../models/project');
 let logger = require('../../../logs/log_helper').logger;
 let project_facade = require("../facade/project_facade");
+const redirectToImportServer = require('../../pm/controllers/pm_controller').redirectToImportServer;
 
 //统一回调函数
 var callback = function(req, res, err, message, data){
@@ -47,6 +48,20 @@ module.exports = {
             }
         });
     },
+    getDataForInterface: async function (req, res) {
+        const data = JSON.parse(req.body.data);
+        let result={
+            error:0
+        };
+        try {
+            result = await redirectToImportServer(data,"getDataForInterface",req);
+        } catch (err) {
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
+    },
     markUpdateProject:async function (req,res) {
         let result={
             error:0
@@ -179,16 +194,14 @@ module.exports = {
         res.json(result);
     },
     loadSEIProjectData:async function(req,res){
-        let data = JSON.parse(req.body.data);
+        const data = JSON.parse(req.body.data);
         let result={
-            error: 0,
-            data: null
+            error:0
         };
-        try{
-            result.data = await project_facade.loadSEIProjectData(data.projectID);
-        }
-        catch(err){
-            logger.err(err);
+        try {
+            result = await redirectToImportServer(data,"loadSEIProjectData",req);
+        } catch (err) {
+            console.log(err);
             result.error=1;
             result.message = err.message;
         }

+ 26 - 3
modules/main/facade/project_facade.js

@@ -411,7 +411,7 @@ function saveProperty(data, callback){
             logger.err(pn + ' save error: ' + err);
             callback(err, null)
         } else {
-            logger.info(pn + ' saved.');
+            // logger.info(pn + ' saved.');
             callback('', null);
         }}
     );
@@ -495,11 +495,34 @@ async function loadSEIProjectData(projectID) {
     };
     return{bills:bills,rations:rations,ration_gljs:ration_gljs,projectGLJs:projectGLJs}
 }
+//从建设项目中取工程信息默认值
+async function setDefaultInfo(rootProjectID,engineerInfos){
+    let rootProject = await projectsModel.findOne({ID:rootProjectID}).lean();
+    if(rootProject){
+        let baseInfo = _.find(rootProject.property.basicInformation,{key:"basicInfo"});
+        if(!baseInfo) return;
+        let baseMap = {};
+        let attrs = ["工程地点","开工日期","竣工日期","编制日期"];
+        for(let item of baseInfo.items){
+            baseMap[item.dispName] = item;
+        }
+        for(let en of engineerInfos){
+            if(attrs.indexOf(en.dispName) != -1){
+                let key = en.dispName;
+                if(key == "工程地点") key = "工程所在地";
+                en.value = baseMap[key].value;
+            }
+        }
+    }
+}
 
 async function setSEILibData(property){
     if(property.engineerInfoLibID){//工程信息指标
-        let engineerInfo = await engineerInfoLib.findOne({'ID':property.engineerInfoLibID});
-        if(engineerInfo) property.engineerInfos = engineerInfo.info;
+        let engineerInfo = await engineerInfoLib.findOne({'ID':property.engineerInfoLibID}).lean();
+        if(engineerInfo){
+            property.engineerInfos = engineerInfo.info;
+            await setDefaultInfo(property.rootProjectID,property.engineerInfos);
+        }
     }
     if(property.engineerFeatureLibID){//工程特征指标
         let engineerFeature = await engineerFeatureLib.findOne({'ID':property.engineerFeatureLibID});

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

@@ -155,6 +155,7 @@ async function insertNewRation(newData,defaultLibID,std,calQuantity) {//插入
         }else {
             newData.programID = std.feeType;
         }
+        newData.manageFeeRate = std.manageFeeRate;
         newData.rationAssList =  createRationAss(std);
         // calculate ration Quantity
     }
@@ -544,6 +545,7 @@ async function  updateRation(std,defaultLibID,rationID,billsItemID,projectID,cal
         ration.stdID = std.ID;
     }
     ration.content = std.jobContent;
+    ration.manageFeeRate = std.manageFeeRate;
     ration.adjustState = '';
     ration.isFromDetail=0;
     ration.isSubcontract=false;

+ 53 - 0
modules/main/middleware/system_setting.js

@@ -0,0 +1,53 @@
+/**
+ * Created by zhang on 2020/1/8.
+ */
+
+module.exports={
+    getSystemSetting,
+    rationNumberChecking:rationNumberChecking,
+    tenderNumberChecking
+};
+
+let mongoose = require("mongoose");
+let rationModel = mongoose.model("ration");
+const systemSettingModel = mongoose.model('system_setting');
+const pmFacade = require('../../pm/facade/pm_facade');
+
+// 获取系统设置,这个系统设置正常情况下有存在session中
+async function getSystemSetting() {
+    return await systemSettingModel.findOne({}).lean();
+}
+
+async function rationNumberChecking(req, res, next) {
+    if(req.session.systemSetting){
+        let type = req.session.compilationVersion.indexOf("免费") == -1?"professional":"normal";
+        let data = req.body.data;
+        if(typeof data === 'object'){
+            data = JSON.stringify(data);
+        }
+        data = JSON.parse(data);
+        let projectID = data.projectID;
+        let no = await rationModel.find({projectID:projectID}).count();
+        if(no >= req.session.systemSetting[type].ration){
+            let  result = {error:1,message:"您套用定额个数超限,请联系我们的客服人员。"};
+            return  res.json(result);
+        }
+    }
+    next();
+}
+
+async function tenderNumberChecking(req, res, next) {
+    const data = JSON.parse(req.body.data);
+    const tenderCount = data.tenderCount;
+    if (tenderCount) {
+        const tenderOverrun = await pmFacade.isTenderOverrun(tenderCount, req.session);
+        if (tenderOverrun) {
+            return res.json({
+                error: 1,
+                message: '您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。'
+            });
+        }
+    }
+    next();
+
+}

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

@@ -131,6 +131,35 @@ Project.prototype.getData = function(projectID, callback){
     });
 };
 
+Project.prototype.getDataSync = function(projectID){
+    return new Promise((resolve, reject) => {
+        const functions = [];
+        const firstTime = +new Date();
+        for (const itemName in moduleMap){
+            functions.push((function(itemName){
+                return function (cb) {
+                    const startTime = +new Date();
+                    moduleMap[itemName].getData(projectID, function(err, moduleName, data){
+                        const endTime = +new Date();
+                        console.log(moduleName+'---------------'+(endTime - startTime));
+                        cb(err, {moduleName: moduleName, data: data})
+                    }, true); // true 返回调价后的数据
+                }
+            })(itemName))
+        }
+    
+        asyncTool.parallel(functions, function(err, results) {
+            if (!err) {
+                const lastTime = +new Date();
+                console.log('最后加载时间---------------'+(lastTime - firstTime));
+                resolve(results);
+            } else {
+                reject(err);
+            }
+        });
+    });
+};
+
 Project.prototype.getFilterData = function (projectID, filter, callback) {
     let functions = [];
     let getModuleData = function (moduleName) {

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

@@ -9,6 +9,7 @@ module.exports = function (app) {
 
     projectRouter.post('/save', projectController.save);
     projectRouter.post('/getData', projectController.getData);
+    projectRouter.post('/getDataForInterface', projectController.getDataForInterface);
     projectRouter.post('/markUpdateProject', projectController.markUpdateProject);
     projectRouter.post('/removeProjectMark', projectController.removeProjectMark);
     projectRouter.post('/updateNodes', projectController.updateNodes);

+ 3 - 2
modules/main/routes/ration_route.js

@@ -2,6 +2,7 @@
  * Created by jimiz on 2017/4/7.
  */
 let express = require('express');
+let ss_middleware = require("../middleware/system_setting");
 
 module.exports = function (app) {
     let rationRouter = express.Router();
@@ -11,8 +12,8 @@ module.exports = function (app) {
     rationRouter.post('/allocIDs', rationController.allocIDs);
     rationRouter.post('/insertGLJAsRation', rationController.action);
     rationRouter.post('/replaceRations', rationController.action);
-    rationRouter.post('/addNewRation', rationController.action);
-    rationRouter.post('/addMultiRation', rationController.action);
+    rationRouter.post('/addNewRation',ss_middleware.rationNumberChecking,rationController.action);
+    rationRouter.post('/addMultiRation',ss_middleware.rationNumberChecking, rationController.action);
     rationRouter.post('/getSameSectionRations', rationController.action);
     rationRouter.post('/getDefaultProgramID', rationController.action);
     rationRouter.post('/applyTemplate', rationController.action);

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

@@ -820,7 +820,7 @@ module.exports = {
             callback(req, res, 1, err, null);
         }
     },
-    importInterface: async function(req, res) {
+    /* importInterface: async function(req, res) {
         logger.info(`${req.ip} importInterface`);
         const uploadOption = {
             uploadDir: './public'
@@ -851,7 +851,23 @@ module.exports = {
                 callback(req, res, 1, err, null);
             }
         });
-    }
+    } */
+    importInterface: async function (req, res) {
+        const data = JSON.parse(req.body.data);
+        let result={
+            error:0
+        };
+        try {
+            data.session = req.session;
+            result = await redirectToImportServer(data,"importInterface",req);
+        } catch (err) {
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
+    },
+    redirectToImportServer: redirectToImportServer
 };
 
 async function redirectToImportServer(data,action,req) {

+ 84 - 2
modules/pm/facade/pm_facade.js

@@ -35,7 +35,9 @@ module.exports={
     initOverHeightItems: initOverHeightItems,
     uploadToken:uploadToken,
     downLoadProjectFile:downLoadProjectFile,
-    importProcessChecking:importProcessChecking
+    importProcessChecking:importProcessChecking,
+    importInterface,
+    isTenderOverrun
 };
 
 
@@ -108,6 +110,7 @@ let qiniu = require("qiniu");
 let fs = require("fs");
 let path = require("path");
 let request = require("request");
+const systemSettingMiddleware = require('../../main/middleware/system_setting');
 
 let qiniu_config = {
     "AccessKey": "_gR1ed4vi1vT2G2YITGSf4_H0fJu_nRS9Tzk3T4z",
@@ -1345,7 +1348,7 @@ async function getProjectPlaceholder(data) {
 }
 
 /*
-* 项目详细数据都导入完成了,再生成项目数据(项目管理界面数据)
+* 接口导入 项目详细数据都导入完成了,再生成项目数据(项目管理界面数据)
 * */
 async function importProject(importObj, userID, compilationID) {
     let toInsertProjects = [importObj];  //待新增项目数据
@@ -1814,6 +1817,60 @@ async function importProcessChecking(data){
 
 }
 
+// 从cdn服务器下载文件,key就是文件名
+async function downloadFileSync(key) {
+    const filePath = path.join(__dirname, '../../../tmp/', key);
+    const bucketManager2 = new qiniu.rs.BucketManager(mac, null);
+    const publicBucketDomain = qiniu_config.Domain; // "http://serverupdate.smartcost.com.cn";//这里不支持https
+    const deadline = parseInt(Date.now() / 1000) + 3600; // 1小时过期
+    const privateDownloadUrl = bucketManager2.privateDownloadUrl(publicBucketDomain, key, deadline);
+    const stream = fs.createWriteStream(filePath);
+    return new Promise((resolve, reject) => {
+        request(privateDownloadUrl)
+            .pipe(stream)
+            .on('close', () => {
+                // 读取文件返回字符串
+                const srcData = fs.readFileSync(stream.path,'utf-8');
+                resolve({
+                    path: stream.path,
+                    srcData: srcData
+                });
+            })
+            .on('error', reject);
+    });
+}
+
+// 导入接口
+async function importInterface(key, session) {
+    // 源文件内容文本
+    let downloadFilePath = '';
+    try {
+        const { path, srcData } = await downloadFileSync(key);
+        downloadFilePath = path;
+        if (!srcData) {
+            throw '无有效数据';
+        }
+        const userID = session.sessionUser.id;
+        const compilationID = session.sessionCompilation._id;
+        const importData = JSON.parse(srcData);
+        let  tenderCount = 0;
+        importData.engs.forEach(eng => {
+            eng.tenders.forEach(tender => {
+                tenderCount += 1;
+            });
+        });
+        if (await isTenderOverrun(tenderCount, session)) {
+            throw '您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。';
+        }
+        const projectData = await importProject(importData, userID, compilationID);
+        return projectData;
+    } catch (err) {
+        throw err;
+    } finally {
+        fs.unlinkSync(downloadFilePath);
+    }
+}
+
 async function importProjects(data,req,updateData) {
     let result = {error:0};
     let stringArr = data.split("|----|");
@@ -1833,6 +1890,12 @@ async function importProjects(data,req,updateData) {
             result.error = 1;
             result.msg = `导入失败:您要导入的文件是由“${fileCompilationName}”导出,当前软件是“${curCompilationName}”,请选择正确的费用定额再进行操作!`;
         }else {
+            const tenders = mainData.projects.filter(item => item.projType === projectType.tender);
+            const tenderOverrun = await isTenderOverrun(tenders.length, req.session);
+            if (tenderOverrun) {
+                result.error = 1;
+                result.msg = `您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。`;
+            }
             let [projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap] = await handleMainProjectDatas(mainData,updateData,req.session.sessionUser.id);
             if(datas.length > 1 ){
                 for(let i = 1;i<datas.length;i++){
@@ -2103,4 +2166,23 @@ function uploadToken() {
         domain: qiniu_config.Domain
     }
     return result
+}
+
+// 有些方法无法通过中间件就检查单位工程数量是否超限
+// 需要到具体的业务代码中进行判断
+// 这个方法就是具体业务代码中,需要检查单位工程数量是否超限用
+async function isTenderOverrun(tenderCount, session) {
+    const userID = session.sessionUser.id;
+    const compilation = session.sessionCompilation._id;
+    const compilationVersion = session.compilationVersion || '免费';
+    let systemSetting = session.systemSetting;
+    // 这种情况只有在刚上线此功能时会出现,不考虑时间差
+    if (!systemSetting) {
+        systemSetting = await systemSettingMiddleware.getSystemSetting();
+        session.systemSetting = systemSetting;
+    }
+    const type = compilationVersion.includes('免费') ? 'normal' : 'professional';
+    const limit = systemSetting[type].project;
+    const curTenderCount = await projectModel.count({userID, compilation, projType: 'Tender', '$or':[{deleteInfo: null}, {'deleteInfo.completeDeleted': false}]});
+    return tenderCount + curTenderCount > limit;
 }

+ 3 - 2
modules/pm/routes/pm_route.js

@@ -6,6 +6,7 @@ import BaseController from "../../common/base/base_controller";
 let express = require('express');
 let pmController = require('./../controllers/pm_controller');
 const baseController = new BaseController();
+const systemMiddleware = require('../../main/middleware/system_setting');
 
 module.exports = function (app) {
 
@@ -34,7 +35,7 @@ module.exports = function (app) {
      req.body = {data: '{user_id: user_id, updateData: [{updateType, updateData}]}'}
      data.updateData.updateType: 1 of ['new', 'update', 'delete']
      */
-    pmRouter.post('/updateProjects', pmController.updateProjects);
+    pmRouter.post('/updateProjects', systemMiddleware.tenderNumberChecking, pmController.updateProjects);
     pmRouter.post('/updateMixDatas', pmController.updateMixDatas);
     pmRouter.post('/moveProject', pmController.moveProject);
 
@@ -43,7 +44,7 @@ module.exports = function (app) {
      data.updateData.updateType: 1 of ['update', 'copy']
      */
     pmRouter.post('/getProjectsByQuery', pmController.getProjectsByQuery);
-    pmRouter.post('/copyProjects', pmController.copyProjects);
+    pmRouter.post('/copyProjects', systemMiddleware.tenderNumberChecking, pmController.copyProjects);
     pmRouter.post('/renameProject', pmController.rename);
     pmRouter.post('/beforeOpenProject', pmController.beforeOpenProject);
     pmRouter.post('/getProject', pmController.getProject);

+ 8 - 0
modules/users/controllers/login_controller.js

@@ -14,6 +14,10 @@ const SMS = require('../models/sms');
 const moment = require('moment');
 // 验证码
 const Captcha = require("../models/captcha");
+let mongoose = require("mongoose");
+let systemSettingModel = mongoose.model("system_setting");
+
+
 
 class LoginController {
 
@@ -99,6 +103,8 @@ class LoginController {
                     request.session.sessionCompilation = compilationData;
                     if(request.session.sessionUser.latest_used !== preferenceSetting.select_version) await userModel.updateLatestUsed(request.session.sessionUser.id,preferenceSetting.select_version);
                 }
+                let systemSetting = await systemSettingModel.findOne({}).lean();
+                request.session.systemSetting = systemSetting;
                 request.session.online_start_time = +new Date();
                 console.log(`${request.session.sessionUser.real_name}--id:${request.session.sessionUser.id}--登录了系统`);
                 if (preferenceSetting.login_ask === 1 || preferenceSetting.select_version === '') {
@@ -318,6 +324,8 @@ class LoginController {
             console.log(error);
             return response.json({error: 1, msg: error});
         }
+        let systemSetting = await systemSettingModel.findOne({}).lean();
+        request.session.systemSetting = systemSetting;
         request.session.online_start_time = +new Date();
         console.log(`${request.session.sessionUser.real_name}--id:${request.session.sessionUser.id}--登录了系统`);
         response.json({

+ 3 - 3
modules/users/controllers/user_controller.js

@@ -57,9 +57,9 @@ class UserController extends BaseController {
                 real_name: request.body.real_name,
                 province: request.body.province,
                 company: request.body.company,
-                version: request.body.version,
-                company_type: request.body.company_type,
-                company_scale: request.body.company_scale,
+                version: request.body.version ? request.body.version : null,
+                company_type: request.body.company_type ? request.body.company_type : null,
+                company_scale: request.body.company_scale ? request.body.company_scale : null,
             };
             let sessionUser = request.session.sessionUser;
             // 切换验证场景

+ 1 - 0
public/scHintBox.html

@@ -180,6 +180,7 @@
     };
 
     $('#hintBox_form').on('hide.bs.modal', function() {
+        $('#hintBox_caption').removeClass('export-check');
         if($.bootstrapLoading) $.bootstrapLoading.end();
         return;
     });

+ 7 - 6
public/web/common_ajax.js

@@ -6,21 +6,21 @@ var CommonAjax = {
     get:function (url,data,cb,dataType) {
         $.get(url,data,cb,dataType)
     },
-    post: function (url, data, successCallback, errorCallback) {
+    post: function (url, data, successCallback, errorCallback, timeout = 50000) {
         $.ajax({
             type:"POST",
             url: url,
             data: {'data': JSON.stringify(data)},
             dataType: 'json',
             cache: false,
-            timeout: 50000,
+            timeout: timeout,
             success: function(result){
                 if (result.error === 0) {
                     if (successCallback) {
                         successCallback(result.data);
                     }
                 } else {
-                    alert('error: ' + result.message);
+                    alert(result.message);
                     if (errorCallback) {
                         errorCallback();
                     }
@@ -173,11 +173,12 @@ async function ajaxPost(url, data, isPlainData = false) {
             cache: false,
             timeout: 200000,
             success: function(result){
-                if (!result.error || !result.err) {
+                if (!result.error ||  commonUtil.isDef(result.err) && !result.err) {
                     resolve(result.data);
                 } else {
-                    alert('error: ' + result.message);
-                    reject(result.message);
+                    const message = result.message || result.msg;
+                    alert('error: ' + message);
+                    reject(message);
                 }
             },
             error: function(jqXHR, textStatus, errorThrown){

+ 6 - 6
public/web/ration_glj_units.js

@@ -31,14 +31,14 @@ const rationAndGljUnits =[
     '部',
     '10t',
     '10m',
-    '10 m2',
-    '10 m3',
+    '10m2',
+    '10m3',
     '100m',
-    '100 m2',
-    '100 m3',
+    '100m2',
+    '100m3',
     '1000m',
-    '1000 m2',
-    '1000 m3',
+    '1000m2',
+    '1000m3',
     '10000块',
     '立方米',
     '平方米',

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

@@ -378,6 +378,9 @@ var sheetCommonObj = {
                 val = setting.getText[setting.header[col].getText](data[row],val)
             }
             sheet.setValue(row, col, val, ch);
+            if(data[row].foreColor && data[row].styleCol == col){
+                sheet.setStyle(row, col, {foreColor: "#ff2a23"});
+            }
         }
         this.setRowStyle(row,sheet,data[row].bgColour);
         if(setting.autoFit==true){//设置自动行高
@@ -1223,7 +1226,10 @@ var sheetCommonObj = {
             treeSetting.cols.push(getSettingCol(h))
         }
         for(let l of setting.view.lockColumns){
-            treeSetting.cols[l].readOnly = true;
+            if(_.isString(l)){//如果是dataCode 进行转换
+                l = _.findIndex(setting.header,{dataCode:l})
+            }
+            if(treeSetting.cols[l]) treeSetting.cols[l].readOnly = true;
         }
         return treeSetting;
         function getSettingCol(header) {
@@ -1465,5 +1471,11 @@ var sheetCommonObj = {
                 break;
         }
         return result;
+    },
+    // 延迟一段时间刷新表格,因为有的弹窗里面有表格,马上刷新可能会造成,弹窗界面还未完全显示完就完成了表格刷新,导致表格显示不完整
+    refreshWorkbookDelDefer(workbook, time) {
+        if (workbook) {
+            setTimeout(() => workbook.refresh(), time);
+        }
     }
 }

+ 7 - 12
public/web/tree_sheet/tree_sheet_helper.js

@@ -158,27 +158,22 @@ var TREE_SHEET_HELPER = {
                     }
                     return data;
                 };
+                var cell = sheet.getCell(iRow, iCol, GC.Spread.Sheets.SheetArea.viewport);
                 if(sheet.name()=="mainSheet"){
-                 /*   if(colSetting.data.field=="quantity"){  2018-08-06 去掉工程量列表达式
-                        let tag = node.data.quantityEXP?node.data.quantityEXP:'';
-                        sheet.setTag(iRow, iCol,tag);
-                    }*/
                     if(colSetting.data.field=="code"){
                         let tag =null;
                         if(node.sourceType == ModuleNames.ration){//定额的时候换算子目
                             tag = node.data.adjustState?node.data.adjustState:'';
-                        }/*else if(node.sourceType == ModuleNames.bills &&projectObj.ifItemCharHiden(setting)){//清单、并且项目特征列隐藏的时候悬浮提示  这里改成在 计量单位那里提示
-                            tag = node.data.itemCharacterText?node.data.itemCharacterText:'';
-                        }*/
+                        }
                         if(tag!=null) sheet.setTag(iRow, iCol,tag);
                     }
-                    /*if(colSetting.data.field=="name"){ 2018-08-06 改成在编号列悬浮提示
-                        let tag = node.data.itemCharacterText?node.data.itemCharacterText:'';
-                        sheet.setTag(iRow, iCol,tag);
-                    }*/
+                    // 单元格字体颜色
+                    const foreColorFunc = MainTreeCol.foreColor[colSetting.data.field];
+                    if (foreColorFunc) {
+                        cell.foreColor(foreColorFunc(node));
+                    }
                 }
                 if(colSetting.visible == false) return;//隐藏列不做其它操作
-                var cell = sheet.getCell(iRow, iCol, GC.Spread.Sheets.SheetArea.viewport);
                 if (colSetting.data.getText && Object.prototype.toString.apply(colSetting.data.getText) === "[object Function]") {
                     cell.value(colSetting.data.getText(node));
                 }else if((colSetting.data.field=="mainBills"||(colSetting.data.field=="outPutMaxPrice" && $("#fileKind").val() != '1'))&&MainTreeCol.mainBillsEnable(node)){//主要清单有三种状态,所以直接显示就好,不走最后的逻辑

+ 63 - 0
public/web/upload_cdn.js

@@ -0,0 +1,63 @@
+/*
+ * @Descripttion: 上传文件到cdn服务器
+ * @Author: Zhong
+ * @Date: 2019-12-31 15:38:44
+ */
+
+const UPLOAD_CDN = (() => {
+    
+    const config = {
+        useCdnDomain: true,
+        disableStatisticsReport: false,
+        retryCount: 6,
+        region: qiniu.region.z2
+    };
+
+    // 上传
+    function upload(file, key, token, callback, errCallback) {
+        const putExtra = {
+            fname: "",
+            params: {"x:name":key.split(".")[0]},
+            mimeType: null
+        };
+        const observable = qiniu.upload(file, key, token, putExtra, config);
+        observable.subscribe({
+            error:function (err) {
+                console.log(err);
+                if (errCallback) {
+                    errCallback(err);
+                }
+            },
+            complete:function(res){
+                if (callback) {
+                    callback(res);
+                }
+            }
+        })
+    }
+
+    // 同步上传
+    function uploadSync(file, key, token) {
+        return new Promise((resolve, reject) => {
+            const putExtra = {
+                fname: "",
+                params: {"x:name":key.split(".")[0]},
+                mimeType: null
+            };
+            const observable = qiniu.upload(file, key, token, putExtra, config);
+            observable.subscribe({
+                error:function (err) {
+                    reject(err);
+                },
+                complete:function(res){
+                    resolve(res);
+                }
+            })
+        });
+    }
+
+    return {
+        upload,
+        uploadSync,
+    };
+})();

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

@@ -374,6 +374,9 @@ input.text-right{
     color:#ECE0F5 !important;
     -webkit-text-stroke:.5px #ced4da;
 }
+.z-index-3000 {
+    z-index: 3000;
+}
 .progress-bar {
     position: relative;
     width: 100%;
@@ -444,4 +447,19 @@ input.text-right{
 }
 .hide-area {
     display: none;
+}
+.middle-modal-width {
+    max-width: 650px;
+}
+.middle-modal-height {
+    height: 500px;
+}
+
+@media screen and (max-width: 1366px), (max-height: 768px) {
+    .middle-modal-width {
+        max-width: 500px;
+    }
+    .middle-modal-height {
+        height: 350px;
+    }
 }

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

@@ -871,7 +871,9 @@
                                 <li class="nav-item ml-3"><a class="nav-link" data-toggle="pill" href="#compilationIllustrationP" role="tab" id="tab_compilation_illustration_p">编制说明</a></li>
                                 <li class="nav-item"><a class="nav-link disabled">单位工程信息</a></li>
                                 <li class="nav-item ml-3"><a class="nav-link" data-toggle="pill" href="#poj-settings-projFeature" id="tab_poj-settings-projFeature" role="tab">工程特征</a></li>
-                                <li class="nav-item ml-3"><a class="nav-link" data-toggle="pill" href="#poj-settings-indicativeInfo" id="tab_poj-settings-indicativeInfo" role="tab">指标信息</a></li>
+                                <% if (compilationName === '重庆定额(2018)') { %>
+                                    <li class="nav-item ml-3"><a class="nav-link" data-toggle="pill" href="#poj-settings-indicativeInfo" id="tab_poj-settings-indicativeInfo" role="tab">指标信息</a></li>
+                                <% } %>
                                 <li class="nav-item ml-3"><a class="nav-link" data-toggle="pill" href="#poj-settings-4" id="about-calc" role="tab">关于计算</a></li>
                                 <li class="nav-item ml-3"><a class="nav-link" data-toggle="pill" href="#poj-settings-billsQuanDecimal" id="tab_poj-settings-bqDecimal" role="tab">清单工程量精度</a></li>
                                 <li class="nav-item ml-3"><a class="nav-link" data-toggle="pill" href="#poj-settings-decimal" role="tab" id="tab_poj-settings-decimal">小数位数</a></li>
@@ -1273,7 +1275,8 @@
                             </div>
                         </div>
                 </div>
-                <div class="modal-footer">
+                <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>

+ 3 - 3
web/building_saas/main/html/tender_price.html

@@ -3,8 +3,8 @@
     <div class="btn-toolbar py-1">
         <div class="input-group input-group-sm mr-2">
             <select class="form-control form-control-sm" style="width: auto; font-size: .875rem" id="calcPriceOption">
-                <option value="coeBase">根据调整系数计算报价</option>
-                <option value="priceBase" >根据报价计算调整系数</option>
+                <option value="coeBase">按调价系数计算</option>
+                <option value="priceBase" >按目标价计算</option>
             </select>
         </div>
         <div class="input-group input-group-sm mr-2" style="width:240px">
@@ -23,7 +23,7 @@
         <div class="form-check" style="margin-left:40px; margin-top:5px;">
             <label class="form-check-label">
                 <input class="form-check-input" name="cbShowTenderFields" id="cbShowTenderFields" value="true" type="checkbox">
-                显示调价后数据(造价书人材机界面、人材机汇总界面)
+                <span data-toggle="tooltip" data-original-title="造价书界面定额人材机的调价、人材机汇总界面的调价">显示调价后数据</span>
             </label>
         </div>
     </div>

+ 18 - 4
web/building_saas/main/js/models/calc_program.js

@@ -1016,6 +1016,19 @@ let calcTools = {
     isEmptyObject(obj){
         let arr = Object.keys(obj);
         return arr.length == 0;
+    },
+    // 清单价格是否大于最高限价
+    unitFeeGTMaxPrice: function (node, feeField) {
+        if (!this.isBill(node)) {
+            return false;
+        }
+        const totalFee = this.getFee(node, feeField);
+        const maxPrice = node.data.maxPrice;
+        // 最高限价有值才对比
+        if (!commonUtil.isDef(maxPrice)) {
+            return false;
+        }
+        return totalFee > maxPrice;
     }
 };
 
@@ -1503,9 +1516,10 @@ class CalcProgram {
         return ModuleNames.calc_program;
     };
     // 兼容Project框架方法
-    loadData (datas) {
+    // isInit:是否初始化,进入单位工程为true,导出接口为false,不需要存储费率临时数据
+    loadData (datas, isInit) {
         this.datas = datas;
-        this.compileAllTemps();
+        this.compileAllTemps(isInit);
     };
     // 兼容Project框架方法
     doAfterUpdate (err, data) {
@@ -1515,7 +1529,7 @@ class CalcProgram {
     };
 
     // 经测试,全部编译一次耗时0.003~0.004秒。耗时基本忽略不计。
-    compileAllTemps(){
+    compileAllTemps(isInit = false){
         let me = this;
         me.compiledFeeRates = {};
         me.compiledLabourCoes = {};
@@ -1543,7 +1557,7 @@ class CalcProgram {
 
 
         // 存储费率临时数据,报表用。
-        if (me.saveForReports.length > 0){
+        if (isInit && me.saveForReports.length > 0){
             let saveDatas = {};
             saveDatas.projectID = projectObj.project.projectInfo.ID;
             saveDatas.calcItems = me.saveForReports;

+ 82 - 17
web/building_saas/main/js/models/exportSEIInterface.js

@@ -29,24 +29,41 @@ let exportUtil = {
 async function exportSEI(projectID) {
 
     let tenderProjects = [];
+    let errorMap = {};
     let result = await ajaxPost("/project/getSEIProjects",{projectID:projectID});
     let pr = new SCComponent.InitProgressBar();
     pr.start('导出数据接口', '正在导出文件,请稍候……');
-    console.log(result);
     let project = getProject(result);
-
     await prepareTenderDatas(tenderProjects,project);
 
 
+    if(!_.isEmpty(errorMap)){
+        showError(errorMap);
+        return pr.end();
+    }
     toXml(project);
 
     pr.end();
 
-
-
+    
+    function showError(errorMap) {
+        let infoList = [];
+        for(let name in errorMap){
+            infoList.push(`<span style="font-weight: bold">单位工程“${name}”下:</span>`);
+            for(let err of errorMap[name]){
+                infoList.push(err);
+            }
+        }
+        if(infoList.length > 20){
+            $('#hintBox_caption').addClass('export-check');
+        }
+        let msg = infoList.join(`<br>`);
+        alert(msg);
+    }
 
     function getProject(orignal) {//取建设项目信息
         let basicInformation = getBaseInfo(orignal);
+        let textTpye =  orignal.property.taxType == 1 ?"一般计税":"简易计税";
         let project = {
             name: "建设项目",
             attrs: [
@@ -54,7 +71,8 @@ async function exportSEI(projectID) {
                 {name:"建设单位",value:getItemValueBykey(basicInformation.items,"constructionUnit")},
                 {name:"施工单位",value:getItemValueBykey(basicInformation.items,"buildingUnit")},
                 {name: "标准名称", value: "重庆市建设工程造价指标采集标准(清单计价)"},
-                {name: "标准版本号", value: "1.1"}
+                {name: "标准版本号", value: "1.1"},
+                {name: "计税模式", value: textTpye}
             ],
             basicInformation:basicInformation,
             children:[]
@@ -93,7 +111,8 @@ async function exportSEI(projectID) {
             children:[],
             valuationType:source.property.valuationType,
             taxType:source.property.taxType,
-            property:source.property
+            property:source.property,
+            name:source.name
         };
         return tender;
     }
@@ -141,6 +160,7 @@ async function exportSEI(projectID) {
             if(projectData) gljUtil.calcProjectGLJQuantity(projectData.projectGLJs,projectData.ration_gljs,projectData.rations,projectData.bills,getDecimal("glj.quantity"),_,scMathUtil);
             let materials = indexObj.getMainMaterialDatas(tender.property.materials,projectData.projectGLJs,tender.property.calcOptions,tender.property.decimal,false,_,scMathUtil);
             for(let m of materials){
+                if(m.name == "其他材料") continue;//2020-01-08  其他材料 不输出字段(不管是否有值)。
                 mainIndex.children.push(getMaterial(m))
             }
             let index = {
@@ -151,8 +171,9 @@ async function exportSEI(projectID) {
             return index;
 
             function getMaterial(m) {
+                let mname = m.exportName?m.exportName:m.name;
                 let material = {
-                    name:m.name,
+                    name:mname,
                     attrs:[
                         {name:"综合单价",value:scMathUtil.roundToString(m.unitPrice,2)},
                         {name:"数量",value:scMathUtil.roundToString(m.quantity,2)},
@@ -184,11 +205,12 @@ async function exportSEI(projectID) {
             return index;
 
             function getQuantity(q) {
+                let qname = q.exportName?q.exportName:q.name;
                 let quantity = {
-                    name:q.name,
+                    name:qname,
                     attrs:[
                         {name:"工程量指标",value:scMathUtil.roundToString(q.quantity,3)},
-                        {name:"单位",value:q.unit}
+                        {name:"单位",value:q.quantityIndexUnit}
                     ]
                 };
                 return quantity;
@@ -213,8 +235,9 @@ async function exportSEI(projectID) {
             return index;
 
             function getEco(e) {
+                let ename = e.exportName?e.exportName:e.name;
                 let eco = {
-                  name:e.name,
+                  name:ename,
                   attrs:[
                       {name:"综合合价",value:scMathUtil.roundToString(e.cost,2)},
                       {name:"单方指标",value:scMathUtil.roundToString(e.unitCost,2)},
@@ -264,13 +287,35 @@ async function exportSEI(projectID) {
                 {name: "备注", value: source.remark}
             ]
         };
+        handleXMLEntity(g.attrs);
         return g;
     }
-
+    // 对每个元素的所有属性值进行特殊字符处理
+    function handleXMLEntity(attrs) {
+        let _xmlEntity = {
+            '&': '&amp;',
+            '\n': '&#xA;',
+            '"': '&quot;',
+            '\'': '&apos;',
+            '<': '&lt;',
+            '>': '&gt;'
+        };
+        for (let attr of attrs) {
+            if (!attr.value) {
+                continue;
+            }
+            for (let [key, value] of Object.entries(_xmlEntity)) {
+                attr.value = attr.value+"";
+                attr.value = attr.value.replace(new RegExp(key, 'g'), value);
+            }
+        }
+    }
 
 
     function setEngineerInfo(tender) {//设置工程信息
         let infos = tender.property.engineerInfos?tender.property.engineerInfos:[];
+        let errors =  infoRequireChecking(infos);
+        if(errors.length > 0) errorMap[tender.name] = errors;
         let info = {
             name:"工程信息",
             attrs:[
@@ -289,8 +334,7 @@ async function exportSEI(projectID) {
                 {name:"计价方式及依据",value:getItemValueByDispName(infos,"计价方式及依据")},
                 {name:"工程类别",value:getItemValueByDispName(infos,"工程类别")},
                 {name:"编制日期",value:getItemValueByDispName(infos,"编制日期")},
-                {name:"审查日期",value:getItemValueByDispName(infos,"审查日期")}
-
+                {name:"审查日期",value:getItemValueByDispName(infos,"审查日期"),required:false}
             ]
         };
         initAtts(info.attrs);
@@ -326,8 +370,8 @@ async function exportSEI(projectID) {
         }
 
         function getFeatrue(node,parentMap){
+            let name = node.exportName?node.exportName:node.name;
             if(parentMap[node.ID]){//如果有子节点,那么它就是一个节点
-                let name = node.exportName?node.exportName:node.name;
                 let tem = {
                     name:name.replace("*",""),
                     attrs:[],
@@ -340,11 +384,21 @@ async function exportSEI(projectID) {
                 return tem;
             }else {//如果没有子节点,那么它就是父节点的一个属性
                 if(node.isDetail == true){//如果是明细节点,则造一个明细节点
-                    return {name:"明细",attrs:[{name:"名称",value:node.value}],children:[]};
+                    return {name:"明细",attrs:[{name:"名称",value:getValue(node)}],children:[]};
                 }
-                return {name:node.name.replace("*",""),value:node.value};
+                return {name:name.replace("*",""),value:getValue(node)};
             }
         }
+
+        function getValue(node) {
+            let value = node.value;
+            if(node.required == true || node.cellType == "number"){//必填项的值为空时导出0
+                if(value == undefined || value == null || value == "") value = "0";
+            }
+            return value;
+        }
+
+
     }
 
 
@@ -357,6 +411,15 @@ async function exportSEI(projectID) {
     }
 
 
+    function infoRequireChecking(items) {
+        let error = [];
+        for(let i of items){
+            if(i.required == true && _.isEmpty(i.value)) error.push(`工程信息 - ${i.dispName}不能为空。`);
+        }
+        return error;
+    }
+
+
     function getItemValueBykey(items,key) {
         let item = _.find(items,{"key":key});
         if(item) return item.value;
@@ -364,7 +427,9 @@ async function exportSEI(projectID) {
     }
     function getItemValueByDispName(items,dispName) {
         let item = _.find(items,{"dispName":dispName});
-        if(item) return item.value;
+        if(item){
+            return item.value;
+        }
         return ""
     }
 

+ 54 - 20
web/building_saas/main/js/models/exportStdInterfaceBase.js

@@ -162,7 +162,7 @@ const XML_EXPORT_BASE = (() => {
      * @return {Object} failHints没通过的属性提示 filterAttrs过滤后的属性数据(失败提示在属性是必须的时候才提示,如果该属性失败了,但是是非必要属性,那么该属性不显示)
      * */
     function check(eleName, datas) {
-        let rst = {failHints: [], filterAttrs: []};
+        let rst = { failHints: [], filterAttrs: [] };
         let dateReg = /([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8])))/;
         for (let data of datas) {
             // 值统一转换成String
@@ -176,19 +176,15 @@ const XML_EXPORT_BASE = (() => {
                 data.value = data.value.trim();
                 data.value = data.value.replace(/\s{1,}/g, ' ');
             }
-            /*if (!data.value && !data.minLen && !data.enumeration) {  //值为空,且没有限制最小字符数,且没有限制值,则不需判断
-             rst.filterAttrs.push(data);
-             continue;
-             }*/
             let isFail = false;
             let tempFail = '';
             // 提示的名称需要处理,有的接口xml属性为英文,需要显示其展示名称
             const name = data.dName || data.name;
-            if (data.minLen && data.value.length < data.minLen){
+            if (data.minLen && data.value.length < data.minLen) {
                 isFail = true;
                 tempFail = data.failHint
                     ? `${data.failHint}字符数不可小于${data.minLen}个。`
-                    :`${eleName}-“${data.name}”字符数不可小于${data.minLen}个。`;
+                    : `${eleName}-“${data.name}”字符数不可小于${data.minLen}个。`;
             } else if (data.maxLen && data.value.length > data.maxLen) {
                 isFail = true;
                 tempFail = data.failHint
@@ -224,7 +220,7 @@ const XML_EXPORT_BASE = (() => {
                     tempFail = data.failHint
                         ? `${data.failHint}必须为数值。`
                         : `${eleName}-“${data.name}”必须为数值。`;
-                } else if (!data.value.length || (data.value.split('.').length > 1 && data.value.split('.')[1].length > 2)){
+                } else if (!data.value.length || (data.value.split('.').length > 1 && data.value.split('.')[1].length > 2)) {
                     isFail = true;
                 }
             } else if (data.type && data.type === TYPE.BOOL && !['true', 'false'].includes(String(data.value))) {
@@ -466,7 +462,7 @@ const XML_EXPORT_BASE = (() => {
         let target = [],
             temp = {};
         for (let data of datas) {
-            temp[data.ID] = {me: data, next: null, prev: null};
+            temp[data.ID] = { me: data, next: null, prev: null };
         }
         for (let data of datas) {
             let next = temp[data.NextSiblingID] || null;
@@ -503,8 +499,8 @@ const XML_EXPORT_BASE = (() => {
         let projectData = _cache.projectData;
         // 没有数据,需要拉取
         if (!Object.keys(projectData).length) {
-            projectData = await ajaxPost('/pm/api/getProjectByGranularity', {user_id: userID, tenderID: tenderID, granularity: granularity});
-            _cache.projectData =  projectData;
+            projectData = await ajaxPost('/pm/api/getProjectByGranularity', { user_id: userID, tenderID: tenderID, granularity: granularity });
+            _cache.projectData = projectData;
         }
         return projectData;
     }
@@ -521,6 +517,9 @@ const XML_EXPORT_BASE = (() => {
         if (!tenderDetail) {
             tenderDetail = PROJECT.createNew(tenderID, userID);
             await tenderDetail.loadDataSync();
+            // 标记序号
+            const count = Object.keys(_cache.tenderDetailMap).length;
+            tenderDetail.serialNo = count + 1;
             _cache.tenderDetailMap[tenderID] = tenderDetail;
         }
         return tenderDetail;
@@ -562,7 +561,7 @@ const XML_EXPORT_BASE = (() => {
     // 1.有子项,则取固定清单对应基数
     // 2.无子项,有基数,a.优先转换为行代号(不可自身) b.不能转换为行代号则找对应字典
     // 3.基数中有无法转换的,根据导出类型决定
-    function transformCalcBase(exportKind, tenderDetail, node, {CalcBaseMap, FlagCalcBaseMap}) {
+    function transformCalcBase(exportKind, tenderDetail, node, { CalcBaseMap, FlagCalcBaseMap }) {
         let expr = node.data.calcBase || '';
         if (node.children.length) {
             let flag = node.getFlag();
@@ -612,7 +611,7 @@ const XML_EXPORT_BASE = (() => {
                 } else {
                     let totalFee = getFee(node.data.fees, 'common.totalFee'),
                         feeRate = node.data.feeRate;
-                    return +feeRate ? scMathUtil.roundTo(totalFee/(feeRate/100), -2) : totalFee
+                    return +feeRate ? scMathUtil.roundTo(totalFee / (feeRate / 100), -2) : totalFee
                 }
             }
             return expr;
@@ -655,7 +654,7 @@ const XML_EXPORT_BASE = (() => {
                     break;
                 }
                 newBase.push(node && node.data.name ? node.data.name : '');
-            } else if (CalcStateMap[base]){    //字典转换为中文
+            } else if (CalcStateMap[base]) {    //字典转换为中文
                 newBase.push(CalcStateMap[base]);
             } else if (/^\d+(\.\d+)?$/.test(base)) {    //金额
                 newBase.push(base);
@@ -738,6 +737,40 @@ const XML_EXPORT_BASE = (() => {
         });
     }
 
+    /**
+     * 弱自检自检,有错误非强制不可导的自检
+     * failList里存的是导出规定属性时,不符合标准规定的错误,存在这种错误就不允许导出
+     * 这里的错误是业务上的提示错误,与标准文件规定无关,存在这种错误是允许导出的
+     * @return {Array} - 提示信息数组 
+     */
+    function softCheck() {
+        const tenderDetailMap = _cache.tenderDetailMap;
+        // 检查清单综合单价是否大于最高限价,大于则提示
+        function checkMaxPrice(tenderDetail) {
+            return tenderDetail.mainTree.items
+                    .filter(node => calcTools.unitFeeGTMaxPrice(node, 'common.unitFee'))
+                    .map(node => {
+                        const code = node.data.code || '';
+                        const name = node.data.name || '';
+                        return `第${node.serialNo()}行“${code + name}”,清单综合单价 > 最高限价`;
+                    });
+        }
+        const infos = [];
+        // 按照获取顺序serialNo(根据树结构)排序
+        Object.values(tenderDetailMap)
+            .sort((a, b) => a.serialNo - b.serialNo)
+            .forEach(tenderDetail => {
+                const maxPriceInfos = checkMaxPrice(tenderDetail);
+                if (!maxPriceInfos.length) {
+                    return;
+                }
+                infos.push(`<span style="font-weight: bold">单位工程“${tenderDetail.projectInfo.name}”下:</span>`);
+                infos.push(...maxPriceInfos);
+            });
+        return infos;
+    }
+    
+
     const UTIL = Object.freeze({
         deWeightHints,
         isDef,
@@ -759,7 +792,8 @@ const XML_EXPORT_BASE = (() => {
         getCodeSheetData,
         getElementFromSrc,
         getParsedData,
-        setupCode
+        setupCode,
+        softCheck
     });
 
     // 开始标签
@@ -794,8 +828,8 @@ const XML_EXPORT_BASE = (() => {
         //调整格式
         let rgx = /\n(<(([^\?]).+?)(?:\s|\s*?>|\s*?(\/)>)(?:.*?(?:(?:(\/)>)|(?:<(\/)\2>)))?)/mg;
         let nodeStack = [];
-        let output = text.replace(rgx, function($0, all, name, isBegin, isCloseFull1, isCloseFull2, isFull1, isFull2){
-            let isClosed = (isCloseFull1 === '/') || (isCloseFull2 === '/' ) || (isFull1 === '/') || (isFull2 === '/');
+        let output = text.replace(rgx, function ($0, all, name, isBegin, isCloseFull1, isCloseFull2, isFull1, isFull2) {
+            let isClosed = (isCloseFull1 === '/') || (isCloseFull2 === '/') || (isFull1 === '/') || (isFull2 === '/');
             let prefix = '';
             if (isBegin === '!') {
                 prefix = getPrefix(nodeStack.length);
@@ -810,7 +844,7 @@ const XML_EXPORT_BASE = (() => {
                     prefix = getPrefix(nodeStack.length);
                 }
             }
-            let ret =  '\n' + prefix + all;
+            let ret = '\n' + prefix + all;
             return ret;
         });
         let outputText = output.substring(1);
@@ -818,7 +852,7 @@ const XML_EXPORT_BASE = (() => {
         function getPrefix(prefixIndex) {
             let span = '    ';
             let output = [];
-            for (let i = 0 ; i < prefixIndex; ++i) {
+            for (let i = 0; i < prefixIndex; ++i) {
                 output.push(span);
             }
             return output.join('');
@@ -847,7 +881,7 @@ const XML_EXPORT_BASE = (() => {
             xmlStr = `<?xml version="1.0" encoding="utf-8"?>${xmlStr}`;
             // 格式化
             xmlStr = _formatXml(xmlStr);
-            let blob = new Blob([xmlStr], {type: 'text/plain;charset=utf-8'});
+            let blob = new Blob([xmlStr], { type: 'text/plain;charset=utf-8' });
             return {
                 blob: blob,
                 exportKind: extractObj.exportKind,

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

@@ -1064,8 +1064,6 @@ const OVER_HEIGHT = (() => {
      * 因此各种操作下改变了相关定额,都要重新计算超高子目
      * 为了降低复杂度和保证逻辑统一性,重新计取为重新走(删除新增逻辑)
      * 需要尽可能地降低操作的触发率
-     * @param {type}  - 
-     * @return: {type} - 
      */
     function reCalcOverHeightFee() {
         const project = projectObj.project;

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

@@ -30,7 +30,8 @@ var PROJECT = {
             });
 
         };
-        tools.doAfterLoad = function(result, showProjectInfo, callback){
+        // isInit: 是否是初始化,导出接口用到getData,但是非初始化,多次调用getData不会覆盖原有缓存数据
+        tools.doAfterLoad = function(result, isInit, callback){
             var counter;
             //必须要先load ProjectInfo的信息
             let projectInfoModule = result.find(data => data.moduleName === ModuleNames.projectInfo);
@@ -41,7 +42,7 @@ var PROJECT = {
             result.forEach(function(item){
                 if (item.moduleName !== ModuleNames.projectInfo) {
                     if (me.modules[item.moduleName]){
-                        me.modules[item.moduleName].loadData(item.data);
+                        me.modules[item.moduleName].loadData(item.data, isInit);
                     } else if (item.moduleName === me.projCounter) {
                         counter = item.data;
                     } else if (item.moduleName === me.projSetting) {
@@ -57,7 +58,7 @@ var PROJECT = {
                     me.modules[module].setMaxID(counter[module]);
                 }
             }
-            if (showProjectInfo) {
+            if (isInit) {
                 projectInfoObj.showProjectInfo(me._project.projectInfo);
             }
             me._project.loadMainTree();
@@ -242,8 +243,8 @@ var PROJECT = {
                 timeout: 50000,
                 success: function (result) {
                     if (!result.error) {
-                        let showProjectInfo = true;
-                        tools.doAfterLoad(result.data, showProjectInfo, callback);
+                        const isInit = true;
+                        tools.doAfterLoad(result.data, isInit, callback);
                         // for test calc
                         //tools.doAfterLoad([{moduleName: 'bills', data: BillsData}, {'moduleName': 'ration', data: DrawingData}], callback);
                     } else {
@@ -257,10 +258,10 @@ var PROJECT = {
             });
         };
         project.prototype.loadDataSync = async function () {
-            let data = await ajaxPost('/project/getData', {user_id: tools._userID, project_id: tools._ID});
+            const data = await ajaxPost('/project/getDataForInterface', {user_id: tools._userID, project_id: tools._ID});
             if (data) {
-                let showProjectInfo = false;
-                tools.doAfterLoad(data, showProjectInfo);
+                const isInit = false;
+                tools.doAfterLoad(data, isInit);
             }
         };
 

+ 2 - 2
web/building_saas/main/js/models/ration.js

@@ -516,7 +516,7 @@ var Ration = {
                         clearInterval(interval);
                     }
                 }, 100);
-                CommonAjax.post("/ration/addMultiRation",{newDatas: newDatas},function (rstData) {
+                CommonAjax.post("/ration/addMultiRation",{projectID:me.project.ID(),newDatas: newDatas},function (rstData) {
                     let newNodes = [];
                     //更新缓存
                     for(let data of rstData){
@@ -638,7 +638,7 @@ var Ration = {
                     needInstall = project.Bills.isFBFX(billsNode);//在分部分项插入的定额才需要定额安装增加费
                 }
                 $.bootstrapLoading.start();
-                CommonAjax.post("/ration/addNewRation",{itemQuery:itemQuery,newData:newData,defaultLibID: rationLibObj.getDefaultStdRationLibID(),calQuantity:calQuantity,brUpdate:brUpdate,needInstall:needInstall},function (data) {
+                CommonAjax.post("/ration/addNewRation",{projectID:me.project.ID(),itemQuery:itemQuery,newData:newData,defaultLibID: rationLibObj.getDefaultStdRationLibID(),calQuantity:calQuantity,brUpdate:brUpdate,needInstall:needInstall},function (data) {
                     //更新缓存
                     me.datas.push(data.ration);
                     me.addSubListOfRation(data);

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

@@ -370,14 +370,10 @@ $(document).ready(function () {
         if(toggle === 'calcBase'){
             $('#tabCalcBase').tab('show');
         }
-        if(calcBaseView.workBook){
-            calcBaseView.workBook.refresh();
-        }
+        sheetCommonObj.refreshWorkbookDelDefer(calcBaseView.workBook, 100);
     });
     $('#tabCalcBase').on('shown.bs.tab', function () {
-        if(calcBaseView.workBook){
-            calcBaseView.workBook.refresh();
-        }
+        sheetCommonObj.refreshWorkbookDelDefer(calcBaseView.workBook, 100);
     });
 
     /*$('#qd-jsjs').on('hidden.bs.modal', function () {

+ 39 - 16
web/building_saas/main/js/views/export_view.js

@@ -59,6 +59,22 @@ const ExportView = (() => {
         _exportCache = [];
         _cache.clear();
     }
+    // 设置提示高度样式
+    function setHeightByInfo(infos) {
+        if (infos.length * 20 > 400) {
+            $('#hintBox_caption').addClass('export-check');
+        }
+    }
+    // 显示工程编码设置窗口
+    function showSetCodeModal(isPring) {
+        if (isPring) {
+            setTimeout(() => {
+                $('#exportCode').modal('show');
+            }, 300);
+        } else {
+            $('#exportCode').modal('show');
+        }
+    }
     //事件监听
     function exportListener() {
         //导出接口-项目自检
@@ -84,15 +100,18 @@ const ExportView = (() => {
                     }
                 }
                 failList = _util.deWeightHints(failList);
-                //设置提示弹窗
-                if (failList.length * 20 > 400) {
-                    $('#hintBox_caption').addClass('export-check');
-                }
                 if (failList.length) {
+                    //设置提示弹窗
+                    setHeightByInfo(failList);
                     throw failList.join('<br/>');
-                } else {
-                    throw '自检完成,未检测到错误数据。'
+                } 
+                // 弱自检
+                const infos = _util.softCheck();
+                if (infos.length) {
+                    setHeightByInfo(infos);
+                    throw infos.join('<br/>');
                 }
+                throw '自检完成,未检测到错误数据。'
             } catch (err) {
                 alert(err);
             }
@@ -128,21 +147,25 @@ const ExportView = (() => {
                     pr.end();
                 }
                 failList = _util.deWeightHints(failList);
-                //错误-设置提示弹窗
-                if (failList.length * 20 > 400) {
-                    $('#hintBox_caption').addClass('export-check');
-                }
                 if (failList.length) {
+                    //错误-设置提示弹窗
+                    setHeightByInfo(failList);
                     throw failList.join('<br/>');
                 }
-                //弹出工程编号设置窗口
-                if (isPring) {
+                // 如果没有自检过,需要进行弱自检,并且弹出的窗口包含“继续、取消”按钮
+                const infos = _util.softCheck();
+                if (infos.length) {
+                    const doYes = () => {
+                        showSetCodeModal(isPring);
+                    };
+                    hintBox.infoBox('系统提示', infos.join('<br/>'), hintBox.btnType.yesNo, doYes, null, ['继续', '取消']);
                     setTimeout(() => {
-                        $('#exportCode').modal('show');
+                        STATE.exporting = false;
                     }, 300);
-                } else {
-                    $('#exportCode').modal('show');
+                    return;
                 }
+                //弹出工程编号设置窗口
+                showSetCodeModal(isPring);
             } catch (err) {
                 console.log(err);
                 pr.end();
@@ -208,7 +231,7 @@ const ExportView = (() => {
             STATE.exporting = false;
             STATE.confirming = false;
             //恢复设置提示弹窗 因为是共用的alert
-            $('#hintBox_caption').removeClass('export-check');
+            //$('#hintBox_caption').removeClass('export-check');  已经统一在info_box关闭时删除了
             $('#export input[type="checkbox"]:eq(0)').prop('checked', true);
             if (spread) {
                 spread.destroy();

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

@@ -145,6 +145,7 @@ var feeRateObject={
         let visibleMap = {};
         sheet.suspendPaint();
         sheet.suspendEvent();
+        sheet.setRowCount(data.length);
         for (let col = 0; col < setting.header.length; col++) {
             let hAlign = "left", vAlign = "center";
             if (setting.header[col].hAlign) {

+ 5 - 1
web/building_saas/main/js/views/index_view.js

@@ -110,7 +110,7 @@ let indexObj= {
         if(projectReadOnly){
             disableSpread(this.spread);
         }
-        this.spread.refresh();
+        sheetCommonObj.refreshWorkbookDelDefer(this.spread, 100);
     },
     initEngineerInfoSheet:function () {
         let sheet = this.spread.getSheet(0);
@@ -224,6 +224,10 @@ let indexObj= {
                         options:info.options,
                         cellType:info.cellType
                     };
+                    if(info.required == true){
+                        d.foreColor = "#ff2a23";
+                        d.styleCol = 0;
+                    }
                     if(info.cellType)d.dateCol = 1;
                     datas.push(d);
                 }

+ 9 - 3
web/building_saas/main/js/views/main_tree_col.js

@@ -557,6 +557,14 @@ let MainTreeCol = {
             tips += `${node.data.annotation.replace(/\n/g,"<br>")}<br>`;
         }
         return tips
+    },
+    // 字体颜色w
+    foreColor: {
+        // 清单综合单价>最高限价时,标红显示
+        'feesIndex.common.unitFee': function (node) {
+            const color = 'red';
+            return calcTools.unitFeeGTMaxPrice(node, 'common.unitFee') ? color : null;
+        }
     }
 };
 
@@ -752,7 +760,5 @@ $('#poj-set').on('hidden.bs.modal', function (e) {
 });
 
 $('#tab_display_setting').on('shown.bs.tab', function () {
-    if(colSettingObj.settingSpread){
-        colSettingObj.settingSpread.refresh();
-    }
+    sheetCommonObj.refreshWorkbookDelDefer(colSettingObj.settingSpread, 100);
 });

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

@@ -517,7 +517,7 @@ $(document).ready(function () {
     });
 
     $('#tab_poj-settings-basicInfo').on('shown.bs.tab', function () {
-        basicInfoView.workBook.refresh();
+        sheetCommonObj.refreshWorkbookDelDefer(basicInfoView.workBook, 100);
     });
     $('#openProjSet').click(function () {
         $('[data-toggle="tooltip"]').tooltip('hide');

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

@@ -367,7 +367,7 @@ $(document).ready(function () {
     });
 
     $('#tab_poj-settings-bqDecimal').on('shown.bs.tab', function () {
-        billsDecimalView.workBook.refresh();
+        sheetCommonObj.refreshWorkbookDelDefer(billsDecimalView.workBook, 100);
     });
 
  /*   $('#property_ok').bind('click', function () {

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

@@ -476,6 +476,6 @@ $(document).ready(function () {
     });
 
     $('#tab_poj-settings-projFeature').on('shown.bs.tab', function () {
-        projFeatureView.workBook.refresh();
+        sheetCommonObj.refreshWorkbookDelDefer(projFeatureView.workBook, 100);
     });
 });

+ 7 - 14
web/building_saas/main/js/views/project_view.js

@@ -626,16 +626,6 @@ var projectObj = {
         let code = node.data.code ? node.data.code : '';
         if(node.sourceType == ModuleNames.bills){//当清单是“分部分项工程”、“措施项目工程”时,要展开清单规则节点
             if(BILLS.isFXorBX(node)||(node.data.type == billType.BILL && BILLS.isMeasure(node))){//是分项或补项,是清单并且属于措施项目节点
-              /*  if(billsLibObj.stdBillsTree === null){
-                    billsLibObj.doAfterLoadBills = function () {
-                        this.locateAtBills(code);
-                        this.doAfterLoadBills = null;
-                    };
-                }
-                else {
-                    billsLibObj.locateAtBills(code);
-                }
-                if(!$("#qd").is(":visible"))  $('#stdBillsTab').click();*/
                 if(!billsGuidance.bills.tree){
                     doAfterLoadGuidance = function () {
                         billsGuidance.locateAtBills(code);
@@ -928,12 +918,13 @@ var projectObj = {
             }
             if (!err) {
                 that.project.projectGLJ.calcQuantity(true);//计算分部分项和技术措施项目消耗量;
-                gljCol.initGljCol(that.project.projSetting.glj_col?that.project.projSetting.glj_col.showAdjustPrice:false);
-                subObj.initSubSpread();//初始化主界面下方的表格
-
                 let quantityTime = +new Date();
                 console.log(`计算quantity时间——${quantityTime - mTime}`);
                 that.project.property = projectObj.project.projectInfo.property;
+                gljCol.initGljCol(that.project.projSetting.glj_col?that.project.projSetting.glj_col.showAdjustPrice:false,
+                    that.project.property.tenderSetting.showTenderFields?that.project.property.tenderSetting.showTenderFields:false);
+                subObj.initSubSpread();//初始化主界面下方的表格
+
                 if (typeof overwriteRationCalcBases === 'function')
                     overwriteRationCalcBases(that.project.property.taxType);
                 //that.project.calcProgram.compileAllTemps();
@@ -2222,7 +2213,9 @@ $('#downLevel').click(function () {
 $('#insertRation').click(function () {
     // projectObj.project.Ration.addNewRation(null,rationType.ration);
     // 连续点工具栏的插入定额按钮,显示树结构画线有问题。
-    projectObj.project.Ration.addNewRationFast(rationType.ration,projectObj.selectColAndFocus);
+    //projectObj.project.Ration.addNewRationFast(rationType.ration,projectObj.selectColAndFocus);
+    //2020-01-08  要限制定额数量,所以还是要用等回传信息的方式插入定额
+    projectObj.project.Ration.addNewRation(null,rationType.ration,projectObj.selectColAndFocus,false);
 });
 $('#upMove').click(function () {
     var controller = projectObj.mainController, project = projectObj.project;

+ 10 - 9
web/building_saas/main/js/views/std_ration_lib.js

@@ -205,14 +205,15 @@ var rationLibObj = {
                 return;
             }
             //提取需要匹配的章节名称
-            //去掉前缀
-            let toMatchArr = sectionName.split(' '),
-                toMatchStr = toMatchArr[toMatchArr.length - 1];
-            //去掉后缀
-            let sectionReg = /\(\w{9,}\)/g,
-                regMatch = toMatchStr.match(sectionReg);
-            if (regMatch) {
-                toMatchStr = toMatchStr.replace(regMatch[regMatch.length - 1], '');
+            // 去掉后缀
+            const suffixReg = /[((]编码[::]\d+[))]/;
+            const tempName = sectionName.split(suffixReg)[0];
+            // 获取第一个空格后的内容
+            const sReg = /\s(.+)/;
+            const tempTarget = tempName.match(sReg);
+            const target = tempTarget ? tempTarget[1] : null;
+            if (!target) {
+                return;
             }
             //简化匹配到的定额名称
             for (let data of datas) {
@@ -225,7 +226,7 @@ var rationLibObj = {
                     continue;
                 }
                 let matchName = nameArr[0];
-                if (matchName === toMatchStr) {
+                if (matchName === target) {
                     nameArr.shift();
                     data.name = nameArr.join(' ');
                 }

+ 9 - 2
web/building_saas/main/js/views/tender_price_view.js

@@ -257,8 +257,11 @@ let tender_obj={
     cleanTenderPrice:function (updateData,node) {//清空调整后报价
         if(node.data.fees){
             for(let i =0; i< node.data.fees.length; i++){
-                node.data.fees[i].tenderUnitFee?updateData.data["fees."+i+'.tenderUnitFee'] = 0:'';
-                node.data.fees[i].tenderTotalFee?updateData.data["fees."+i+'.tenderTotalFee'] = 0:'';
+                // 问题:清空调价后,报表显示为空。解决:清空后仍然要保留调后单价和调后金额,初始化使之与原始综合单价、综合合价相等。
+                // node.data.fees[i].tenderUnitFee?updateData.data["fees."+i+'.tenderUnitFee'] = 0:'';
+                // node.data.fees[i].tenderTotalFee?updateData.data["fees."+i+'.tenderTotalFee'] = 0:'';
+                node.data.fees[i].tenderUnitFee?updateData.data["fees."+i+'.tenderUnitFee'] = node.data.fees[i].unitFee:'';
+                node.data.fees[i].tenderTotalFee?updateData.data["fees."+i+'.tenderTotalFee'] = node.data.fees[i].totalFee:'';
             }
         }
     },
@@ -442,6 +445,8 @@ let tender_obj={
             }
 
             tender_obj.showTenderData();
+            projectObj.project.projectGLJ.calcQuantity();
+            // projectGljObject.refreshViewsData();
         };
         if (tender == tenderTypes.ttReverseGLJ || tender == tenderTypes.ttReverseRation){
             projectObj.project.calcProgram.prepareForDistribute(tender_obj.tenderTree.roots[0]);
@@ -460,6 +465,8 @@ $(function () {
             tender_obj.showTenderData();
             tender_obj.initPageContent();
         }
+        else tender_obj.showTenderData();
+
         autoFlashHeight();
         tender_obj.tenderSpread.refresh();
     });

+ 6 - 6
web/building_saas/pm/html/project-management.html

@@ -250,7 +250,7 @@
                         </div>
                     </div>
                     <span class="form-text text-danger" id="proj-valuation-info" style="display: none;">请选择计价规则</span>
-                    <div class="form-group row">
+                    <div class="form-group row" id="project-dialog-fileKind">
                         <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">
@@ -355,7 +355,7 @@
                             </div>
                         </div>
                         <span class="form-text text-danger" id="valuation-info" style="display: none;">请选择计价规则</span>
-                        <div class="form-group row">
+                        <div class="form-group row" id="tender-dialog-fileKind">
                             <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">
@@ -491,7 +491,7 @@
             <div class="modal-footer">
                 <a href="javascript:void(0);" class="btn btn-primary" id="add-tender-prev" style="display: none">上一步</a>
                 <a href="javascript:void(0);" class="btn btn-primary" id="add-tender-next">下一步</a>
-                <a href="javascript:void(0);" class="btn btn-primary" style="display: none" id="add-tender-confirm">确定</a>
+                <a href="javascript:void(0);" class="btn btn-primary" data-dismiss="modal" style="display: none" id="add-tender-confirm">确定</a>
                 <button type="button" class="btn btn-secondary" data-dismiss="modal" id="add-tender-cancel" style="display: none;">取消</button>
             </div>
         </div>
@@ -593,7 +593,7 @@
 </div>
 <!--弹出复制到-->
 <div class="modal fade" id="copy-to-dialog" data-backdrop="static">
-    <div class="modal-dialog" style="max-width: 650px" role="document">
+    <div class="modal-dialog middle-modal-width" role="document">
         <div class="modal-content">
             <div class="modal-header">
                 <h5 class="modal-title">复制到...</h5>
@@ -603,7 +603,7 @@
             </div>
             <div class="modal-body">
                <!-- <ul id="treeDemo2" class="ztree"></ul>-->
-                <div style="height: 500px" id="copyToSpread"><!--sjs ID设置此div--->
+                <div class="middle-modal-height" id="copyToSpread"><!--sjs ID设置此div--->
                 </div>
             </div>
             <div class="modal-footer">
@@ -650,7 +650,6 @@
                             </div>
                         </div>
                         <!--从本建设项目中选择-->
-                        <label id="project_name">9.21<!--本建设项目名称--></label>
                         <div class="form-group" id = "fromProject">
                             <select class="form-control" id="currentOptions">
                                 <option>测试5单价文件</option><!--单价文件-->
@@ -950,6 +949,7 @@
 <script src="/web/building_saas/js/global.js"></script>
 <script src="/public/web/uuid.js"></script>
 <script src="/public/web/date_util.js"></script>
+<script src="/public/web/upload_cdn.js"></script>
 <script src="/web/building_saas/pm/js/pm_tree.js"></script>
 <script src="/public/web/id_tree.js"></script>
 <script src="/public/web/tree_sheet/tree_sheet_helper.js"></script>

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

@@ -25,10 +25,11 @@ var GetAllProjectData = function (callback) {
 }
 // 更新数据到服务器
 var UpdateProjectData = function (updateData, callback, errCB) {
+    const tenderCount = updateData.filter(item => item.updateType === 'new' && item.updateData.projType === projectType.tender).length;
     $.ajax({
         type:"POST",
         url: '/pm/api/updateProjects',
-        data: {'data': JSON.stringify({"user_id": userID, "updateData": updateData})},
+        data: {'data': JSON.stringify({"user_id": userID, "updateData": updateData, tenderCount})},
         dataType: 'json',
         cache: false,
         timeout: 50000,
@@ -39,7 +40,7 @@ var UpdateProjectData = function (updateData, callback, errCB) {
                 if (errCB) {
                     errCB();
                 }
-                alert('error: ' + result.message);
+                alert(result.message);
             }
         },
         error: function(jqXHR, textStatus, errorThrown){

+ 38 - 51
web/building_saas/pm/js/pm_import.js

@@ -13,11 +13,11 @@ const importView = (() => {
     let xmlObj = null;  //导入xml转化后的对象
     let tbcObj = null;  //待确认对象
     //显示、隐藏提示上传文件窗口相关提示信息
-    function showUploadAlert(success, msg){
-        if(!success){
+    function showUploadAlert(success, msg) {
+        if (!success) {
             $('#uploadAlert').removeClass('alert-success');
             $('#uploadAlert').addClass('alert-danger');
-        } else{
+        } else {
             $('#uploadAlert').removeClass('alert-danger');
             $('#uploadAlert').addClass('alert-success');
         }
@@ -122,7 +122,7 @@ const importView = (() => {
             $('#tbc-tenderName').val(curData.name || '');
             $('#tbc-engineering').val(curData.engineering || '');
             curData.temp.confirmed = true;   //增加确认信息(已读)
-            if (this.curIdx === this.datas.length -1) { //到最后一个数据了,无法点击下一工程
+            if (this.curIdx === this.datas.length - 1) { //到最后一个数据了,无法点击下一工程
                 $('#tbc-next').prop('disabled', true);
             } else {
                 $('#tbc-next').prop('disabled', false);
@@ -150,7 +150,7 @@ const importView = (() => {
             } else {
                 $('#tbc-prev').prop('disabled', false);
             }
-            if (this.curIdx !== this.datas.length -1 ) {    //不为最后一个数据了,可以点击下一工程
+            if (this.curIdx !== this.datas.length - 1) {    //不为最后一个数据了,可以点击下一工程
                 $('#tbc-next').prop('disabled', false);
             } else {
                 $('#tbc-next').prop('disabled', true);
@@ -190,8 +190,8 @@ const importView = (() => {
             //工程专业名称去重
             let engineeringNames = [...new Set(this.engineeringList.map(data => data.lib.name))];
             let engineeringHtml = engineeringNames.map(name =>
-             `<option ${tenderData.temp.engineering === name ? 'selected' : ''} value="${name}">${name}</option>`);
-             $('#tbc-engineering').html(engineeringHtml);
+                `<option ${tenderData.temp.engineering === name ? 'selected' : ''} value="${name}">${name}</option>`);
+            $('#tbc-engineering').html(engineeringHtml);
             //$('#tbc-engineering').html(`<option selected value="${tenderData.engineering}">${tenderData.engineering}</option>`);
             //费用标准,若当前工程有费用标准数据(该数据本身没有费用标准数据,选择过了,就会记录),则选中该费用标准
             let feeOptsHtml = engineerings.map(data =>
@@ -218,12 +218,12 @@ const importView = (() => {
     function getTaxData(query, engineeringList) {
         //工程专业名称 + 费用标准名称 确定一个工程专业数据
         let engineering = engineeringList.find(data => query.engineeringName === data.lib.name &&
-                                                       query.feeStandard === data.lib.feeName);
+            query.feeStandard === data.lib.feeName);
         if (!engineering || !Array.isArray(engineering.lib.tax_group)) {
             return null;
         }
         return engineering.lib.tax_group.find(data => query.taxType == data.taxType &&
-                                                          query.calcProgram === data.program_lib.name);
+            query.calcProgram === data.program_lib.name);
     }
     //获取单位工程项目属性的一些数据(新建单位工程需要,前端能获取到的一些数据,还有一些数据需要后端获取: rootProjectID, projectFeature..)
     /*
@@ -247,7 +247,7 @@ const importView = (() => {
         }
         //当前工程专业数据
         let curEngineering = tbcObj.engineeringList.find(data => query.engineeringName === data.lib.name &&
-        query.feeStandard === data.lib.feeName);
+            query.feeStandard === data.lib.feeName);
         return {
             region: '全省',   //地区
             valuationType: tbcObj.valuationType,    //计价方式
@@ -264,19 +264,19 @@ const importView = (() => {
             indexName: curEngineering.lib.indexName,    // 指标名称
             engineerInfoLibID: curEngineering.lib.engineer_info_lib[0] ? curEngineering.lib.engineer_info_lib[0].id : '',   // 工程信息指标
             engineerFeatureLibID: curEngineering.lib.engineer_feature_lib[0] ? curEngineering.lib.engineer_feature_lib[0].id : '',  //工程特征指标
-            economicLibID: curEngineering.lib.economic_lib[0] ?  curEngineering.lib.economic_lib[0].id : '',    // 主要经济指标
+            economicLibID: curEngineering.lib.economic_lib[0] ? curEngineering.lib.economic_lib[0].id : '',    // 主要经济指标
             mainQuantityLibID: curEngineering.lib.main_quantity_lib[0] ? curEngineering.lib.main_quantity_lib[0].id : '',   // 主要工程量指标
             materialLibID: curEngineering.lib.material_lib[0] ? curEngineering.lib.material_lib[0].id : '', // 主要工料指标
-            calcProgram: {name: taxData.program_lib.name, id: taxData.program_lib.id},  //计算程序
+            calcProgram: { name: taxData.program_lib.name, id: taxData.program_lib.id },  //计算程序
             colLibID: taxData.col_lib.id,   //列设置
             templateLibID: taxData.template_lib.id, //清单模板
-            unitPriceFile: {name: curData.name, id: ''},    //新建单价文件
-            feeFile: {name: curData.name, id: `newFeeRate@@${taxData.fee_lib.id}`}  //新建费率文件
+            unitPriceFile: { name: curData.name, id: '' },    //新建单价文件
+            feeFile: { name: curData.name, id: `newFeeRate@@${taxData.fee_lib.id}` }  //新建费率文件
         };
     }
     function eventListen() {
         //选择文件
-        $('#customFile').change(async function() {
+        $('#customFile').change(async function () {
             let file = $(this)[0].files[0];
             $('#import-confirm').prop('disabled', true);    //确认导入无效
             $('.custom-file-label').text(`${file ? file.name : ''}`);   //设置选择框文本
@@ -285,7 +285,7 @@ const importView = (() => {
             hideTBCInfo();
             if (file) {
                 let reg = /(xml|XML|qtf|QTF)$/;
-                if(file.name && !reg.test(file.name)){
+                if (file.name && !reg.test(file.name)) {
                     $('.selFile').hide();
                     showUploadAlert(false, '请选择xml或qtf文件。');
                     return;
@@ -425,7 +425,7 @@ const importView = (() => {
                     data.property = getProperty(tbcObj, data);
                     //默认定额库
                     let curEngineering = tbcObj.engineeringList.find(enData => data.temp.engineering === enData.lib.name &&
-                    data.temp.feeStandard === enData.lib.feeName);
+                        data.temp.feeStandard === enData.lib.feeName);
                     let defaultLib = curEngineering.lib.ration_lib.find(data => data.isDefault) || curEngineering.lib.ration_lib[0];
                     data.defaultRationLib = parseInt(defaultLib.id);
                     //此费用定额下可用的定额库id,人材机库id
@@ -433,7 +433,7 @@ const importView = (() => {
                     data.gljLibIDs = curEngineering.lib.glj_lib.map(data => parseInt(data.id));
                 });
                 //确定生成建设项目的父、前、后节点ID
-                let {parentProjectID, preProjectID, nextProjectID} = projTreeObj.getRelProjectID(projTreeObj.tree.selected);
+                let { parentProjectID, preProjectID, nextProjectID } = projTreeObj.getRelProjectID(projTreeObj.tree.selected);
                 xmlObj.ParentID = parentProjectID;
                 xmlObj.preID = preProjectID;
                 xmlObj.NextSiblingID = nextProjectID;
@@ -443,44 +443,31 @@ const importView = (() => {
                     xmlObj.name += `(${moment(Date.now()).format('YYYY-MM-DD HH:mm:ss')})`;
                 }
                 $('#importInterface').modal('hide');
-                //TEST======================
-                /*let importData = await importXML.transformData(xmlObj);
-                console.log(importData);*/
-                //TEST======================
                 pr.start('导入文件', '正在生成文件,请稍候……');
                 let importData = await importXML.transformData(xmlObj);
-                let blob = new Blob([JSON.stringify(importData)], {type: 'text/plain;charset=utf-8'});
-                let formData = new FormData();
-                formData.append('file', blob);
-                $.ajax({
-                    url: '/pm/import/importInterface',
-                    type: 'POST',
-                    data: formData,
-                    cache: false,
-                    contentType: false,
-                    processData: false,
-                    timeout: 1000 * 60 * 3, //3分钟
-                    success: function(response){
-                        if (response.data && Array.isArray(response.data)) {
-                            doAfterImport(response.data);
-                        }
-                        pr.end();
-                        setTimeout(function () {
-                            STATE.importing = false;
-                        }, 500);
-                    },
-                    error: function(jqXHR){
-                        pr.end();
-                        throw `与服务器通信发生错误${jqXHR.status} ${jqXHR.statusText}`;
-                    }
-                });
+                let blob = new Blob([JSON.stringify(importData)], { type: 'text/plain;charset=utf-8' });
+                // 转换成File实例
+                const key = `${uuid.v1()}.json`;
+                const file = new File([blob], key);
+                // 上传文件
+                console.time();
+                await projTreeObj.getUploadToken();
+                await UPLOAD_CDN.uploadSync(file, key, projTreeObj.uptoken);
+                // 下载并处理文件
+                const rstData = await ajaxPost('/pm/import/importInterface', { key });
+                console.timeEnd();
+                if (Array.isArray(rstData)) {
+                    doAfterImport(rstData);
+                }
             } catch (err) {
+                console.log(err);
+                alert(err);
+            } finally {
+                projTreeObj.uptoken = null;
                 setTimeout(function () {
                     STATE.importing = false;
                 }, 500);
                 pr.end();
-                console.log(err);
-                alert(err);
             }
         });
         // 导入窗口激活
@@ -515,9 +502,9 @@ const importView = (() => {
                 next = projTreeObj.tree.items.find(node => node.data.ID === data.NextSiblingID);
             lastNode = projTreeObj.insert(data, parent, next);
         }
-        if(lastNode) {
+        if (lastNode) {
             projTreeObj.workBook.getSheet(0).showRow(lastNode.serialNo(), GC.Spread.Sheets.VerticalPosition.center);
         }
     }
-    return {eventListen};
+    return { eventListen };
 })();

+ 38 - 32
web/building_saas/pm/js/pm_newMain.js

@@ -464,6 +464,8 @@ const projTreeObj = {
                             if(selectedItem.children[0] .projType === projectType.project ) return false;//如果文件夹有子项,并且是建设项目,可用
                         }
                     }
+                } else {
+                    return false;
                 }
                 return true;
             },
@@ -712,7 +714,6 @@ const projTreeObj = {
         if(args.sheet.name() == "projectSheet"){
             await projTreeObj.initFileChangePage(projTreeObj.setting.header[args.col].dataCode);
             $("#mr_from").val(projTreeObj.setting.header[args.col].dataCode);
-            $("#project_name").text(projTreeObj.tree.selected.data.name);
             $('#m_replace_file').modal('show');
         }else if(args.sheet.name() == "replaceSheet"){
             let cellType = args.sheet.getCellType(args.row, args.col);
@@ -1354,6 +1355,7 @@ const projTreeObj = {
             datas.push(i.data);
         }
         me.copyTree  = pmTree.createNew(projTreeObj.setting, datas);
+        initNodesVisibility(me.copyTree.items, false);
         me.showTreeData(me.copyTree.items, me.copyToSetting, sheet);
         me.copySelected = null;
         let initSel = sheet.getSelections()[0] ? sheet.getSelections()[0] : {row: 0, rowCount: 1};
@@ -1647,6 +1649,9 @@ const projTreeObj = {
             parent = selectNode.parent;
             next = selectNode.nextSibling;
             updateData["update"] = {query:{ID:selectNode.id()}}
+        } else if (!selectNode) { // 默认在第一位
+            parent = selectNode ? selectNode.parent : projTreeObj.tree._root;
+            next = selectNode ? selectNode.nextSibling : projTreeObj.tree.firstNode();
         }
         updateData["self"] ={ParentID:parent.id(),NextSiblingID:next?next.id():-1};
         return updateData;
@@ -1694,6 +1699,13 @@ function setupRequiredWarn(compilation) {
     }
 }
 $(document).ready(function() {
+    // 单位工程超限提示后,弹出客服列表
+    $('#hintBox_form').on('hide.bs.modal', function () {
+        const text = $('#hintBox_caption').text();
+        if (text === '您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。') {
+            $('#customerService').click();
+        } 
+    });
     setupRequiredWarn(compilationData);
     $('#add-project-dialog').find('.modal-body').append();
     //绑定项目管理的升降级、上下移按钮事件
@@ -1850,12 +1862,26 @@ $(document).ready(function() {
         folderDialog.on('shown.bs.modal', function () {
             $('#folder-name').focus();
         });
+        projDialog.on('show.bs.modal', function () {
+            // 内蒙古费用定额,新建建设项目页面隐藏“文件类型:投标、招标”
+            const filterCompilationNames = ['内蒙古定额(2017)'];
+            if (filterCompilationNames.includes(compilationData.name)) {
+                $('#project-dialog-fileKind').hide();
+            }
+        });
         projDialog.on('shown.bs.modal', function () {
             $('#project-name').focus();
         });
         engDialog.on('shown.bs.modal', function () {
             $('#engineering-name').focus();
         });
+        tenderDialog.on('show.bs.modal', function () {
+            $('#tender-name').focus();
+            const filterCompilationNames = ['内蒙古定额(2017)'];
+            if (filterCompilationNames.includes(compilationData.name)) {
+                $('#tender-dialog-fileKind').hide();
+            }
+        });
         tenderDialog.on('shown.bs.modal', function () {
             $('#tender-name').focus();
         });
@@ -2602,12 +2628,12 @@ $(document).ready(function() {
         projectMap['copy'] = {document:projectData};
         $("#copy-to-dialog").modal('hide');
         $.bootstrapLoading.start();
-        CommonAjax.post('/pm/api/copyProjects',{projectMap:projectMap,user_id: userID},function (result) {
+        CommonAjax.post('/pm/api/copyProjects',{projectMap:projectMap,user_id: userID, tenderCount: 1},function (result) {
             let newNode = projTreeObj.insert(result['copy'].document,parent,next);
             let refreshNodes = projTreeObj.calEngineeringCost(newNode);
             projTreeObj.refreshNodeData(refreshNodes);
             $.bootstrapLoading.end();
-        })
+        }, null, 1000 * 60 * 5);
 
     });
     $('#selectSameTypeProject').click(function(){
@@ -2801,12 +2827,13 @@ function getWorkBookWidth(){
 }
 
 // 初始化节点可见性,默认收起至建设项目层
-function initNodesVisibility(sheet, nodes, visible) {
+function initNodesVisibility(nodes, visible) {
     function recurSetVisible(nodes, visible) {
         nodes.forEach(node => {
             node.visible = visible;
             if (node.children) {
-                recurSetVisible(node.children, visible);            }
+                recurSetVisible(node.children, visible);
+            }
         });
     }
     nodes.forEach(node => {
@@ -2836,7 +2863,7 @@ function initProjects(callback) {
             sheet.name('projectSheet');
             sheetCommonObj.spreadDefaultStyle(projTreeObj.workBook);
             projTreeObj.sumEngineeringCost();
-            initNodesVisibility(sheet, projTreeObj.tree.items, false);
+            initNodesVisibility(projTreeObj.tree.items, false);
             projTreeObj.showTreeData(projTreeObj.tree.items, projTreeObj.setting, sheet);
             //初始选择
             const initSel = sheet.getSelections()[0] ? sheet.getSelections()[0] : { row: 0, rowCount: 1 };
@@ -4690,33 +4717,12 @@ $("#confirm-import").click(function() {
     let file = input.files[0];
     let key = uuid.v1() + ".ybp"; //file.name;
     if (file) {
-        let config = {
-            useCdnDomain: true,
-            disableStatisticsReport: false,
-            retryCount: 6,
-            region: qiniu.region.z2
+        const callback = () => {
+            $("#progress_modal_body").text("正在导入建设项目");
+            startImportProject(key);
+            projTreeObj.uptoken = null;
         };
-        let putExtra = {
-            fname: "",
-            params: {"x:name":key.split(".")[0]},
-            mimeType: null
-        };
-        // 添加上传dom面板
-        let observable = qiniu.upload(file, key, token, putExtra, config);
-        observable.subscribe({
-            next:function (reponse) {
-                console.log(reponse);
-            },
-            error:function (err) {
-                console.log(err);
-            },
-            complete:function(res){
-                console.log("complete")
-                $("#progress_modal_body").text("正在导入建设项目");
-                startImportProject(key);
-                projTreeObj.uptoken = null;
-            }
-        });
+        UPLOAD_CDN.upload(file, key, token, callback);
     }
 
     async function  startImportProject(key) {

+ 2 - 2
web/building_saas/pm/js/pm_share.js

@@ -946,11 +946,11 @@ const pmShare = (function () {
             copyMap.copy = {document: copyData};
             $('#copyShare').modal('hide');
             $.bootstrapLoading.start();
-            CommonAjax.post('/pm/api/copyProjects', {projectMap: copyMap, user_id: userID}, function (rstData) {
+            CommonAjax.post('/pm/api/copyProjects', {projectMap: copyMap, user_id: userID, tenderCount: 1}, function (rstData) {
                 $.bootstrapLoading.end();
             }, function () {
                 $.bootstrapLoading.end();
-            });
+            }, 1000 * 60 * 5);
         });
 
     }

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

@@ -4,7 +4,7 @@
     <% if(controller === 'boot' || controller === 'pm'){ %>
     <!--<a style="text-decoration: none" href="javascript:void(0);" class="header-logo">-->
     <% }else { %>
-    <div class="mx-2"><a href="/pm" class="btn btn-sm" data-toggle="tooltip" title="返回项目管理"><i class="fa fa-angle-left" style="font-size:24px"></i></a></div>
+    <div class="mx-2"><a href="/pm" class="btn btn-sm text-white" data-toggle="tooltip" title="返回项目管理"><i class="fa fa-angle-left" style="font-size:24px"></i></a></div>
         <!--<a style="text-decoration: none" href="/pm" class="header-logo">-->
     <% } %>
     <div class="header-logo">
@@ -18,7 +18,7 @@
         <!--大屏菜单-->
         <ul class="nav navbar-nav" id="fluid-menu">
             <li class="nav-item dropdown" id="showqqgroup" >
-                <a class="nav-link text-danger" href="#"><i class="fa fa-qq" ></i> 群</a>
+                <a class="nav-link" href="#"><i class="fa fa-qq" ></i> 群</a>
                 <div class="dropdown-menu p-3 dropdown-menu-right" id="qqgroup" style="width: 200px;top:36px">
                     <p class="text-center">
                         <a href="https://jq.qq.com/?_wv=1027&k=5XivMJY" target="_blank" class="btn btn-sm btn-outline-primary">点击加入QQ交流群</a>
@@ -30,7 +30,7 @@
                 </div>
             </li>
             <li class="nav-item dropdown">
-                <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" id="link_userName"><%= sessionUser.company ? sessionUser.company : sessionUser.real_name ? sessionUser.real_name : sessionUser.mobile %></a>
+                <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" id="link_userName"><%= (sessionUser.company && sessionUser.real_name) ? (sessionUser.company + '(' + sessionUser.real_name + ')') : sessionUser.company ? sessionUser.company : sessionUser.real_name ? sessionUser.real_name : sessionUser.mobile %></a>
                 <div class="dropdown-menu dropdown-menu-right">
                     <a class="dropdown-item" href="/user/info" target="_blank">企业资料</a>
                     <a class="dropdown-item" href="/user/safe" target="_blank">账号安全</a>
@@ -79,7 +79,7 @@
                 </a>
                 <div class="dropdown-menu dropdown-menu-right">
                     <div class="dropdown dropleft dropdown-submenu">
-                        <button class="dropdown-item dropdown-toggle" type="button" data-toggle="dropdown"><%= sessionUser.company ? sessionUser.company : sessionUser.real_name ? sessionUser.real_name : sessionUser.mobile %></button>
+                        <button class="dropdown-item dropdown-toggle" type="button" data-toggle="dropdown"><%= (sessionUser.company && sessionUser.real_name) ? (sessionUser.company + '(' + sessionUser.real_name + ')') : sessionUser.company ? sessionUser.company : sessionUser.real_name ? sessionUser.real_name : sessionUser.mobile %></button>
                         <div class="dropdown-menu">
                             <a class="dropdown-item" href="/user/info" target="_blank">企业资料</a>
                             <a class="dropdown-item" href="/user/safe" target="_blank">账号安全</a>
@@ -183,7 +183,7 @@
 </div>
 <!--激活产品 & 售后服务 & 联系客服-->
 <!--办事处客服列表-->
-<div class="modal fade" 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;" aria-hidden="true">
     <div class="modal-dialog modal-lg" role="document">
         <div class="modal-content">
             <div class="modal-header">

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

@@ -18,7 +18,7 @@
     <div class="container login-panel">
         <form class="form-signin">
             <h1 class="d-flex justify-content-center mb-0"><img src="/web/building_saas/img/building2.png" width="40" height="40" class="mr-3" style="margin-top: 5px"> 大司空云计价</h1>
-            <p class="text-center mb-4 text-muted">市政、房建、轨道、管廊管线等专业工程计价软件</p>
+            <p class="text-center mb-4 text-muted">市政、房建、轨道、管廊管线等建设工程计价软件</p>
             <p class="mb-1">您关闭了「账号登录」,请通过手机验证码登录。</p>
             <p class="">请点击“获取验证码”,验证码将发送至手机 <%= mobile.substr(0, 3) + '****' + mobile.substr(7, 11) %>,注意查收。</p>
             <div class="form-row sms-login-modal">

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

@@ -18,7 +18,7 @@
     <div class="container login-panel ver-panel">
             <h1 class="d-flex justify-content-center mb-0">
                 <img src="/web/building_saas/img/building2.png" width="40" height="40" class="mr-3" style="margin-top: 5px"> 大司空云计价</h1>
-            <p class="text-center mb-4 text-muted">市政、房建、轨道、管廊管线等专业工程计价软件</p>
+            <p class="text-center mb-4 text-muted">市政、房建、轨道、管廊管线等建设工程计价软件</p>
             <div class="d-flex justify-content-between">
                 <h5 class="modal-title">选择费用定额</h5>
                 <p class=" text-warning"><i class="fa fa-exclamation-triangle"></i> <b>登录设置</b> 中可以修改您的登录习惯。</p>

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

@@ -21,7 +21,7 @@
                 <img src="/web/building_saas/img/building.png" width="40" height="40" class="mr-3" style="margin-top: 5px">
                 大司空云计价
             </h1>
-            <p class="text-center mb-4 text-muted">市政、房建、轨道、管廊管线等专业工程计价软件</p>
+            <p class="text-center mb-4 text-muted">市政、房建、轨道、管廊管线等建设工程计价软件</p>
             <p class="change-login-p">账号登录</p>
             <div class="form-group user-login-modal">
                 <input id="inputEmail" class="form-control " name="inputEmail" placeholder="邮箱/手机" autofocus="" />

+ 2 - 2
web/users/html/user-buy.html

@@ -51,7 +51,7 @@
                             <div class="col-sm-4 mb-5">
                                 <div class="card free-version">
                                   <div class=" card-body">
-                                    <h3 class="card-title">免费版 </h3>
+                                    <h3 class="card-title text-white">免费版 </h3>
                                       <p class="card-text">
                                     <!--  <ul class="pl-3">
                                           <li>只可创建 50 个单位工程</li>
@@ -76,7 +76,7 @@
                             <div class="col-sm-4 mb-5">
                                 <div class="card pro-version">
                                   <div class=" card-body">
-                                    <h3 class="card-title">专业版</h3>
+                                    <h3 class="card-title text-white">专业版</h3>
                                       <p class="card-text">
                                       <!--<ul class="pl-3">
                                           <li>创建单位工程无限制</li>