فهرست منبع

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

TonyKang 6 سال پیش
والد
کامیت
649ba6491c
48فایلهای تغییر یافته به همراه1131 افزوده شده و 501 حذف شده
  1. 1 0
      config/gulpConfig.js
  2. 1 0
      lib/js-base64/base64.min.js
  3. 4 1
      modules/all_models/bills.js
  4. 5 1
      modules/all_models/ration.js
  5. 2 2
      modules/bills_lib/models/bills_lib_interfaces.js
  6. 13 6
      modules/complementary_glj_lib/controllers/gljController.js
  7. 22 58
      modules/complementary_glj_lib/models/gljModel.js
  8. 1 0
      modules/complementary_glj_lib/routes/routes.js
  9. 6 6
      modules/complementary_ration_lib/models/searchModel.js
  10. 2 2
      modules/complementary_ration_lib/models/sectionTreeModel.js
  11. 3 1
      modules/pm/controllers/pm_controller.js
  12. 16 12
      modules/pm/models/project_model.js
  13. 1 1
      modules/ration_glj/facade/ration_glj_facade.js
  14. 2 2
      modules/std_billsGuidance_lib/facade/facades.js
  15. 15 7
      modules/users/controllers/login_controller.js
  16. 1 1
      public/web/common_ajax.js
  17. 0 1
      public/web/sheet/sheet_common.js
  18. 1 1
      public/web/tree_sheet/tree_sheet_helper.js
  19. 4 7
      web/building_saas/complementary_glj_lib/js/components.js
  20. 70 33
      web/building_saas/complementary_glj_lib/js/glj.js
  21. 2 10
      web/building_saas/complementary_glj_lib/js/gljClassTree.js
  22. 14 7
      web/building_saas/complementary_glj_lib/js/gljComponent.js
  23. 1 0
      web/building_saas/complementary_glj_lib/js/sheetOpr.js
  24. 4 0
      web/building_saas/css/custom.css
  25. 88 27
      web/building_saas/main/html/main.html
  26. 42 3
      web/building_saas/main/js/models/calc_program.js
  27. 142 103
      web/building_saas/main/js/models/exportStandardInterface.js
  28. 65 45
      web/building_saas/main/js/models/exportStdInterfaceBase.js
  29. 8 8
      web/building_saas/main/js/models/importStandardInterface.js
  30. 14 0
      web/building_saas/main/js/models/main_consts.js
  31. 8 4
      web/building_saas/main/js/models/project.js
  32. 9 0
      web/building_saas/main/js/models/ration.js
  33. 1 22
      web/building_saas/main/js/models/ration_glj.js
  34. 82 0
      web/building_saas/main/js/views/character_content_view.js
  35. 27 26
      web/building_saas/main/js/views/export_view.js
  36. 4 3
      web/building_saas/main/js/views/glj_view.js
  37. 277 21
      web/building_saas/main/js/views/locate_view.js
  38. 45 3
      web/building_saas/main/js/views/main_tree_col.js
  39. 15 1
      web/building_saas/main/js/views/project_view.js
  40. 25 0
      web/building_saas/main/js/views/side_tools.js
  41. 1 1
      web/building_saas/main/js/views/std_billsGuidance_lib.js
  42. 0 34
      web/building_saas/pm/js/pm_newMain.js
  43. 0 35
      web/building_saas/pm/js/pm_share.js
  44. 53 0
      web/over_write/config/compilation_config.js
  45. 18 0
      web/over_write/js/guangdong_2018.js
  46. 11 1
      web/over_write/js/neimenggu_2017.js
  47. 2 2
      web/users/html/login.html
  48. 3 3
      web/users/js/login.js

+ 1 - 0
config/gulpConfig.js

@@ -69,6 +69,7 @@ module.exports = {
         //'lib/lodash/lodash.js',
         // 'test/tmp_data/test_ration_calc/ration_calc_base.js',
         'web/building_saas/main/js/models/main_consts.js',
+        'web/over_write/config/compilation_config.js',
         'public/web/common_util.js',
         'web/building_saas/glj/js/project_glj.js',
         'web/building_saas/glj/js/composition.js',

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
lib/js-base64/base64.min.js


+ 4 - 1
modules/all_models/bills.js

@@ -75,7 +75,10 @@ let billsSchema = new Schema({
     economicType:String,//工程经济指标类别
     quantityIndexType:String,//工程量指标类别
     quantityIndexUnit:String,//工程量指标单位
-    quantityIndexCoe:Number//单位转换系数
+    quantityIndexCoe:Number,//单位转换系数
+    bookmarkBackground:String,//书签背景色
+    bookmarkAnnotation:String//批注
+
 });
 
 mongoose.model("bills", billsSchema);

+ 5 - 1
modules/all_models/ration.js

@@ -69,6 +69,8 @@ let rationSchema = new Schema({
     ruleText: String,                            // 计算规则
     prefix: {type: String, default: ''},                              //定额是补充、借用时用  补 借
     referenceRationID:String,//如果是通过模板关联子目生成的定额,这里记录对应的主定额ID
+    // 工作内容 (选择自清单)
+    jobContentText: String,
 
     //工料机特有属性
     projectGLJID:Number,  //项目工料机ID
@@ -79,7 +81,9 @@ let rationSchema = new Schema({
     customQuantity:String,//自定义消耗
     model: Number,// 机型
     adjCoe:Number,
-    remark:String
+    remark:String,
+    bookmarkBackground:String,//书签背景色
+    bookmarkAnnotation:String//批注
 });
 
 let ration = mongoose.model("ration", rationSchema, "ration");

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

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

+ 13 - 6
modules/complementary_glj_lib/controllers/gljController.js

@@ -101,12 +101,19 @@ class GljController extends BaseController{
     }
     getGljItems(req, res) {
         let data = JSON.parse(req.body.data);
-        let stdGljLibId = data.stdGljLibId,
-            userId = data.userId,
-            compilationId = data.compilationId
-            gljDao.getGljItems(stdGljLibId, req.session.sessionUser.id, req.session.sessionCompilation._id, function(err, data){
-                callback(req,res,err,'Get Items',data)
-            });
+        let stdGljLibId = data.stdGljLibId;
+        let projection = data.projection;
+        gljDao.getGljItems(stdGljLibId, req.session.sessionUser.id, req.session.sessionCompilation._id, projection, function(err, data){
+            callback(req,res,err,'Get Items',data)
+        });
+    }
+    getStdItems(req, res) {
+        let data = JSON.parse(req.body.data),
+            stdGljLibId = data.stdGljLibId,
+            projection = data.projection;
+        gljDao.getStdItems(stdGljLibId, projection, function (err, data) {
+            callback(req, res, err, '获取人材机数据失败', data);
+        });
     }
 
     updateComponent(req, res){

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

@@ -41,70 +41,25 @@ class GljDao {
         }
     }
     //获得用户的补充工料机和用户当前所在编办的标准工料机
-    async getGljItems (stdGljLibId, userId, compilationId, callback){
+    async getGljItems (stdGljLibId, userId, compilationId, projection, callback){
         let me = this;
         let rst = {stdGljs: [], complementaryGljs: []};
         //批量获取异步
-       /* let functions = [];
-        let count = await stdGljModel.find({repositoryId: stdGljLibId, $or: [{deleted: null}, {deleted: false}]}).count();
-        let findCount = Math.ceil(count/500);
-        for(let i = 0, len = findCount; i < len; i++){
-            functions.push((function(flag) {
-                return function (cb) {
-                    stdGljModel.find({repositoryId: stdGljLibId, deleted: null}, cb).skip(flag).sort({ID: 1}).limit(500);
+        async.parallel([
+            async function (cb) {
+                try{
+                    let stdGljs = await stdGljModel.find({repositoryId: stdGljLibId}, projection).lean();
+                    me.sortToNumber(stdGljs);
+                    rst.stdGljs = stdGljs;
+                    cb(null);
                 }
-            })(i*500));
-        }
-        async.parallel(functions,  function (err, results) {
-            if(err){
-                callback(err, null);
-            }
-            else{
-                for(let stdGljs of results){
-                    rst.stdGljs = rst.stdGljs.concat(stdGljs);
+                catch (err){
+                    cb(err);
                 }
-                callback(0, rst);
-            }
-        });*/
-        async.parallel([
-           async function (cb) {
-               try{
-                   let stdGljs = [];
-                   let first = await stdGljModel.find({repositoryId: stdGljLibId}).sort({ID: 1}).limit(1);
-                   let count = await stdGljModel.find({repositoryId: stdGljLibId, $or: [{deleted: null}, {deleted: false}]}).count();
-                   let findCount = Math.ceil(count/500);
-                   let flag = first[0].ID;
-                   //let flag = 0;
-                   //批量获取,非skip
-                   for(let i = 0, len = findCount; i < len; i++){
-                       let tempStdGlj;
-                       if(i === 0){
-                           tempStdGlj = await stdGljModel.find({repositoryId: stdGljLibId, deleted: null, ID: {$gte: flag}}).sort({ID: 1}).limit(500);
-                           if(tempStdGlj.length > 0){
-                               flag = tempStdGlj[tempStdGlj.length - 1].ID;
-                           }
-                       }
-                       else {
-                           tempStdGlj = await stdGljModel.find({repositoryId: stdGljLibId, deleted: null, ID: {$gt: flag}}).sort({ID: 1}).limit(500);
-                           if(tempStdGlj.length > 0){
-                               flag = tempStdGlj[tempStdGlj.length - 1].ID;
-                           }
-                       }
-                       if(tempStdGlj){
-                           stdGljs = stdGljs.concat(tempStdGlj);
-                       }
-                   }
-                   me.sortToNumber(stdGljs);
-                   rst.stdGljs = stdGljs;
-                   cb(null);
-               }
-               catch (err){
-                   cb(err);
-               }
 
             },
             function (cb) {
-                complementaryGljModel.find({userId: userId, compilationId: compilationId}, function (err, complementaryGljs) {
+                complementaryGljModel.find({userId: userId, compilationId: compilationId}, '-_id', {lean: true}, function (err, complementaryGljs) {
                     if(err){
                         cb(err);
                     }
@@ -126,6 +81,15 @@ class GljDao {
 
     };
 
+    async getStdItems (stdGljLibId, projection, callback) {
+        try {
+            let stdItems = await stdGljModel.find({repositoryId: stdGljLibId}, projection).lean();
+            callback(0, stdItems);
+        } catch (err) {
+            callback(1, null);
+        }
+    }
+
     getGljItemsByCode (repositoryId, codes, callback){
         gljModel.find({"repositoryId": repositoryId,"code": {"$in": codes}},function(err,data){
             if(err) callback(true, "")
@@ -339,8 +303,8 @@ class GljDao {
 
     async getMixedTree(gljLibId, userId, compilationId){
         let rst = {std: [], comple: []};
-        rst.std = await gljClassModel.find({repositoryId: gljLibId});
-        rst.comple = await compleClassModel.find({userId: userId, compilationId: compilationId});
+        rst.std = await gljClassModel.find({repositoryId: gljLibId}).lean();
+        rst.comple = await compleClassModel.find({userId: userId, compilationId: compilationId}).lean();
         return rst;
     }
 }

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

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

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

@@ -127,9 +127,9 @@ class SearchDao{
             //搜索定额
             let stdGljIds = [],
                 comGljIds = [];
-            let stdRations = rationRepId.length === 0 ? [] : await stdRationModel.find(filter).sort({code: 1}).skip(skip.std).limit(limit);
+            let stdRations = rationRepId.length === 0 ? [] : await stdRationModel.find(filter).lean().sort({code: 1}).skip(skip.std).limit(limit);
             for(let i = 0, len = stdRations.length; i < len; i++){
-                stdRations[i]._doc.type = 'std';
+                stdRations[i].type = 'std';
                 for(let glj of stdRations[i].rationGljList){
                     stdGljIds.push(glj.gljId);
                 }
@@ -137,9 +137,9 @@ class SearchDao{
             let compleRations = [];
             let residueLimit = limit - stdRations.length;
             if (residueLimit > 0) {
-                compleRations = findCompleRtion ? await compleRationModel.find(compleFilter).sort({code: 1}).skip(skip.comple).limit(residueLimit) : [];
+                compleRations = findCompleRtion ? await compleRationModel.find(compleFilter).lean().sort({code: 1}).skip(skip.comple).limit(residueLimit) : [];
                 for(let i = 0, len = compleRations.length; i <len; i++){
-                    compleRations[i]._doc.type = 'complementary';
+                    compleRations[i].type = 'complementary';
                     for(let glj of compleRations[i].rationGljList){
                         if(glj.type === 'std'){
                             stdGljIds.push(glj.gljId);
@@ -207,7 +207,7 @@ class SearchDao{
                     hintsArr.push(`附注:`);
                     hintsArr = hintsArr.concat(ration.annotation.split('\n'));
                 }
-                ration._doc.hint = hintsArr.join('<br>');
+                ration.hint = hintsArr.join('<br>');
             }
             for(let ration of compleRations){
                 let hintsArr = [];
@@ -226,7 +226,7 @@ class SearchDao{
                     hintsArr.push(`附注:`);
                     hintsArr = hintsArr.concat(ration.annotation.split('\n'));
                 }
-                ration._doc.hint = hintsArr.join('<br>');
+                ration.hint = hintsArr.join('<br>');
             }
             rst.data = stdRations.concat(compleRations);
             callback(0, rst);

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

@@ -57,9 +57,9 @@ class SectionTreeDao {
         try {
             let treeData;
             if (type === rationLibType.complementary) {
-                treeData = await compleRationSectionTreeModel.find({userId: userId, compilationId: compilationId, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]});
+                treeData = await compleRationSectionTreeModel.find({userId: userId, compilationId: compilationId, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]}).lean();
             } else {
-                treeData = await stdSectionTreeModel.find({rationRepId: rationRepId});
+                treeData = await stdSectionTreeModel.find({rationRepId: rationRepId}).lean();
             }
             callback(0, treeData);
         } catch (err) {

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

@@ -80,7 +80,7 @@ module.exports = {
     },
     updateProjects: async function (req, res) {
         let data = JSON.parse(req.body.data);
-        await ProjectsData.updateUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, req.session.sessionCompilation.name, data.updateData, function (err, message, data) {
+        await ProjectsData.updateUserProjects(req.session.sessionUser.id, req.session.sessionCompilation._id, req.session.sessionCompilation.name,req.session.sessionCompilation.overWriteUrl, data.updateData, function (err, message, data) {
             if (err === 0) {
                 callback(req, res, err, message, data);
             } else {
@@ -641,6 +641,8 @@ module.exports = {
                             } else if (projC.projType === projType.tender) {
                                 //设置工程专业
                                 projC._doc.feeStandardName = projC.property.feeStandardName || '';
+                                //设置计税方法
+                                projC._doc.taxType = projC.property.taxType;
                                 if (proj.projType === projType.engineering) {
                                     ungroupedTenders.push(projC._doc);
                                 }

+ 16 - 12
modules/pm/models/project_model.js

@@ -67,7 +67,7 @@ ProjectsDAO.prototype.getUserProjects = async function (userId, compilation, cal
                 'compilation': compilation,
                 'deleteInfo': null
             }, {'userID': userId, 'compilation': compilation, 'deleteInfo.deleted': {'$in': [null, false]}}]
-        }, '-_id');
+        }, '-_id', {lean: true});
         let projIDs= [];
         for(let project of projects){
             if(project.projType === projectType.project){
@@ -79,16 +79,16 @@ ProjectsDAO.prototype.getUserProjects = async function (userId, compilation, cal
         for(let proj of projects){
             let summaryProj = summaryInfo[proj.ID];
             if(summaryProj){
-                proj._doc.engineeringCost = summaryProj.engineeringCost;
-                proj._doc.subEngineering = summaryProj.subEngineering;
-                proj._doc.measure = summaryProj.measure;
-                proj._doc.safetyConstruction = summaryProj.safetyConstruction;
-                proj._doc.other = summaryProj.other;
-                proj._doc.charge = summaryProj.charge;
-                proj._doc.tax = summaryProj.tax;
-                proj._doc.rate = summaryProj.rate;
-                proj._doc.buildingArea = summaryProj.buildingArea;
-                proj._doc.perCost = summaryProj.perCost;
+                proj.engineeringCost = summaryProj.engineeringCost;
+                proj.subEngineering = summaryProj.subEngineering;
+                proj.measure = summaryProj.measure;
+                proj.safetyConstruction = summaryProj.safetyConstruction;
+                proj.other = summaryProj.other;
+                proj.charge = summaryProj.charge;
+                proj.tax = summaryProj.tax;
+                proj.rate = summaryProj.rate;
+                proj.buildingArea = summaryProj.buildingArea;
+                proj.perCost = summaryProj.perCost;
             }
         }
         callback(0, '', projects);
@@ -112,7 +112,7 @@ ProjectsDAO.prototype.getUserProject = function (userId, ProjId, callback) {
     });
 };
 
-ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId, compilationName, datas, callback) {
+ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId, compilationName,overWriteUrl, datas, callback) {
     let data, project, updateLength = 0, hasError = false, deleteInfo = null, i, newProject;
     let updateAll = function (err) {
         if (!err) {
@@ -174,6 +174,10 @@ ProjectsDAO.prototype.updateUserProjects = async function (userId, compilationId
                     data.updateData.property.displaySetting = displaySetting;
 
                     data.updateData.property.billsCalcMode = 0;
+                    if(overWriteUrl && overWriteUrl !=""){
+                        let overWrite = require("../../.."+overWriteUrl);
+                        if(overWrite.getBillsCalcMode) data.updateData.property.billsCalcMode = overWrite.getBillsCalcMode();//重写清单计费取费方式
+                    }
                     data.updateData.property.zanguCalcMode = 0;
                     //计算选项
                     data.updateData.property.calcOptions = calcOptions;

+ 1 - 1
modules/ration_glj/facade/ration_glj_facade.js

@@ -556,7 +556,7 @@ function getGLJData(info, callback) {
             }
         },
         function (cb) {
-            gljDao.getGljItems(info.gljLibId, info.userID, info.compilationId, function (err, data) {
+            gljDao.getGljItems(info.gljLibId, info.userID, info.compilationId, {_id: 0}, function (err, data) {
                 if (err) {
                     cb(err);
                 } else {

+ 2 - 2
modules/std_billsGuidance_lib/facade/facades.js

@@ -40,7 +40,7 @@ async function getLibWithBills(libID){
     if(!billsLib){
         throw '引用的清单规则库不存在!';
     }
-    let bills = await stdBillsModel.find({billsLibId: billsLib.billsLibId}, '-_id');
+    let bills = await stdBillsModel.find({billsLibId: billsLib.billsLibId}, '-_id').lean();
     return {guidanceLib: guidanceLib[0], bills};
 }
 
@@ -142,7 +142,7 @@ async function getItemsByCode(guidanceLibID, code){
     if (!guidanceLib) {
         return rst;
     }
-    let stdBills = await stdBillsModel.findOne({code: code, billsLibId: guidanceLib.billsLibId});
+    let stdBills = await stdBillsModel.findOne({code: code, billsLibId: guidanceLib.billsLibId}).lean();
     if (!stdBills) {
         return rst;
     }

+ 15 - 7
modules/users/controllers/login_controller.js

@@ -131,6 +131,7 @@ class LoginController {
      * @return {string}
      */
     async login(request, response) {
+        console.log("开始登录操作------------------");
         let preferenceSetting = {};
         let compilationList = [];
         try {
@@ -156,12 +157,14 @@ class LoginController {
                     throw '短信验证码错误或已过期。';
                 }
                 responseData = await userModel.getInfoFromSSOMobile(mobile);
+                console.log("getInfoFromSSOMobile  complete------------------");
             } else {
                 let account = request.body.account;
                 let password = request.body.pw;
 
                 // 调用接口验证登录信息
                 responseData = await userModel.getInfoFromSSO(account, password);
+                console.log("getInfoFromSSO  complete------------------");
             }
 
             // 先判断返回值是否为未激活状态
@@ -215,15 +218,16 @@ class LoginController {
             // }
 
             // 判断极验验证码是否通过
-            const captcha = new Captcha();
-            const captchResult = await captcha.validate(request);
-            console.log(captchResult);
-            if (!captchResult) {
-                throw '极验验证码错误';
-            }
+            // const captcha = new Captcha();
+            // const captchResult = await captcha.validate(request);
+            // console.log(captchResult);
+            // if (!captchResult) {
+            //     throw '极验验证码错误';
+            // }
 
             // 判断用户是否开启了只使用短信登录
             const userInfo = await userModel.findDataByAccount(userData.mobile);
+            console.log("判断用户是否开启了只使用短信登录 完成------------------");
             if (request.body.mobile === undefined && request.body.code === undefined && userInfo !== undefined && userInfo !== null && userInfo.isSmsLogin === 1) {
                 return response.json({error: 3, msg: '只能手机短信登录。', data: userData.mobile});
             }
@@ -247,10 +251,11 @@ class LoginController {
             request.session.sessionUser = sessionUser;
             // 记录用户数据到数据库
             let result = await userModel.markUser(sessionUser, request);
-
+            console.log("markUser 完成------------------");
             // 获取偏好设置
             let settingModel = new SettingModel();
             preferenceSetting = await settingModel.getPreferenceSetting(request.session.sessionUser.id);
+            console.log("获取偏好设置 完成------------------");
             if (!result) {
                 throw '标记用户信息失败!';
             }
@@ -258,6 +263,7 @@ class LoginController {
             if(preferenceSetting.login_ask === 1 || preferenceSetting.select_version === ''){
                 preferenceSetting.login_ask = 1;
                 compilationList = await  compilationModel.getList();
+                console.log("compilationList 完成------------------");
             }
             else{
                 compilationList = [];
@@ -270,6 +276,7 @@ class LoginController {
                 let compilationData = await compilationModel.getCompilationById(preferenceSetting.select_version);
                 // 判断当前用户的是使用免费版还是专业版
                 let compilationVersion = await userModel.getVersionFromUpgrade(sessionUser.ssoId, preferenceSetting.select_version);
+                console.log("当前用户的是使用免费版还是专业版 完成------------------");
                 request.session.compilationVersion = compilationVersion;
                 request.session.sessionCompilation = compilationData;
                 if(request.session.sessionUser.latest_used !== preferenceSetting.select_version) await userModel.updateLatestUsed(request.session.sessionUser.id,preferenceSetting.select_version);
@@ -300,6 +307,7 @@ class LoginController {
                     const Sms = new SMS();
                     const logInfo = loginList[0];
                     await Sms.sendLoginMsg(userData.mobile, request.session.sessionUser.real_name, moment(logInfo.create_time).format('YYYY-MM-DD'), moment(logInfo.create_time).format('HH:mm:ss'), logInfo.message.ip_info, logInfo.message.ip);
+                    console.log("sendLoginMsg 完成------------------");
                     // const msg = '【纵横云计价】您的账号(账号昵称:'+ request.session.sessionUser.real_name +')于' + moment(logInfo.create_time).format('YYYY-MM-DD HH:mm:ss') + '在' + logInfo.message.ip_info + '(' + logInfo.message.ip + ')' + '登录成功。纵横云计价提醒您,如果怀疑此次登录行为有异常,请及时修改账号密码。';
                     // console.log(msg);
                 }

+ 1 - 1
public/web/common_ajax.js

@@ -173,7 +173,7 @@ async function ajaxPost(url, data) {
             cache: false,
             timeout: 50000,
             success: function(result){
-                if (result.error === 0 || result.err ===0) {
+                if (!result.error || !result.err) {
                     resolve(result.data);
                 } else {
                     alert('error: ' + result.message);

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

@@ -852,7 +852,6 @@ var sheetCommonObj = {
         ComboCellForActiveCell.prototype.paintValue = function (ctx, value, x, y, w, h, style, options) {
             let sheet = options.sheet;
             if (options.row === sheet.getActiveRowIndex() && options.col === sheet.getActiveColumnIndex() && (!forLocked || forLocked && !sheet.getCell(options.row, options.col).locked())) {
-
                 GC.Spread.Sheets.CellTypes.ComboBox.prototype.paintValue.apply(this, arguments);
 
             } else {

+ 1 - 1
public/web/tree_sheet/tree_sheet_helper.js

@@ -202,7 +202,7 @@ var TREE_SHEET_HELPER = {
                         cell.locked(true);
                     }else {
                         if (Object.prototype.toString.apply(colSetting.readOnly) === "[object Function]") {
-                            cell.locked(colSetting.readOnly(node));
+                            cell.locked(colSetting.readOnly(node, colSetting.data.field));
                         } else {
                             cell.locked(true);
                         }

+ 4 - 7
web/building_saas/complementary_glj_lib/js/components.js

@@ -88,9 +88,7 @@ let componentOprObj = {
                         }
                     }
                     if(!isExist){
-                        if(clearChecked){
-                            gljList[i].isChecked = false;
-                        }
+                        gljList[i].isChecked = false;
                     }
                     else {
                         gljList[i].isChecked = true;
@@ -224,7 +222,6 @@ let componentOprObj = {
                     cacheSection.push(data[i]);
                 }
             }
-            sheetOpr.cleanSheet(me.workBook.getSheet(0), me.setting, -1);
             sheetOpr.showData(me.workBook.getSheet(0), me.setting, cacheSection, re.distTypeTree);
             me.workBook.getSheet(0).setRowCount(cacheSection.length);
             cacheSection = null;
@@ -283,13 +280,13 @@ let componentTypeTreeOprObj = {
     }
 }
 $(document).ready(function () {
-    $('#gljSearchKeyword').change(function () {
+    $('#gljSearchKeyword').bind('keyup', function () {
         componentOprObj.filterDatasAndShow();
     });
-    $('#gljSearchKeyword').bind('keypress', function (e) {
+    /*$('#gljSearchKeyword').bind('keypress', function (e) {
         if(e.keyCode === 13){
             $(this).blur();
             return false;
         }
-    });
+    });*/
 });

+ 70 - 33
web/building_saas/complementary_glj_lib/js/glj.js

@@ -60,8 +60,10 @@ let pageOprObj = {
         //repositoryGljObj.getRationGljIds(gljLibId);
         repositoryGljObj.getGljDistType(function () {
             repositoryGljObj.currentRepositoryId = me.stdGljLibId;
-            repositoryGljObj.getGljItems(me.stdGljLibId, function () {
-                gljClassTreeObj.getGljClassTree(stdGljLibId);
+            gljClassTreeObj.getGljClassTree(stdGljLibId, function () {
+                repositoryGljObj.getGljItems(me.stdGljLibId, function () {
+                    gljClassTreeObj.initSelection(gljClassTreeObj.tree.selected);
+                });
             });
         });
     }
@@ -70,6 +72,10 @@ let pageOprObj = {
 let repositoryGljObj = {
     treeObj : null,
     workBook: null,
+    // 是否获取了完整的人材机数据
+    // 第一次打开页面只加载补充人材机和标准人材机的编码
+    // 第一次打开选择组成物的窗口才加载标准人材机剩余数据
+    pullCompleteData: false,
     gljCurTypeId: -1,
     currentRepositoryId: -1,
     currentCache: null,
@@ -174,9 +180,19 @@ let repositoryGljObj = {
         });
     },
     getGljItems: function(stdGljLibId, callback) {
-        console.log('enterGG');
-        let me = this;
-        CommonAjax.post('complementartGlj/api/getGljItems', {stdGljLibId: stdGljLibId}, function (rstData) {
+        let me = this,
+            // 一开始获取的标准人材机只包含code,提升打开页面速度
+            projection = {
+                _id: 0,
+                ID: 1,
+                code: 1,
+                unit: 1,
+                name: 1,
+                priceProperty: 1,
+                basePrice: 1
+            };
+        $.bootstrapLoading.start();
+        CommonAjax.post('/complementartGlj/api/getGljItems', {stdGljLibId, projection}, function (rstData) {
             me.stdGljList = rstData.stdGljs;
             //兼容多单价情况
             for(let sGlj of me.stdGljList){
@@ -184,14 +200,44 @@ let repositoryGljObj = {
                     sGlj.basePrice = sGlj.priceProperty.price1;
                 }
             }
-            me.complementaryGljList = rstData.complementaryGljs;
-            me.workBook.getSheet(0).setRowCount(me.stdGljList.length);
             me.sortGlj(me.stdGljList);
             me.setProp('isStd', true, me.stdGljList);
+            me.complementaryGljList = rstData.complementaryGljs;
             me.sortGlj(me.complementaryGljList);
             if(callback){
                 callback();
             }
+            $.bootstrapLoading.end();
+        }, function () {
+            $.bootstrapLoading.end();
+        });
+    },
+    // 获取标准人材机数据
+    getStdItems: function (stdGljLibId, callback) {
+        $.bootstrapLoading.start();
+        let me = this;
+        let projection = {
+            _id: 0,
+            ID: 1,
+            specs: 1,
+            gljType: 1,
+            shortName: 1,
+            gljClass: 1
+        };
+        CommonAjax.post('/complementartGlj/api/getStdItems', {stdGljLibId, projection}, function (data) {
+            me.pullCompleteData = true;
+            // 更新标准人材机数据
+            let IDMapping = {};
+            for (let glj of data) {
+                IDMapping[glj.ID] = glj;
+            }
+            for (let glj of me.stdGljList) {
+                Object.assign(glj, IDMapping[glj.ID]);
+            }
+            callback();
+            $.bootstrapLoading.end();
+        }, function () {
+            $.bootstrapLoading.end();
         });
     },
     showGljItems: function(data, type) {
@@ -297,32 +343,23 @@ let repositoryGljObj = {
     },
 
     getCurrentComponent: function (gljComponent) {
-        let me = repositoryGljObj, rst = [];
-        for(let i = 0; i < gljComponent.length; i++){
-            let obj = {};
-            for(let j = 0; j < me.complementaryGljList.length; j++){
-                if(gljComponent[i].ID == me.complementaryGljList[j].ID){
-                    obj.isStd = false;
-                    obj.ID = me.complementaryGljList[j].ID;
-                    obj.code = me.complementaryGljList[j].code;
-                    obj.name = me.complementaryGljList[j].name;
-                    obj.unit = me.complementaryGljList[j].unit;
-                    obj.basePrice = me.complementaryGljList[j].basePrice;
-                    obj.consumeAmt = gljComponent[i].consumeAmt;
-                    rst.push(obj);
-                }
-            }
-            for(let j = 0; j < me.stdGljList.length; j++){
-                if(gljComponent[i].ID == me.stdGljList[j].ID){
-                    obj.isStd = true;
-                    obj.ID = me.stdGljList[j].ID;
-                    obj.code = me.stdGljList[j].code;
-                    obj.name = me.stdGljList[j].name;
-                    obj.unit = me.stdGljList[j].unit;
-                    obj.basePrice = me.stdGljList[j].basePrice;
-                    obj.consumeAmt = gljComponent[i].consumeAmt;
-                    rst.push(obj);
-                }
+        let me = repositoryGljObj,
+            rst = [];
+        for(let thisGlj of gljComponent){
+            let toFindGljList = thisGlj.isStd
+                ? me.stdGljList
+                : me.complementaryGljList;
+            let matchGlj = toFindGljList.find(glj => glj.ID === thisGlj.ID);
+            if (matchGlj) {
+                rst.push({
+                    isStd: thisGlj.isStd ? true : false,
+                    ID: matchGlj.ID,
+                    code: matchGlj.code,
+                    name: matchGlj.name,
+                    unit: matchGlj.unit,
+                    basePrice: matchGlj.basePrice,
+                    consumeAmt: thisGlj.consumeAmt
+                });
             }
         }
         rst.sort(function (a, b) {

+ 2 - 10
web/building_saas/complementary_glj_lib/js/gljClassTree.js

@@ -119,11 +119,6 @@ let gljClassTreeObj = {
         let sucFunc = function (rstData) {
             me.treeData = rstData;
             let compleTreeData = me.treeData.comple;
-            /*zTreeHelper.createTree(rstData.std, componentSetting, "componentTree", componentOprObj);
-            let rootNode = componentOprObj.treeObj.getNodes()[0];
-            if(rootNode && rootNode.isParent && rootNode.isFirstNode){
-                componentOprObj.rootNode = rootNode;
-            }*/
             if (compleTreeData && compleTreeData.length > 0) {
                 me.gljCurTypeId = compleTreeData[0].ID;
             }
@@ -135,15 +130,12 @@ let gljClassTreeObj = {
             me.initController(me.tree, me.sheet, me.setting.sheet);
             me.controller.showTreeData();
             me.sheet.setFormatter(-1, 0, '@');
-            me.initSelection(me.tree.selected);
+            //me.initSelection(me.tree.selected);
             if(callback){
                 callback();
             }
         };
-        let errFunc = function () {
-
-        };
-        CommonAjax.post(url, postData, sucFunc, errFunc);
+        CommonAjax.post(url, postData, sucFunc);
     },
 
     initTree: function (datas) {

+ 14 - 7
web/building_saas/complementary_glj_lib/js/gljComponent.js

@@ -91,13 +91,20 @@ let gljComponentOprObj = {
                         callback: function(){},
                         items: {
                             "insert": {name: "插入", disabled: insertDis, icon: "fa-sign-in", callback: function (key, opt) {
-                                //默认radio所有工料机
-                                co.initRadio();
-                                co.gljCurTypeId = null;
-                                //默认点击树根节点
-                                co.initClassTree('std', gljClassTreeObj.treeData.std);
-                                //弹出窗口
-                                $('#component').modal('show');
+                                let oprFunc = function () {
+                                    //默认radio所有工料机
+                                    co.initRadio();
+                                    co.gljCurTypeId = null;
+                                    //默认点击树根节点
+                                    co.initClassTree('std', gljClassTreeObj.treeData.std);
+                                    //弹出窗口
+                                    $('#component').modal('show');
+                                };
+                                if (repositoryGljObj.pullCompleteData) {
+                                    oprFunc();
+                                } else {
+                                    repositoryGljObj.getStdItems(pageOprObj.stdGljLibId, oprFunc);
+                                }
                             }},
                             "delete": {name: "删除", disabled: delDis, icon: "fa-remove", callback: function (key, opt) {
                                 //删除

+ 1 - 0
web/building_saas/complementary_glj_lib/js/sheetOpr.js

@@ -43,6 +43,7 @@ let sheetOpr = {
         spreadBook.options.allowCopyPasteExcelStyle = false;
         spreadBook.options.allowExtendPasteRange = true;
         spreadBook.options.allowUserDragDrop = false;
+        spreadBook.options.allowUserDragFill = false;
         spreadBook.options.allowContextMenu = false;
         var spreadNS = GC.Spread.Sheets;
         var sheet = spreadBook.getSheet(0);

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

@@ -340,4 +340,8 @@ input.text-right{
     100% {
         transform: translateX(4%)
     }
+}
+/*占位底色*/
+.occupied {
+    background: #f1f1f1;
 }

+ 88 - 27
web/building_saas/main/html/main.html

@@ -42,6 +42,7 @@
         let projectReadOnly = JSON.parse('<%- projectReadOnly %>');
         let projectCooperate = JSON.parse('<%- projectCooperate %>');
         let projectOptins =  JSON.parse('<%- options %>');
+        const overWriteUrl = '<%- overWriteUrl %>';
         console.log(projectCooperate);
         const G_SHOW_BLOCK_LIB = true;
 //        const G_SHOW_BLOCK_LIB = false;
@@ -525,10 +526,10 @@
                                                   <input class="form-check-input" type="radio" name="content_type" id="raion_glj" value="ration_glj">
                                                   <label class="form-check-label" for="raion_glj">人材机</label>
                                               </div>
-                                              <!--<div class="form-check form-check-inline">
-                                                  <input class="form-check-input" type="radio" name="inlineRadioOptions" id="bookmark" value="option4">
-                                                  <label class="form-check-label" for="bookmark">书签注</label>
-                                              </div>-->
+                                              <div class="form-check form-check-inline">
+                                                  <input class="form-check-input" type="radio" name="content_type" id="bookmark" value="bookmark">
+                                                  <label class="form-check-label" for="bookmark">书签注</label>
+                                              </div>
                                           </div>
                                           <!--搜索分项/清单 出现-->
                                           <div class="col-12"  id="outstandingOptions">
@@ -539,36 +540,94 @@
                                           </div>
                                       </div>
                                   </div>
-                                  <!--上下结构-->
-                                  <div class="top-content" id="locateTopDiv" style="overflow: hidden">
-                                      <div class="" id="locate_result" >
+                                  <div id="aboutLocateDiv">
+                                      <!--上下结构-->
+                                      <div class="top-content" id="locateTopDiv" style="overflow: hidden">
+                                          <div class="" id="locate_result" >
+                                          </div>
+                                      </div>
+                                      <div class="resize-y" id="locate_resize"></div>
+                                      <div class="bottom-content" id="locateBottomDiv">
+                                          <div class="" id="locate_sub"></div>
                                       </div>
                                   </div>
-                                  <div class="resize-y" id="locate_resize"></div>
-                                  <div class="bottom-content" id="locateBottomDiv">
-                                      <div class="" id="locate_sub"></div>
-                                  </div>
-                                <!--  <div class="main-data-side-y">
-                                      <table class="table table-sm table-bordered">
-                                          <tr><th>编码</th><th>项目名称</th><th>工程量</th><th>单位</th><th>综合单价</th><th>综合合价</th></tr>
-                                      </table>
-                                  </div>
-                                  <div class="sidebar-bottom container-fluid">
-                                      <div class="row">
-                                          <div class="col-lg-12">
-                                              <table class="table table-sm table-bordered">
-                                                  <tr><th>定额编号</th><th>定额名称</th></tr>
-                                                  <tr><td>AA0047</td><td>人工沟槽 较硬岩 槽深4m以内</td></tr>
-                                                  <tr><td>AA0005</td><td>人工挖沟槽土方 槽深4m以内</td></tr>
-                                              </table>
+                                  <div id="aboutBookmarkDiv" style="display: none">
+                                      <!--上下结构-->
+                                      <div id="bookmarkTopDiv">
+                                          <div class="sidebar-tools-bar container-fluid tools-bar-height-q">
+                                              <div class="p-1 row" style="padding-top:0px!important">
+                                                  <div class="dropdown" id="bookmarkSettingDropdown">
+                                                      <button class="btn btn-sm btn-primary dropdown-toggle" type="button" data-toggle="dropdown">
+                                                          书签设置
+                                                      </button>
+                                                      <form class="dropdown-menu p-2" id = "bookmarkSettingForm" onsubmit="return false;">
+                                                          <input type="hidden" id="bookmarkSelected">
+                                                          <div id="bookmarkSettingList">
+                                                              <div class="input-group input-group-sm mb-2" style="width:200px">
+                                                                  <div class="input-group-prepend">
+                                                                      <span class="input-group-text annotate-color-1">&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                                                  </div>
+                                                                  <input type="text" class="form-control" placeholder="描述">
+                                                              </div>
+                                                              <div class="input-group input-group-sm mb-2">
+                                                                  <div class="input-group-prepend">
+                                                                      <span class="input-group-text annotate-color-2">&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                                                  </div>
+                                                                  <input type="text" class="form-control" id="E2F2C5" placeholder="描述">
+                                                              </div>
+                                                              <div class="input-group input-group-sm mb-2">
+                                                                  <div class="input-group-prepend">
+                                                                      <span class="input-group-text annotate-color-3">&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                                                  </div>
+                                                                  <input type="text" class="form-control" placeholder="描述">
+                                                              </div>
+                                                              <div class="input-group input-group-sm mb-2">
+                                                                  <div class="input-group-prepend">
+                                                                      <span class="input-group-text annotate-color-4">&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                                                  </div>
+                                                                  <input type="text" class="form-control" placeholder="描述">
+                                                              </div>
+                                                              <div class="input-group input-group-sm mb-2">
+                                                                  <div class="input-group-prepend">
+                                                                      <span class="input-group-text annotate-color-5">&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                                                  </div>
+                                                                  <input type="text" class="form-control" placeholder="描述">
+                                                              </div>
+                                                              <div class="input-group input-group-sm mb-2">
+                                                                  <div class="input-group-prepend">
+                                                                      <span class="input-group-text annotate-color-6">&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                                                  </div>
+                                                                  <input type="text" class="form-control" placeholder="描述">
+                                                              </div>
+                                                              <div class="input-group input-group-sm mb-2">
+                                                                  <div class="input-group-prepend">
+                                                                      <span class="input-group-text annotate-color-7">&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                                                                  </div>
+                                                                  <input type="text" class="form-control" placeholder="描述">
+                                                              </div>
+                                                          </div>
+                                                          <button  class="btn btn-sm btn-primary" id = "bookmarkSettingConfirm">确定</button>
+                                                      </form>
+                                                  </div>
+                                              </div>
                                           </div>
+                                          <div class="" id = "bookmarkSpread" style="overflow: hidden"></div>
                                       </div>
-                                  </div>-->
-
+                                      <div class="resize-y" id="bookmark_resize"></div>
+                                      <div class=" container-fluid" id="annotationDiv">
+                                          <div class="row">
+                                              <div class="col-lg-12" style="padding: 0px" >
+                                                  <input type="hidden" id="bookmarkNodeID">
+                                                  <input type="hidden" id="bookmarkNodeType">
+                                                  <textarea class="form-control" id="annotationTextarea"  placeholder = "批注内容"></textarea>
+                                              </div>
+                                          </div>
+                                      </div>
+                                  </div>
                               </div>
 
                               <!--清单指引-->
-                              <div class="tab-pane" id="zy">
+                              <div class="tab-pane occupied" id="zy">
                                   <div class="sidebar-tools-bar container-fluid tools-bar-height-z">
                                       <div class="p-1 row">
                                           <div class="col p-0">
@@ -2127,6 +2186,7 @@
     <img src="/web/dest/css/img/tender.png" id="block_pic" style="display: none">
 
         <!-- JS. -->
+    <script src="/lib/js-base64/base64.min.js"></script>
     <script src = "/lib/spreadjs/sheets/gc.spread.sheets.all.11.1.2.min.js"></script>
     <script src="/lib/spreadjs/sheets/interop/gc.spread.excelio.11.1.2.min.js"></script>
     <script>GC.Spread.Sheets.LicenseKey =  '<%- LicenseKey %>';</script>
@@ -2145,6 +2205,7 @@
     <!-- inject:js -->
     <!--<script type="text/javascript" src="/test/tmp_data/test_ration_calc/ration_calc_base.js"></script>-->
     <script type="text/javascript" src="/web/building_saas/main/js/models/main_consts.js"></script>
+    <script type="text/javascript" src="/web/over_write/config/compilation_config.js"></script>
     <script type="text/javascript" src="/public/web/common_util.js"></script>
     <script type="text/javascript" src="/web/building_saas/glj/js/project_glj.js"></script>
     <script type="text/javascript" src="/web/building_saas/glj/js/composition.js"></script>

+ 42 - 3
web/building_saas/main/js/models/calc_program.js

@@ -98,7 +98,11 @@ let calcTools = {
     getNodeByID: function (ID){
         return cbTools.getNodeByID(ID);
     },
-
+    // 是否是标题清单
+    isTitleBills: function (node) {
+        let flag = node.getFlag();
+        return titleFlags.includes(flag);
+    },
     isBill: function(treeNode){
         return treeNode.sourceType === ModuleNames.bills;
     },
@@ -194,7 +198,7 @@ let calcTools = {
         delete treeNode.data.gljList;
         if (this.isRationCategory(treeNode)) {
             if (treeNode.data.type != rationType.volumePrice) {
-                treeNode.data.gljList = projectObj.project.ration_glj.getGljArrByRation(treeNode.data);
+                treeNode.data.gljList = projectObj.project.calcProgram.getGljArrByRation(treeNode.data);
             }
         }
         else if (this.isBill(treeNode)){
@@ -1438,6 +1442,8 @@ class CalcProgram {
         let me = this;
         me.project = project;
         me.datas = [];
+        me.rationMap = null;//定额 - 工料机映射临时变量
+        me.pgljMap = null;
         project.registerModule(ModuleNames.calc_program, me);
     };
 
@@ -1637,7 +1643,8 @@ class CalcProgram {
         let me = this;
         // 仅用作树节点显示的工料机不能参与计算。
         if (treeNode.sourceType === ModuleNames.ration_glj) return;
-
+        //设置定额工料机映射表
+        me.setRationMap();
         treeNode.calcType = calcTools.getCalcType(treeNode);
         let nQ = calcTools.uiNodeQty(treeNode);
         let nTQ = calcTools.uiNodeTenderQty(treeNode);
@@ -1931,6 +1938,8 @@ class CalcProgram {
         me.project.updateNodes(dataArr, function (data) {
             let endShowTime = +new Date();
             console.log(`保存所需时间——${endShowTime - startTime}`);
+            me.rationMap = null;
+            me.pgljMap = null;
             if(callback){
                 callback(data);
             };
@@ -2170,6 +2179,36 @@ class CalcProgram {
             projectObj.project.calcProgram.calculate(treeNode, false, false, tenderTypes.ttCalc);  // 再正向算
         };
     };
+    setRationMap(){
+        if(this.rationMap == null){
+            this.rationMap = {};
+            for(let glj of projectObj.project.ration_glj.datas){
+                if(this.rationMap[glj.rationID]){
+                    this.rationMap[glj.rationID].push(glj)
+                }else {
+                    this.rationMap[glj.rationID] = [glj];
+                }
+            }
+        }
+        if(this.pgljMap == null ){
+            this.pgljMap = _.indexBy(projectObj.project.projectGLJ.datas.gljList, 'id');
+        }
+    };
+    getGljArrByRation(ration){
+        if (ration.type == rationType.gljRation){
+            let glj = JSON.parse(JSON.stringify(ration));
+            glj.type = glj.subType;
+            glj.quantity = 1;
+            glj.totalQuantity = parseFloatPlus(ration.quantity);
+            return [glj];
+        } else{
+            if(!this.rationMap) return [];
+            let result = this.rationMap[ration.ID];
+            if(!result) return [];
+            result = gljOprObj.combineWithProjectGlj(result,false,ration,this.pgljMap);
+            return result;
+        }
+    }
 };
 
 // export default analyzer;

+ 142 - 103
web/building_saas/main/js/models/exportStandardInterface.js

@@ -32,7 +32,7 @@ const XMLStandard = (function () {
         0: '1800',  //其他未定义的大项费用
     };
     //文件类型
-    const FILE_KIND = {
+    const FILE_KIND_TEXT = {
         '1': '投标',
         '2': '招标',
         '3': '控制价'
@@ -141,17 +141,15 @@ const XMLStandard = (function () {
         _util = _base.UTIL,
         _cache = _base.CACHE;
 
-    return function (userID, granularity, exportKind) {
+    /*
+    * 提取数据入口
+    * @param  {String}userID 用户ID
+    *         {Number}exportKind 导出类型(投标1、招标2、控制价3
+    *         {Object}projectData 项目数据(项目自身的数据、建设、单项、单位的数据(projects表的数据),不包含详细的清单定额等数据)
+    * @return {Array} [{data: Object, exportKind: Number, fileName: String}]
+    * */
+    async function entry(userID, exportKind, projectData) {
         let _failList = _cache.getItem('failList');
-        //默认导出建设项目
-        if (!granularity || ![1, 2, 3].includes(granularity)) {
-            granularity = _config.GRANULARITY.PROJECT;
-        }
-        //默认导出投标文件
-        if (!exportKind || ![1, 2, 3].includes(exportKind)) {
-            exportKind = _config.EXPORT_KIND.Tender;
-        }
-        this.originalDatas = []; //转换后的数据(xml化前的数据结构)
 
         //建设项目定义
         //source:来源数据
@@ -170,6 +168,7 @@ const XMLStandard = (function () {
             this.constraints = {
                 engCode: [],    //单项工程编号
                 tenderCode: [], //单位工程编号
+                billsCode: [],  //清单项目项目编码(修改成了建设项目下唯一)
             };
             XML_EXPORT_BASE.Element.call(this, '标段', attrs);
         }
@@ -186,7 +185,7 @@ const XMLStandard = (function () {
                 {name: '审核人', value: _util.getValueByKey(source.basicInformation, 'auditUnitAuditor')},
                 {name: '开工日期', value: _util.getValueByKey(source.basicInformation, 'commencementDate'), type: _config.TYPE.DATE},
                 {name: '竣工日期', value: _util.getValueByKey(source.basicInformation, 'completionDate'), type: _config.TYPE.DATE},
-                {name: '编制日期', value: _util.getValueByKey(source.basicInformation, 'essentialEstablishDate'), type: _config.TYPE.DATE, required: true},
+                {name: '编制日期', value: _util.getValueByKey(source.basicInformation, 'establishDate'), type: _config.TYPE.DATE, required: true},
                 {name: '审核日期', value: _util.getValueByKey(source.basicInformation, 'auditDate'), type: _config.TYPE.DATE},
                 {name: '材料价格期', value: _util.getValueByKey(source.basicInformation, 'materialPricePeriod'), required: true},
                 {name: '合同价类型', value: _util.getValueByKey(source.basicInformation, 'contractPriceType')},
@@ -199,7 +198,7 @@ const XMLStandard = (function () {
             //控制总价: 如果文件类型是“控制价”,则导出建设项目的工程造价;如果是“招标”、“投标”,则取0
             let attrs = [
                 {name: '招标代理机构', value: _util.getValueByKey(source.basicInformation, 'agency')},
-                {name: '造价工程师', value: _util.getValueByKey(source.basicInformation, 'tenderingCostEngineer'), required: true},
+                {name: '造价工程师', value: _util.getValueByKey(source.basicInformation, 'tenderCostEngineer'), required: true},
                 {name: '造价工程师注册证号', value: _util.getValueByKey(source.basicInformation, 'tenderingRegistrationCertificateNumber'), required: true},
                 {name: '招标工期', value: _util.getValueByKey(source.basicInformation, 'tenderingPeriod'), required: true, type: _config.TYPE.INT},
                 {name: '控制总价', value: exportKind === _config.EXPORT_KIND.Control ? source.summaryInfo.engineeringCost : '0',
@@ -396,7 +395,7 @@ const XMLStandard = (function () {
                     failHint: `第${source.row}行清单-“主要清单标志”`},
                 {name: '暂估清单标志', value: !!source.isEstimate, type: _config.TYPE.BOOL,
                     failHint: `第${source.row}行清单-“暂估清单标志”`},
-                {name: '最高限价', value: exportKind === _config.EXPORT_KIND.Control ? source.maxPrice : '0', type: _config.TYPE.NUM2,
+                {name: '最高限价', value: source.maxPrice, type: _config.TYPE.NUM2,
                     failHint: `第${source.row}行清单-“最高限价”`},
                 {name: '备注', value: source.remark},
             ];
@@ -586,7 +585,7 @@ const XMLStandard = (function () {
         //暂列金额定义
         function Provisional(source) {
             let attrs = [
-                {name: '金额', value: exportKind === _config.EXPORT_KIND.Tender ? _util.getFee(source.fees, 'common.totalFee') : '0', type: _config.TYPE.NUM2}
+                {name: '金额', value: _util.getFee(source.fees, 'common.totalFee'), type: _config.TYPE.NUM2}
             ];
             XML_EXPORT_BASE.Element.call(this, '暂列金额', attrs);
         }
@@ -599,7 +598,7 @@ const XMLStandard = (function () {
                     failHint: `第${source.row}行暂列金额清单-“名称”`},
                 {name: '计量单位', value: source.unit, maxLen: 20, required: true,
                     failHint: `第${source.row}行暂列金额清单-“单位”`},
-                {name: '金额', value: exportKind === _config.EXPORT_KIND.Tender ? _util.getFee(source.fees, 'common.totalFee') : '0',
+                {name: '金额', value: _util.getFee(source.fees, 'common.totalFee'),
                     type: _config.TYPE.NUM2, required: true},
                 {name: '备注', value: source.remark, required: true}
             ];
@@ -608,7 +607,7 @@ const XMLStandard = (function () {
         //专业工程暂估价定义
         function EngEstimate(source) {
             let attrs = [
-                {name: '金额', value: exportKind === _config.EXPORT_KIND.Tender ? _util.getFee(source.fees, 'common.totalFee') : '0'}
+                {name: '金额', value: _util.getFee(source.fees, 'common.totalFee')}
             ];
             XML_EXPORT_BASE.Element.call(this, '专业工程暂估价', attrs);
         }
@@ -621,7 +620,7 @@ const XMLStandard = (function () {
                     failHint: `第${source.row}行专业工程暂估清单-“名称”`},
                 {name: '工程内容', value: source.engineeringContent, maxLen: 2000, required: true,
                     failHint: `第${source.row}行专业工程暂估清单-“工程内容”`},
-                {name: '金额', value: exportKind === _config.EXPORT_KIND.Tender ? _util.getFee(source.fees, 'common.totalFee') : '0',
+                {name: '金额', value: _util.getFee(source.fees, 'common.totalFee'),
                     type: _config.TYPE.NUM2, required: true},
                 {name: '备注', value: source.remark, required: true}
             ];
@@ -705,9 +704,9 @@ const XMLStandard = (function () {
                 {name: '项目名称', value: source.name, required: true},
                 {name: '计量单位', value: source.unit, required: true},
                 {name: '数量', value: source.quantity, type: _config.TYPE.DECIMAL, required: true},
-                {name: '单价', value: exportKind === _config.EXPORT_KIND.Tender ? _util.getFee(source.fees, 'common.unitFee') : '0',
+                {name: '单价', value: _util.getFee(source.fees, 'common.unitFee'),
                     type: _config.TYPE.DECIMAL, required: true},
-                {name: '合价', value: exportKind === _config.EXPORT_KIND.Tender ? _util.getFee(source.fees, 'common.totalFee') : '0',
+                {name: '合价', value: _util.getFee(source.fees, 'common.totalFee'),
                     type: _config.TYPE.NUM2, required: true},
                 {name: '依据', value: source.claimVisa, required: true},
             ];
@@ -948,49 +947,40 @@ const XMLStandard = (function () {
         let curProjectEle = null,  //建设项目节点
             curTenderEle = null;   //单位工程节点
 
-        //获取需要导出的项目数据
+        // 获取需要导出的项目数据
         //@param {Number}tenderID(当前界面的单位工程ID,后台根据这个单位工程,去找其建设项目下所有数据)
         //@return {Object}(eleObj)
-        async function loadProject(tenderID) {
-            //拉取标段数据:建设项目、单项工程、单位工程数据
-            let projectsData = await _util.getProjectByGranularity(granularity, tenderID, userID);
-            if (!projectsData) {
-                throw '获取项目数据错误';
-            }
-            //单项工程、单位工程按照树结构数据进行排序
-            projectsData.children = _util.sortByNext(projectsData.children);
-            for (let engData of projectsData.children) {
-                engData.children = _util.sortByNext(engData.children);
-            }
+        async function loadProject(projectData) {
+            console.log(projectData);
             //标段
             let project = new Project({
-                basicInformation: projectsData.property.basicInformation,
-                name: projectsData.name,
-                fileKind: FILE_KIND[exportKind],
-                taxType: projectsData.property.taxType ? _config.TAX_TYPE[projectsData.property.taxType] : ''
+                basicInformation: projectData.property.basicInformation,
+                name: projectData.name,
+                fileKind: FILE_KIND_TEXT[exportKind],
+                taxType: projectData.property.taxType ? _config.TAX_TYPE[projectData.property.taxType] : ''
             });
             curProjectEle = project;
             //项目信息
-            let projectInfo = new ProjectInfo({basicInformation: projectsData.property.basicInformation});
+            let projectInfo = new ProjectInfo({basicInformation: projectData.property.basicInformation});
             project.children.push(projectInfo);
             //招标信息
-            let biddingInfo = new BiddingInfo({basicInformation: projectsData.property.basicInformation, summaryInfo: projectsData.summaryInfo[projectsData.ID]});
+            let biddingInfo = new BiddingInfo({basicInformation: projectData.property.basicInformation, summaryInfo: projectData.summaryInfo[projectData.ID]});
             projectInfo.children.push(biddingInfo);
             //投标信息 导出类型为投标时,才导出
             if (exportKind === _config.EXPORT_KIND.Tender) {
-                let bidInfo = new BidInfo({basicInformation: projectsData.property.basicInformation, summaryInfo: projectsData.summaryInfo[projectsData.ID]});
+                let bidInfo = new BidInfo({basicInformation: projectData.property.basicInformation, summaryInfo: projectData.summaryInfo[projectData.ID]});
                 projectInfo.children.push(bidInfo);
             }
             //编制说明
-            let compilationIll = new CompilationIllustration({compilationIllustration: projectsData.property.compilationIllustration});
+            let compilationIll = new CompilationIllustration({compilationIllustration: projectData.property.compilationIllustration});
             project.children.push(compilationIll);
             //系统信息
             let generatedTime = moment(Date.now()).format('YYYY-MM-DDTHH:mm:ss'),
-                sysInfoSource = {softInfo: projectsData.softInfo, generatedTime},
+                sysInfoSource = {softInfo: projectData.softInfo, generatedTime},
                 sysInfo = new SystemInfo(sysInfoSource);
             project.children.push(sysInfo);
             //费用构成
-            let feeForm = new FeeFrom(projectsData.summaryInfo[projectsData.ID]);
+            let feeForm = new FeeFrom(projectData.summaryInfo[projectData.ID]);
             project.children.push(feeForm);
             //主要清单汇总    主要清单明细在loadTender中设置
             let mainBillsSummaryEle = new MainBillsSummary();
@@ -999,13 +989,17 @@ const XMLStandard = (function () {
             let appraisalSummary = new AppraisalSummary();
             project.children.unshift(appraisalSummary);
             //单项工程
-            for (let eng of projectsData.children) {
-                let engElement = await loadEngineering(projectsData.summaryInfo, eng);
+            for (let eng of projectData.children) {
+                let engElement = await loadEngineering(projectData.summaryInfo, eng);
                 project.children.push(engElement);
             }
             //主要清单汇总、评审材料汇总 排在后面
             project.children = [...project.children.slice(2), mainBillsSummaryEle, appraisalSummary];
-            return project;
+            return [{
+                data: project,
+                exportKind: exportKind,
+                fileName: `重庆标准交换数据(${FILE_KIND_TEXT[exportKind]}).QTF`
+            }];
         }
         //单位工程内的人材机ID: 用于代码映射关系 //单位工程级别,C+数组下标 <==> gljID
         let tenderGljs = [];
@@ -1048,10 +1042,10 @@ const XMLStandard = (function () {
             return engineering;
         }
         /*
-        * 加载单位工程数据
-        * @param {Object}summaryInfo(项目汇总信息映射) {Object}tenderData(单位工程数据)
-        * @return {Object}
-        * */
+         * 加载单位工程数据
+         * @param {Object}summaryInfo(项目汇总信息映射) {Object}tenderData(单位工程数据)
+         * @return {Object}
+         * */
         async function loadTender(summaryInfo, tenderData) {
             //获取单位工程详细数据
             let tenderDetail = await _util.getTenderDetail(tenderData.ID, userID);
@@ -1171,10 +1165,10 @@ const XMLStandard = (function () {
             return tender;
         }
         /*
-        * 加载计算程序费用行
-        * @param {Object}detail(单位工程的详细数据,清单定额等等)
-        * @return {Object}
-        * */
+         * 加载计算程序费用行
+         * @param {Object}detail(单位工程的详细数据,清单定额等等)
+         * @return {Object}
+         * */
         function loadDXFY(detail) {
             //单位工程费汇总
             let tenderFeeSummary = new TenderFeeSummary();
@@ -1202,13 +1196,20 @@ const XMLStandard = (function () {
             return tenderFeeSummary;
         }
         /*
-        * 加载清单项目
-        * @param {Object}node(清单树节点) {Object}detail
-        * */
+         * 加载清单项目
+         * @param {Object}node(清单树节点) {Object}detail
+         * */
         function loadBills(node, detail) {// allRation, allRationGlj, decimal,
             let allRation = detail.Ration.datas,
                 allRationGlj = detail.ration_glj.datas,
                 decimal = detail.projectInfo.property.decimal;
+            // 处理最高限价,输出最高限价时,若最高限价为空,则取综合单价,否则取最高限价
+            let maxPrice = 0;
+            if (exportKind === _config.EXPORT_KIND.Control && node.data.outPutMaxPrice) {
+                maxPrice = _util.isDef(node.data.maxPrice)
+                    ? node.data.maxPrice
+                    : _util.getFee(node.data.fees, 'common.unitFee')
+            }
             let source = {
                 row: detail.mainTree.nodes[detail.mainTree.prefix + node.data.ID].serialNo() + 1,
                 code: node.data.code,
@@ -1217,12 +1218,13 @@ const XMLStandard = (function () {
                 quantity: node.data.quantity,
                 fees: node.data.fees,
                 mainBills: node.data.mainBills,
+                maxPrice: maxPrice,
                 isEstimate: node.data.isEstimate,
                 remark: node.data.remark
             };
             let bills = new FXbills(source);
-            //清单项目项目编码要在单位工程中唯一
-            _util.checkUnique(curTenderEle.constraints.billsCode, source.code, `第${source.row}行`,`清单项目编码${source.code}`);
+            //清单项目项目编码要在建设项目中唯一
+            _util.checkUnique(curProjectEle.constraints.billsCode, source.code, `第${source.row}行`,`清单项目编码${source.code}`);
             //加载特征及内容
             function loadFeatureContent() {
                 let job = [],
@@ -1269,8 +1271,8 @@ const XMLStandard = (function () {
                         let source = {name: '', value: ''};
                         let execRst = reg.exec(f);
                         if (execRst) {
-                           source.name = execRst[1].replace(/^\d+\.{1}/, '').trim();  //去除开头(1.)序号,首位空格
-                           source.value = execRst[2].trim();
+                            source.name = execRst[1].replace(/^\d+\.{1}/, '').trim();  //去除开头(1.)序号,首位空格
+                            source.value = execRst[2].trim();
                         }
                         let featureEle = new Feature(source);
                         //let featureEle = new Feature({name: f.trim(), value: ''});
@@ -1395,10 +1397,10 @@ const XMLStandard = (function () {
             return bills;
         }
         /*
-        * 加载分部分项清单
-        *  @param {Object}detail
-        *  @return {Object || NULL}
-        * */
+         * 加载分部分项清单
+         *  @param {Object}detail
+         *  @return {Object || NULL}
+         * */
         function loadFBFX(detail) {
             let fbfxBills = new FBFXBills();
             let subEngNode = detail.Bills.tree.roots.find((node) => node.getFlag() === fixedFlag.SUB_ENGINERRING);
@@ -1443,10 +1445,10 @@ const XMLStandard = (function () {
             return fbfxBills;
         }
         /*
-        * 加载措施项目清单
-        * @param {Object}detail
-        * @return {Object}
-        * */
+         * 加载措施项目清单
+         * @param {Object}detail
+         * @return {Object}
+         * */
         function loadCSXM(detail) {
             let measureNode = detail.Bills.tree.roots.find((node) => node.getFlag() === fixedFlag.MEASURE);
             if (!measureNode) {
@@ -1539,10 +1541,10 @@ const XMLStandard = (function () {
             }
         }
         /*
-        * 加载其他项目清单,要出现此节点,需要遵循标准条件(至少含有一条相关明细)
-        * @param {Object}detail
-        * @return {Object || Null}
-        * */
+         * 加载其他项目清单,要出现此节点,需要遵循标准条件(至少含有一条相关明细)
+         * @param {Object}detail
+         * @return {Object || Null}
+         * */
         function loadOtherBills(detail) {
             //其他项目清单元素
             let otherEle = new OtherBills();
@@ -1563,7 +1565,7 @@ const XMLStandard = (function () {
             //添加计日工元素
             let dayWorkNode = detail.Bills.tree.items.find(node => node.getFlag() === fixedFlag.DAYWORK);
             let dayWorkEle = new DayWork({fees: dayWorkNode.data.fees});
-                if (dayWorkNode && dayWorkNode.children.length > 0) {
+            if (dayWorkNode && dayWorkNode.children.length > 0) {
                 //要显示计日工元素,人工、材料、施工机械,最少有一条含有子项(计日工项目)
                 let filterNodes = dayWorkNode.children.filter(node => [fixedFlag.LABOUR, fixedFlag.MATERIAL, fixedFlag.MACHINE].includes(node.getFlag()));
                 for (let fNode of filterNodes) {
@@ -1786,10 +1788,10 @@ const XMLStandard = (function () {
 
         }
         /*
-        * 加载规费和税金清单,固定显示:规费、规费子项、税金、增值税、附加税、环境保护税这几个清单
-        * @param {Object}detail
-        * @return {Object}
-        * */
+         * 加载规费和税金清单,固定显示:规费、规费子项、税金、增值税、附加税、环境保护税这几个清单
+         * @param {Object}detail
+         * @return {Object}
+         * */
         function loadChargeTax(detail) {
             let chargeTaxEle = new ChargeTaxBills();
             let filterFlags = [
@@ -1832,10 +1834,10 @@ const XMLStandard = (function () {
             return chargeTaxEle;
         }
         /*
-        * 加载主要清单明细
-        * @param {Obejct}parent(设置到的父项:主要清单汇总元素) {Object}detail
-        * @return {void}
-        * */
+         * 加载主要清单明细
+         * @param {Obejct}parent(设置到的父项:主要清单汇总元素) {Object}detail
+         * @return {void}
+         * */
         function loadMainBillsItems(parent, detail) {
             let mainBills = detail.Bills.datas.filter(data => {
                 let billsNode = detail.Bills.tree.nodes[`${detail.Bills.tree.prefix}${data.ID}`];
@@ -1857,10 +1859,10 @@ const XMLStandard = (function () {
             }
         }
         /*
-        * 加载人材机汇总相关:人材机汇总、评标、暂估
-        * @param {Object}appraisalSummary(建设项目下的评审材料汇总) {Object}detail
-        * @return {Object}
-        * */
+         * 加载人材机汇总相关:人材机汇总、评标、暂估
+         * @param {Object}appraisalSummary(建设项目下的评审材料汇总) {Object}detail
+         * @return {Object}
+         * */
         function loadGlj(appraisalSummary, detail) {
             let gljList = detail.projectGLJ.datas.gljList;
             let evalBidSeq = 1,    //评标序号
@@ -1966,8 +1968,8 @@ const XMLStandard = (function () {
             }
         }
         /*
-        * 加载清单综合单价计算程序
-        * */
+         * 加载清单综合单价计算程序
+         * */
         function loadCalcProgram(detail) {
             let calcProgram = detail.calcProgram;
             if (!calcProgram) {
@@ -2008,22 +2010,59 @@ const XMLStandard = (function () {
             }
             return calcProgramEle.children.length > 0 ? calcProgramEle : null;
         }
-        /*
-         * 转换导出数据
-         * @param {Number}tenderID(当前界面的单位工程ID,后台根据这个单位工程,根据导出粒度去找其建设项目下相关数据)
-         * @param {Number}curExportKind(导出的文件类型:1-投标 2-招标 3-控制价)
-         * @return {void}
-         * */
-        this.transformData = async function (tenderID, curExportKind) {
-            if (curExportKind) {
-                exportKind = parseInt(curExportKind);
-            }
-            let eleData = await loadProject(tenderID);
-            if (!eleData) {
-                return;
+
+        // 返回提取的数据
+        return await loadProject(projectData);
+    }
+
+    /*
+    * 重置工程编号,因为每一个费用定额,导出的结构都不相同,重置工程编号的逻辑需要各自定义
+    * @param  {Array}codes 工程编号
+    *         {Array}extractData 提取的数据
+    * @return {void}
+    * */
+    function resetContentCode(codes, extractData) {
+        // 提取到的数据全是以建设项目为根元素的数据,每份数据都要重置编号
+        extractData.forEach(obj => {
+            let idx = 0;
+            // 从建设项目元素中筛选出单项工程元素
+            let engs = _util.getElementFromSrc(obj.data, '单项工程');
+            engs.forEach(eng => {
+                // 从属性中找到编号项
+                let codeItem = eng.attrs.find(attr => attr.name === '编号');
+                codeItem.value = codes[idx++];
+                // 从单项工程元素中筛选出单位工程元素
+                let tenders = _util.getElementFromSrc(eng, '单位工程');
+                tenders.forEach(tender => {
+                    let codeItem = tender.attrs.find(attr => attr.name === '编号');
+                    codeItem.value = codes[idx++];
+                });
+            });
+        });
+    }
+
+    /*
+    * 导出文件的结构逻辑,每个费用定额导出的最终文件结构要求可能不同,需要各自定义
+    * 重庆18费用定额,只有一个文件的时候直接导出qtf文件,文件数大于1时,导出zip压缩包
+    * @param  {Array}fileData 文件数据
+    * @return {void}
+    * */
+    async function saveAsFile(fileData) {
+        if (fileData.length === 1) {
+            saveAs(fileData[0].blob, fileData[0].fileName);
+        } else if (fileData.length > 1) { //导出压缩包
+            let zip = new JSZip();
+            for (let file of fileData) {
+                zip.file(file.fileName, file.blob, {binary: true});
             }
-            console.log(eleData);
-            this.originalDatas.push({data: eleData, fileName: `重庆标准交换数据(${FILE_KIND[exportKind]}).QTF`});
-        };
+            let zipFile = await zip.generateAsync({type: 'blob'});
+            saveAs(zipFile, '重庆标准交换数据.zip');
+        }
     }
+
+    return {
+        entry,
+        resetContentCode,
+        saveAsFile
+    };
 })();

+ 65 - 45
web/building_saas/main/js/models/exportStdInterfaceBase.js

@@ -18,6 +18,13 @@ const XML_EXPORT_BASE = (() => {
         NUM2: 5,        //数值类型2:最多两位小数
         BOOL: 6         //布尔型
     };
+    // 需要特殊处理的属性类型默认空值(当一个值为undefined、null的时候,默认给赋什么值)
+    const DEFAULT_VALUE = {
+        [TYPE.INT]: '0',
+        [TYPE.DECIMAL]: '0',
+        [TYPE.NUM2]: '0',
+        [TYPE.BOOL]: 'false'
+    };
     // 空白字符处理
     const WHITE_SPACE = {
         COLLAPSE: 1 //移除所有空白字符(换行、回车、空格以及制表符会被替换为空格,开头和结尾的空格会被移除,而多个连续的空格会被缩减为一个单一的空格)
@@ -62,7 +69,7 @@ const XML_EXPORT_BASE = (() => {
         // 失败列表
         failList: [],
         // 项目数据(不包含详细数据,项目管理数据)
-        projectsData: {},
+        projectData: {},
         // 当前导出类型,默认投标
         exportKind: EXPORT_KIND.Tender,
         // 记录拉取的单位工程项目详细数据,导出的时候,可能会导出多个文件,只有导出第一个文件的时候需要请求数据
@@ -157,7 +164,11 @@ const XML_EXPORT_BASE = (() => {
         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
-            data.value = !isDef(data.value) ? '' : String(data.value);
+            data.value = !isDef(data.value)
+                ? DEFAULT_VALUE[data.type]
+                    ? DEFAULT_VALUE[data.type]
+                    : ''
+                : String(data.value);
             if (data.whiteSpace && data.whiteSpace === WHITE_SPACE.COLLAPSE) {  //处理空格相关
                 data.value = data.value.replace(/[\r,\n,\t]/g, ' ');
                 data.value = data.value.trim();
@@ -368,13 +379,13 @@ const XML_EXPORT_BASE = (() => {
     * @return {Object} 返回的数据结构:{children: [{children: []}]} 最外层为建设项目,中间为单项工程,最底层为单位工程
     * */
     async function getProjectByGranularity(granularity, tenderID, userID) {
-        let projectsData = _cache.projectsData;
+        let projectData = _cache.projectData;
         // 没有数据,需要拉取
-        if (!Object.keys(projectsData).length) {
-            projectsData = await ajaxPost('/pm/api/getProjectByGranularity', {user_id: userID, tenderID: tenderID, granularity: granularity});
-            _cache.projectsData =  projectsData;
+        if (!Object.keys(projectData).length) {
+            projectData = await ajaxPost('/pm/api/getProjectByGranularity', {user_id: userID, tenderID: tenderID, granularity: granularity});
+            _cache.projectData =  projectData;
         }
-        return projectsData;
+        return projectData;
     }
 
     /*
@@ -396,14 +407,14 @@ const XML_EXPORT_BASE = (() => {
 
     /*
      * 提取要导出的数据
-     * @param  {Function}entryFuc 提取数据的入口方法
+     * @param  {Function}entryFunc 提取数据的入口方法
      *         {Number}granularity 导出粒度: 1-建设项目、2-单项工程、3-单位工程
      *         {Number}exportKind 导出的文件类型:1-投标、2-招标、3-控制价
      *         {Number}tenderID 单位工程ID
      *         {String}userID 用户ID
-     * @return {Object} 数据结构为:{name: 'elementName', attrs: [...], children: [...], fail: [...]}
+     * @return {Array} 数据结构为:[{data: Object, exportKind: Number, fileName: String}]
      * */
-    async function extractExportData({entryFuc, granularity, exportKind, tenderID, userID}) {
+    async function extractExportData(entryFunc, granularity, exportKind, tenderID, userID) {
         // 默认导出建设项目
         if (!granularity || ![1, 2, 3].includes(granularity)) {
             granularity = GRANULARITY.PROJECT;
@@ -413,16 +424,17 @@ const XML_EXPORT_BASE = (() => {
             exportKind = EXPORT_KIND.Tender;
         }
         // 拉取标段数据:建设项目、单项工程、单位工程数据
-        let projectsData = await getProjectByGranularity(granularity, tenderID, userID);
-        if (!projectsData) {
+        let projectData = await getProjectByGranularity(granularity, tenderID, userID);
+        if (!projectData) {
             throw '获取项目数据错误';
         }
         // 单项工程、单位工程按照树结构数据进行排序
-        projectsData.children = sortByNext(projectsData.children);
-        for (let engData of projectsData.children) {
+        projectData.children = sortByNext(projectData.children);
+        for (let engData of projectData.children) {
             engData.children = sortByNext(engData.children);
         }
-
+        // 提取相关项目的详细导出数据
+        return await entryFunc(userID, exportKind, projectData);
     }
 
     //转换基数表达式
@@ -488,7 +500,7 @@ const XML_EXPORT_BASE = (() => {
             return matchs || [];
         }
     }
-    //转换基数说明,根据转换后的基数处理
+    // 转换基数说明,根据转换后的基数处理
     //1.行引用转换为对应行的名称
     //2.基数字典转换为中文
     function transformCalcBaseState(tenderDetail, expr, CalcStateMap) {
@@ -535,11 +547,11 @@ const XML_EXPORT_BASE = (() => {
         return newExpr;
     }
     // 获取工程编号表格相关数据(导出需要弹出工程编号让用户选择)
-    function getCodeSheetData(PMData) {
+    function getCodeSheetData(projectData) {
         let curCode = '0';
         let sheetData = [];
-        sheetData.push(getObj(PMData));
-        PMData.children.forEach(eng => {
+        sheetData.push(getObj(projectData));
+        projectData.children.forEach(eng => {
             sheetData.push(getObj(eng));
             eng.children.forEach(tender => {
                 sheetData.push(getObj(tender));
@@ -563,7 +575,7 @@ const XML_EXPORT_BASE = (() => {
         }
     }
     // 从srcEle节点中获取元素名为eleName的元素
-    function _getElementFromSrc(srcEle, eleName) {
+    function getElementFromSrc(srcEle, eleName) {
         if (!srcEle || !srcEle.children || !srcEle.children.length) {
             return [];
         }
@@ -585,10 +597,10 @@ const XML_EXPORT_BASE = (() => {
         // 给导出数据里的单项工程、单位工程填上用户设置的工程编号
         exportData.forEach(orgData => {
             let curIdx = 0;
-            let engs = _getElementFromSrc(orgData.data, EngineeringName);
+            let engs = getElementFromSrc(orgData.data, EngineeringName);
             engs.forEach(eng => {
                 eng.attrs.find(attr => attr.name === codeName).value = parsedCodes[curIdx++];
-                let tenders = _getElementFromSrc(eng, tenderName);
+                let tenders = getElementFromSrc(eng, tenderName);
                 tenders.forEach(tender => {
                     tender.attrs.find(attr => attr.name === codeName).value = parsedCodes[curIdx++];
                 });
@@ -611,6 +623,8 @@ const XML_EXPORT_BASE = (() => {
         transformCalcBase,
         transformCalcBaseState,
         getCodeSheetData,
+        getElementFromSrc,
+        getParsedData,
         setupCode
     });
 
@@ -677,32 +691,37 @@ const XML_EXPORT_BASE = (() => {
         }
     }
 
-
-    // 将数据转换为xml文件并导出
-    async function exportFile(originalDatas) {
-        let fileDatas = [];
-        //源数据转换成blob
-        for (let orgData of originalDatas) {
-            //转换成xml字符串
-            let xmlStr = _toXMLStr([orgData.data]);
-            //加上xml声明
+    /*
+    * 根据各自费用定额的文件结构,导出文件
+    * 每个费用定额可能导出的结果文件都不同
+    * 比如广东18需要将一个建设项目文件,多个单位工程文件打包成一个zip文件。重庆18就没这种要求
+    * @param  {Array}codes 工程编号数据
+    *         {Array}extractData 提取的数据
+    *         {Function}setCodeFunc 各自费用定额的导出前重设用户输入的工程编号方法
+    *         {Function}saveAsFunc 各自费用定额的导出方法
+    * @return {void}
+    * */
+    async function exportFile(codes, extractData, setCodeFunc, saveAsFunc) {
+        // 编号重置后将会被导出,需要将编号进行xml字符实体转换
+        codes = getParsedData(codes);
+        setCodeFunc(codes, extractData);
+        // 获取文件数据
+        let fileData = extractData.map(extractObj => {
+            // 转换成xml字符串
+            let xmlStr = _toXMLStr([extractObj.data]);
+            // 加上xml声明
             xmlStr = `<?xml version="1.0" encoding="utf-8"?>${xmlStr}`;
-            //格式化
+            // 格式化
             xmlStr = _formatXml(xmlStr);
             let blob = new Blob([xmlStr], {type: 'text/plain;charset=utf-8'});
-            fileDatas.push({blob: blob, fileName: orgData.fileName});
-        }
-        //导出文件
-        if (fileDatas.length === 1) {
-            saveAs(fileDatas[0].blob, fileDatas[0].fileName);
-        } else if (fileDatas.length > 1) { //导出压缩包
-            let zip = new JSZip();
-            for (let file of fileDatas) {
-                zip.file(file.fileName, file.blob, {binary: true});
-            }
-            let zipFile = await zip.generateAsync({type: 'blob'});
-            saveAs(zipFile, '重庆标准交换数据.zip');
-        }
+            return {
+                blob: blob,
+                exportKind: extractObj.exportKind,
+                fileName: extractObj.fileName
+            };
+        });
+        // 导出
+        await saveAsFunc(fileData);
     }
     
     return {
@@ -710,6 +729,7 @@ const XML_EXPORT_BASE = (() => {
         CACHE,
         UTIL,
         Element,
+        extractExportData,
         exportFile
     };
 })();

+ 8 - 8
web/building_saas/main/js/models/importStandardInterface.js

@@ -272,13 +272,13 @@ const ImportXML = (() => {
                 info.push({key: 'auditUnitAuditor', value: getValue(projInfoSrc, ['_审核人'])});
                 info.push({key: 'commencementDate', value: getValue(projInfoSrc, ['_开工日期'])});
                 info.push({key: 'completionDate', value: getValue(projInfoSrc, ['_竣工日期'])});
-                info.push({key: 'essentialEstablishDate', value: getValue(projInfoSrc, ['_编制日期'])});
+                info.push({key: 'establishDate', value: getValue(projInfoSrc, ['_编制日期'])});
                 info.push({key: 'auditDate', value: getValue(projInfoSrc, ['_审核日期'])});
                 info.push({key: 'materialPricePeriod', value: getValue(projInfoSrc, ['_材料价格期'])});
                 info.push({key: 'contractPriceType', value: getValue(projInfoSrc, ['_合同价类型'])});
                 //招标信息
                 info.push({key: 'agency', value: getValue(projInfoSrc, ['招标信息', '_招标代理机构'])});
-                info.push({key: 'tenderingCostEngineer', value: getValue(projInfoSrc, ['招标信息', '_造价工程师'])});
+                info.push({key: 'tenderCostEngineer', value: getValue(projInfoSrc, ['招标信息', '_造价工程师'])});
                 info.push({key: 'tenderingRegistrationCertificateNumber', value: getValue(projInfoSrc, ['招标信息', '_造价工程师注册证号'])});
                 info.push({key: 'tenderingPeriod', value: getValue(projInfoSrc, ['招标信息', '_招标工期'])});
                 info.push({key: 'engineeringCost', value: getValue(projInfoSrc, ['招标信息', '_控制总价'])});
@@ -709,9 +709,9 @@ const ImportXML = (() => {
                     unit: getValue(src, ['_计量单位']),
                     remark: getValue(src, ['_备注'])
                 };
-                if (importFileKind === FileKind.tender) {
+                //if (importFileKind === FileKind.tender) {
                     obj.fees = [{fieldName: 'common', totalFee: getValue(src, ['_金额']) || '0'},];
-                }
+                //}
                 return obj;
             })};
             //专业工程暂估价
@@ -723,9 +723,9 @@ const ImportXML = (() => {
                     engineeringContent: getValue(src, ['_工程内容']),
                     remark: getValue(src, ['_备注'])
                 };
-                if (importFileKind === FileKind.tender) {
+                //if (importFileKind === FileKind.tender) {
                     obj.fees = [{fieldName: 'common', totalFee: getValue(src, ['_金额']) || '0'},];
-                }
+                //}
                 return obj;
             })};
             //计日工
@@ -788,11 +788,11 @@ const ImportXML = (() => {
                     quantity: getValue(src, ['_数量']),
                     claimVisa: getValue(src, ['_依据'])
                 };
-                if (importFileKind === FileKind.tender) {
+                //if (importFileKind === FileKind.tender) {
                     obj.fees = [
                         {fieldName: 'common', unitFee: getValue(src, ['_单价']) || '0', totalFee: getValue(src, ['_合价']) || '0'}
                     ];
-                }
+                //}
                 return obj;
             }
             let claim = {items: arrayValue(otherSrc, ['索赔计价汇总', '签证索赔计价汇总费用项']).map(extractClaimVisa)},

+ 14 - 0
web/building_saas/main/js/models/main_consts.js

@@ -272,6 +272,20 @@ const fixedFlag = {
     //组织措施费
     ORGANIZATION:32,
 };
+// 只读的固定类别(工程量、单价、综合合价只读,相当于是标题)
+const titleFlags = [
+    fixedFlag.PROVISIONAL,
+    fixedFlag.ESTIMATE,
+    fixedFlag.ENGINEERING_ESITIMATE,
+    fixedFlag.DAYWORK,
+    fixedFlag.LABOUR,
+    fixedFlag.MATERIAL,
+    fixedFlag.MACHINE,
+    fixedFlag.TURN_KEY_CONTRACT,
+    fixedFlag.CLAIM_VISA,
+    fixedFlag.CLAIM,
+    fixedFlag.VISA
+];
 const gljKeyArray =['code','name','specs','unit','type'];
 const rationKeyArray =['code','name','specs','unit','subType'];
 const gljLibKeyArray =['code', 'name', 'specs', 'unit', 'gljType'];

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

@@ -30,7 +30,7 @@ var PROJECT = {
             });
 
         };
-        tools.doAfterLoad = function(result, callback){
+        tools.doAfterLoad = function(result, showProjectInfo, callback){
             var counter;
             //必须要先load ProjectInfo的信息
             let projectInfoModule = result.find(data => data.moduleName === ModuleNames.projectInfo);
@@ -56,7 +56,9 @@ var PROJECT = {
                     me.modules[module].setMaxID(counter[module]);
                 }
             }
-            projectInfoObj.showProjectInfo(me._project.projectInfo);
+            if (showProjectInfo) {
+                projectInfoObj.showProjectInfo(me._project.projectInfo);
+            }
             me._project.loadMainTree();
             //me.test(result[0].data[0]);
             if (callback) {
@@ -236,7 +238,8 @@ var PROJECT = {
                 timeout: 50000,
                 success: function (result) {
                     if (!result.error) {
-                        tools.doAfterLoad(result.data, callback);
+                        let showProjectInfo = true;
+                        tools.doAfterLoad(result.data, showProjectInfo, callback);
                         // for test calc
                         //tools.doAfterLoad([{moduleName: 'bills', data: BillsData}, {'moduleName': 'ration', data: DrawingData}], callback);
                     } else {
@@ -252,7 +255,8 @@ var PROJECT = {
         project.prototype.loadDataSync = async function () {
             let data = await ajaxPost('/project/getData', {user_id: tools._userID, project_id: tools._ID});
             if (data) {
-                tools.doAfterLoad(data);
+                let showProjectInfo = false;
+                tools.doAfterLoad(data, showProjectInfo);
             }
         };
 

+ 9 - 0
web/building_saas/main/js/models/ration.js

@@ -120,6 +120,12 @@ var Ration = {
             let pEngineer = projectObj.project.projectInfo.property.projectEngineering;//量价默认使用后台设置的单位工程取费专业
             newData[project.masterField.ration] = billsID;
             newData['type'] = rType;
+            // 插入定额的时候,定额工作内容默认取清单的第一个工作内容
+            if (_compilationConfig.dynamicRationWorkContent) {
+                let billsNode = projectObj.project.mainTree.nodes['id_' + billsID];
+                let contentText = contentOprObj.getDefaultContent(billsNode);
+                newData.jobContentText = contentText;
+            }
             if (rType == rationType.volumePrice){
                 newData['subType'] =  priceType?priceType:gljType.GENERAL_MATERIAL;   // 如果priceType 没传,默认的量价类型为材料
             };
@@ -446,6 +452,7 @@ var Ration = {
             })
         };
         ration.prototype.addMultiRation = function (items, callback) {
+            console.log('addMultiRation');
             let me = this;
             let project = projectObj.project, sheetController = projectObj.mainController;
             let engineering = projectObj.project.projectInfo.property.engineering;
@@ -558,6 +565,7 @@ var Ration = {
 
 
         ration.prototype.addNewRation = function (itemQuery,rationType,callback=null,isEmpty=false,priceType,needCalcAndSave=true) {//priceType 是量价类型
+            console.log('addNewRation');
             let me = this;
             let project = projectObj.project, sheetController = projectObj.mainController;
             let engineering = projectObj.project.projectInfo.property.engineering;
@@ -645,6 +653,7 @@ var Ration = {
 
         };
         ration.prototype.addNewRationFast = function (rationType,callback) {
+            console.log('addNewRationFast');
             let me = this;
             let project = projectObj.project, sheetController = projectObj.mainController;
             let engineering = projectObj.project.projectInfo.property.engineering;

+ 1 - 22
web/building_saas/main/js/models/ration_glj.js

@@ -40,27 +40,6 @@ let ration_glj = {
             this.datas = datas;
         };
 
-        ration_glj.prototype.getGljArrByRation = function (ration) {
-            if (ration.type == rationType.gljRation){
-                let glj = JSON.parse(JSON.stringify(ration));
-                glj.type = glj.subType;
-                glj.quantity = 1;
-                glj.totalQuantity = parseFloatPlus(ration.quantity);
-                return [glj];
-            }
-            else{
-                let result = this.datas.filter(function (data) {
-                    if(data.rationID === ration.ID){
-                        gljOprObj.getTotalQuantity(data, ration);
-                        return true;
-                    }
-                    return false;
-                })
-                result = gljOprObj.combineWithProjectGlj(result);
-                return result;
-            }
-        };
-
         ration_glj.prototype.getGLJListByRationID = function (rationID) {
            return  _.filter(this.datas, {'rationID': rationID})
         };
@@ -95,7 +74,7 @@ let ration_glj = {
                     result.push(glj);
                 }
                 else{
-                    let rationGljs = this.getGljArrByRation(ration);
+                    let rationGljs = projectObj.project.calcProgram.getGljArrByRation(ration);
                     for (let glj of rationGljs) {
                         let sameGlj = findGlj(glj, result);
                         if (!sameGlj) {

+ 82 - 0
web/building_saas/main/js/views/character_content_view.js

@@ -179,6 +179,60 @@ let contentOprObj = {
         const setting = projectObj.project.property.addRule !== undefined ? projectObj.project.property.addRule : getAddRuleSetting();
         pageCCOprObj.setCharacterBySetting(selectedNode, setting, callback, contentOprObj);
     },
+    // 获取默认清单工作内容文本
+    getDefaultContent: function (billsNode) {
+        if (!billsNode || !Array.isArray(billsNode.data.jobContent)) {
+            return '';
+        }
+        return billsNode.data.jobContent[0].content;
+    },
+    // 从清单节点获取工作内容文本数据
+    getContentTexts: function (billsNode) {
+        if (!billsNode || !Array.isArray(billsNode.data.jobContent)) {
+            return [];
+        }
+        let contents = billsNode.data.jobContent.map(data => data.content);
+        // 去重
+        return [...new Set(contents)];
+    },
+    // 对于定额工作内容与清单工作内容相关联的费用定额来说
+    // 操作工作内容后,需要处理定额的工作内容
+    // 若清单的工作内容修改后,定额的工作内容在清单工作内容中找不到,则把定额的工作内容设置为''
+    getUpdateRations: function (billsNode) {
+        // 获取清单工作内容文本数组
+        let contents = this.getContentTexts(billsNode);
+        // 查看清单下的定额,哪些定额需要更新(工作内容无法匹配)
+        let projectID = projectObj.project.ID(),
+            updates = [];
+        for (let rNode of billsNode.children) {
+            if (!rNode.data.jobContentText) {
+                continue;
+            }
+            // 匹配不到,定额工作内容置空
+            if (!contents.includes(rNode.data.jobContentText)) {
+                updates.push({
+                    updateType: 'ut_update',
+                    updateData: {
+                        ID: rNode.data.ID,
+                        projectID,
+                        jobContentText: ''
+                    }
+                });
+            }
+        }
+        return updates.length
+            ? {
+                project_id: projectID,
+                user_id: userID,
+                date: new Date(),
+                operation: 'updateBills',
+                update_data: [{
+                    moduleName: 'ration',
+                    data: updates
+                }]
+            }
+            : null;
+    },
     onEditStart(sender, args){
         if(projectObj.project.projectInfo.property.lockBills && projectObj.project.withinBillsLocked(projectObj.project.mainTree.selected)){
             args.cancel = true;
@@ -911,6 +965,9 @@ let characterOprObj = {
 };
 
 let pageCCOprObj = {
+    // 记录清单节点-工作内容文本数组的映射关系
+    // 定额-动态工作内容下拉项需要
+    contentCache: {},
     currentFindSet: null,
     mainActiveCell: null,//mainSpread焦点单元格
     nameCache: '',
@@ -1210,6 +1267,31 @@ let pageCCOprObj = {
             if(callback){
                 callback();
             }
+            // 操作工作内容时更新定额显示(工作内容下拉)
+            if (_compilationConfig.dynamicRationWorkContent &&
+                oprObj === contentOprObj) {
+                let updateData = contentOprObj.getUpdateRations(node);
+                if (updateData) {
+                    ajaxPost('/project/save', updateData).then(() => {
+                        updateData.update_data[0].data.forEach(rData => {
+                            let thisUpdate = rData.updateData;
+                            let rationNode = projectObj.project.mainTree.nodes['id_' + thisUpdate.ID];
+                            if (rationNode) {
+                                rationNode.data.jobContentText = thisUpdate.jobContentText;
+                            }
+                        });
+                        TREE_SHEET_HELPER.massOperationSheet(projectObj.mainController.sheet, function () {
+                            TREE_SHEET_HELPER.refreshTreeNodeData(projectObj.mainController.setting,
+                                projectObj.mainController.sheet, node.children, false);
+                        });
+                    });
+                } else {
+                    TREE_SHEET_HELPER.massOperationSheet(projectObj.mainController.sheet, function () {
+                        TREE_SHEET_HELPER.refreshTreeNodeData(projectObj.mainController.setting,
+                            projectObj.mainController.sheet, node.children, false);
+                    });
+                }
+            }
         });
 
     },

+ 27 - 26
web/building_saas/main/js/views/export_view.js

@@ -11,7 +11,8 @@
 const ExportView = (() => {
     let _base = XML_EXPORT_BASE,
         _cache = _base.CACHE;
-    let xmlObj = null;
+    // 导出数据缓存
+    let _exportCache = [];
     //设置工程编号表格数据设置
     const sheetSetting = {
         header: [
@@ -48,13 +49,13 @@ const ExportView = (() => {
         let rst = [];
         //排除建设项目行
         for (let row = 1; row < sheet.getRowCount(); row++) {
-            rst.push(sheet.getValue(row, codeCol) || '');
+            rst.push(sheet.getText(row, codeCol) || '');
         }
         return rst;
     }
     // 回到初始状态,需要清空cache中的数据
     function resetState() {
-        xmlObj = null;
+        _exportCache = [];
         _cache.clear();
     }
     //事件监听
@@ -72,14 +73,16 @@ const ExportView = (() => {
             STATE.checking = true;
             let pr = new SCComponent.InitProgressBar();
             try {
-                if (!xmlObj || !xmlObj.originalDatas.length) {
+                if (!_exportCache || !_exportCache.length) {
                     pr.start('导出数据接口', '正在自检,请稍候……');
-                    xmlObj = new XMLStandard(userID, 1);
                     for (let checkedData of checkedDatas) {
-                        let fileKind = $(checkedData).val();
-                        await xmlObj.transformData(projectObj.project.ID(), fileKind);
+                        let fileKind = parseInt($(checkedData).val());
+                        let exportData = await _base.extractExportData(XMLStandard.entry, _base.CONFIG.GRANULARITY.PROJECT,
+                                fileKind, projectObj.project.ID(), userID);
+                        _exportCache.push(...exportData);
                     }
                 }
+                failList = [...new Set(failList)];
                 //设置提示弹窗
                 if (failList.length * 20 > 400) {
                     $('#hintBox_caption').addClass('export-check');
@@ -100,6 +103,7 @@ const ExportView = (() => {
 
         //导出接口,如果没有错误,弹出工程编号设置窗口
         $('#export-confirm').click(async function () {
+            let failList = _cache.getItem('failList');
             let checkedDatas = $('#export input[type="checkbox"]:checked');
             if (!checkedDatas.length) {
                 return;
@@ -111,24 +115,25 @@ const ExportView = (() => {
             let pr = new SCComponent.InitProgressBar();
             try {
                 let isPring = false;    //是否调用了进度条(控制工程窗口什么时候显示,优化交互)
-                if (!xmlObj || !xmlObj.originalDatas.length) {
+                if (!_exportCache || !_exportCache.length) {
                     isPring = true;
                     pr.start('导出数据接口', '正在导出文件,请稍候……');
-                    xmlObj = new XMLStandard(userID, 1);
                     for (let checkedData of checkedDatas) {
-                        let fileKind = $(checkedData).val();
-                        await xmlObj.transformData(projectObj.project.ID(), fileKind);
+                        let fileKind = parseInt($(checkedData).val());
+                        let exportData = await _base.extractExportData(XMLStandard.entry, _base.CONFIG.GRANULARITY.PROJECT,
+                            fileKind, projectObj.project.ID(), userID);
+                        _exportCache.push(...exportData);
                     }
                     pr.end();
                 }
+                failList = [...new Set(failList)];
                 //错误-设置提示弹窗
-                //不弹出
-                /*if (_failList.length * 20 > 400) {
+                if (failList.length * 20 > 400) {
                     $('#hintBox_caption').addClass('export-check');
                 }
-                if (_failList.length) {
-                    throw _failList.join('<br/>');
-                }*/
+                if (failList.length) {
+                    throw failList.join('<br/>');
+                }
                 //弹出工程编号设置窗口
                 if (isPring) {
                     setTimeout(() => {
@@ -138,6 +143,7 @@ const ExportView = (() => {
                     $('#exportCode').modal('show');
                 }
             } catch (err) {
+                console.log(err);
                 pr.end();
                 alert(err);
             }
@@ -148,17 +154,17 @@ const ExportView = (() => {
         //工程编号设置窗口-----
         //设置工程编号
         $('#exportCode').on('shown.bs.modal', function () {
-            if (!xmlObj) {
+            if (!_exportCache || !_exportCache.length) {
                 alert('数据错误!');
                 $(this).modal('hide');
                 return false;
             }
-            let projectsData = _cache.getItem('projectsData');
-            initSpread(XML_EXPORT_BASE.UTIL.getCodeSheetData(projectsData));
+            let projectData = _cache.getItem('projectData');
+            initSpread(XML_EXPORT_BASE.UTIL.getCodeSheetData(projectData));
         });
         //设置完工程编号后,导出数据。如果选中多个文件,导出压缩包
         $('#exportCode-confirm').click(async function () {
-            if (!spread || !xmlObj) {
+            if (!spread || !_exportCache || !_exportCache.length) {
                 return false;
             }
             if (STATE.confirming) {
@@ -179,15 +185,13 @@ const ExportView = (() => {
                     STATE.confirming = false;
                     return false;
                 }
-                XML_EXPORT_BASE.UTIL.setupCode(xmlObj.originalDatas, codes, '单项工程', '单位工程', '编号');
                 pr.start('导出数据接口', '正在导出文件,请稍候……');
                 // 导出文件
-                await XML_EXPORT_BASE.exportFile(xmlObj.originalDatas);
+                await _base.exportFile(codes, _exportCache, XMLStandard.resetContentCode, XMLStandard.saveAsFile);
             } catch (err) {
                 console.log(err);
                 alert(err);
             }
-            console.log(xmlObj);
             pr.end();
             $('#exportCode').modal('hide');
             $('#export').modal('hide');
@@ -197,9 +201,6 @@ const ExportView = (() => {
 
         });
         //导出窗口--------
-        $('#export').on('show.bs.modal', function () {
-            xmlObj = null;
-        });
         $('#export').on('hide.bs.modal', function() {
             resetState();
             STATE.checking = false;

+ 4 - 3
web/building_saas/main/js/views/glj_view.js

@@ -716,14 +716,14 @@ var gljOprObj = {
         }
         this.sheetData = newList;
     },
-    combineWithProjectGlj: function (ration_gljs,needRatio=true) {
+    combineWithProjectGlj: function (ration_gljs,needRatio=true,ration,p_gljMap) {
         let projectGLJData = projectObj.project.projectGLJ.datas;
         let projectGljs = projectGLJData.gljList;
         let mixRatioMap = projectGLJData.mixRatioMap;
         if (ration_gljs && ration_gljs.length > 0 && projectGljs && projectGljs.length > 0) {
-
+            let pgljMap = p_gljMap?p_gljMap:_.indexBy(projectGljs, 'id');
             for (let i = 0; i < ration_gljs.length; i++) {
-                let glj = _.find(projectGljs, {'id': ration_gljs[i].projectGLJID});
+                let glj = pgljMap[ration_gljs[i].projectGLJID];
                 if (glj) {
                     if(projectObj.project.projectGLJ.isEstimateType(ration_gljs[i].type )){
                         ration_gljs[i].isEstimate = glj.is_evaluate;
@@ -736,6 +736,7 @@ var gljOprObj = {
                         let mixRatios = this.getMixRationShowDatas(mixRatioMap[connect_index], projectGljs);
                         ration_gljs[i].subList = mixRatios;
                     }
+                    if(ration) gljOprObj.getTotalQuantity(ration_gljs[i], ration);
                 }
             }
         }

+ 277 - 21
web/building_saas/main/js/views/locate_view.js

@@ -42,6 +42,31 @@ let locateObject={
         }
     },
     datas:[],
+    bookMarkSetting:{
+        header:[
+            {headerName: "编码", headerWidth: 120, dataCode: "code", dataType: "String"},
+            {headerName: "书签名称", headerWidth: 200, dataCode: "name", dataType: "String"},
+        ],
+        view: {
+            lockColumns: [0,1],
+            colHeaderHeight:30
+        }
+    },
+    bookmarkDatas:[],
+    seletedNodeID:null,//右键设置书签时选中的节点ID临时存储
+    //为了兼容旧项目,这里给个默认值
+    bookmarkPropertySetting:{
+        settingList :[
+            {background:"E2F2C5",describe:""},
+            {background:"F9E2CF",describe:""},
+            {background:"F2EFD9",describe:"hehe"},
+            {background:"F5D1DA",describe:""},
+            {background:"E3E3E3",describe:""},
+            {background:"B6F3F2",describe:""},
+            {background:"ECE0F5",describe:""}
+        ],
+        selected:"E2F2C5"
+    },
     initMainSpread:function(){
         if(!this.mainSpread){
             this.mainSpread = SheetDataHelper.createNewSpread($("#locate_result")[0],3);
@@ -74,13 +99,22 @@ let locateObject={
             this.subSpread.refresh();
         }
     },
-    refreshWorkBook: function () {
-        if (this.mainSpread) {
-            this.mainSpread.refresh();
-        }
-        if (this.subSpread) {
-            this.subSpread.refresh();
+    initBookmarkSpread:function(){
+        if(!this.bookmarkSpread){
+            this.bookmarkSpread = SheetDataHelper.createNewSpread($("#bookmarkSpread")[0]);
+            sheetCommonObj.spreadDefaultStyle(this.bookmarkSpread);
+            this.initBookmarkSheet();
+            this.bookmarkSpread.bind(GC.Spread.Sheets.Events.CellDoubleClick,this.onSheetDoubleClick);
+            if(!projectReadOnly) this.initBookmarkRightClick();
+        }else {
+            this.bookmarkSpread.refresh();
         }
+        this.showBookmarkDatas();
+    },
+    refreshWorkBook: function () {
+        if (this.mainSpread) this.mainSpread.refresh();
+        if (this.subSpread) this.subSpread.refresh();
+        if (this.bookmarkSpread) this.bookmarkSpread.refresh();
     },
     initSubSheet:function () {
         this.subSheet = this.subSpread .getSheet(0);
@@ -88,12 +122,33 @@ let locateObject={
         this.subSheet.setRowCount(0);
         this.subSheet.name('locate_sub');
     },
+    initBookmarkSheet:function () {
+        this.bookmarkSheet = this.bookmarkSpread .getSheet(0);
+        sheetCommonObj.initSheet( this.bookmarkSheet, this.bookMarkSetting);
+        this.bookmarkSheet.setRowCount(0);
+        this.bookmarkSheet.name('bookmark');
+        this.bookmarkSheet.bind(GC.Spread.Sheets.Events.SelectionChanged,this.bookmarkSelectionChange);
+    },
     initOutstanding:function () {
         if(!projectObj.project.property.locateSetting) return;
         let outstd = projectObj.project.property.locateSetting;
         $("#outstanding").prop("checked",outstd.outstanding);
         $("#outInp").val(parseFloat(outstd.outInp));
     },
+    initBookmarkSetting:function () {
+        $("#bookmarkSettingList").empty();
+        let setting = projectObj.project.property.bookmarkSetting?projectObj.project.property.bookmarkSetting:this.bookmarkPropertySetting;
+        for(let s of setting.settingList){
+            let b = `<div class="input-group input-group-sm mb-2" style="width:200px">
+                          <div class="input-group-prepend">
+                              <span class="input-group-text " style="background: #${s.background}">&nbsp;&nbsp;&nbsp;&nbsp;</span>
+                          </div>
+                          <input type="text" class="form-control" id="${s.background}"  placeholder="描述" value="${s.describe}">
+                     </div>`;
+            $("#bookmarkSettingList").append(b);
+            $("#"+s.background).bind('focus', bookmarkSelected);
+        }
+    },
     showMainData:function (datas,setting) {
         sheetCommonObj.showData(this.mainSpread.getActiveSheet(),setting,datas);
         this.mainSpread.getActiveSheet().setRowCount(datas.length);
@@ -103,7 +158,44 @@ let locateObject={
         sheetCommonObj.showData(this.subSheet,this.ration_setting,this.subRationDatas);
         this.subSheet.setRowCount(this.subRationDatas.length);
     },
-
+    showBookmarkDatas:function(refresh = false){
+        let sheet = this.bookmarkSheet;
+        let sel = sheet.getSelections()[0];
+        let oldData = sel.row<this.bookmarkDatas.length?this.bookmarkDatas[sel.row]:null;
+        this.bookmarkDatas = this.getBookmarkDatas();
+        sheetCommonObj.showData(sheet, this.bookMarkSetting,this.bookmarkDatas);
+        let selectedID = null;
+        let focus = false;
+        if(oldData) selectedID = oldData.ID;
+        if(this.seletedNodeID && refresh == false){
+            selectedID =  this.seletedNodeID;
+            this.seletedNodeID = null;
+            focus = true;
+        }
+        sel.row = selectedID?_.findIndex(this.bookmarkDatas,{'ID':selectedID}):0;
+        sheet.setSelection(sel.row==-1?0:sel.row,sel.col,sel.rowCount,sel.colCount);
+        sheet.setRowCount(this.bookmarkDatas.length);
+        if(sel.row!=-1) sheet.showRow(sel.row, GC.Spread.Sheets.VerticalPosition.bottom);
+        this.showAnnotation(sel.row,focus);
+    },
+    getBookmarkDatas:function(){
+        let datas = [];
+        let nodes = projectObj.project.mainTree.items;
+        for(let n of nodes){
+            if(!_.isEmpty(n.data.bookmarkBackground)){
+                let tem = {
+                    ID:n.data.ID,
+                    code:n.data.code,
+                    type:n.sourceType,
+                    name:n.data.name,
+                    bookmarkAnnotation:n.data.bookmarkAnnotation,
+                    bgColour:"#"+n.data.bookmarkBackground
+                };
+                datas.push(tem);
+            }
+        }
+        return datas;
+    },
     getSubRationDatas:function () {
         let datas = [];
         let sheet = this.mainSpread.getActiveSheet();
@@ -123,19 +215,26 @@ let locateObject={
         let me = this;
         let mainHeight = $(window).height()-$(".header").height()-$(".toolsbar").height()-$("#searchPanel").height();
         let subHeight = 0;
-        $('#locateTopDiv').height(mainHeight);
-        $('#locate_result').height(mainHeight);
-        $('#locateBottomDiv').height(subHeight);
-        $('#locate_sub').height(subHeight - 7);
-        if(options == "ration_glj"){
-            let locateLibResize = getLocateLibResize();
-            SlideResize.loadVerticalHeight(locateLibResize.eleObj.module, locateLibResize.eleObj, locateLibResize.limit, function () {
-                locateObject.refreshWorkBook();
-            });
+        if(options == "bookmark"){
+            loadHeight(getBookmarkResize());
+        }else {
+            $('#locateTopDiv').height(mainHeight);
+            $('#locate_result').height(mainHeight);
+            $('#locateBottomDiv').height(subHeight);
+            $('#locate_sub').height(subHeight - 7);
+            if(options == "ration_glj"){
+                loadHeight(getLocateLibResize());
+            }
         }
         if (refreshWorkBook) {
             me.refreshWorkBook();
         }
+
+        function loadHeight(libResize) {
+            SlideResize.loadVerticalHeight(libResize.eleObj.module, libResize.eleObj, libResize.limit, function () {
+                locateObject.refreshWorkBook();
+            });
+        }
     },
     init:function () {
         let me = this;
@@ -144,9 +243,20 @@ let locateObject={
             me.refreshView(options, false);
             me.initMainSpread();
             me.initSubSpread();
+            me.initBookmarkSpread();
         };
-        if(options == "bills") me.initOutstanding();
-        options == "bills"?$("#outstandingOptions").show(0,callback):$("#outstandingOptions").hide(0,callback);
+        if(options == 'bookmark'){
+            $("#aboutLocateDiv").hide();
+            $("#outstandingOptions").hide();
+            me.initBookmarkSetting();
+            $("#aboutBookmarkDiv").show(0,callback);
+        }else {
+            $("#aboutBookmarkDiv").hide();
+            $("#aboutLocateDiv").show(0,function () {
+                if(options == "bills") me.initOutstanding();
+                options == "bills"?$("#outstandingOptions").show(0,callback):$("#outstandingOptions").hide(0,callback);
+            });
+        }
 
     },
     findRecodes:function () {
@@ -297,6 +407,7 @@ let locateObject={
         if(options == "ration_glj"&&sheetName != "locate_sub" ) return;
         let datas = options == "bills"? me.billsDatas:me.rationDatas;
         if( args.sheet.name() == "locate_sub") datas = me.subRationDatas;
+        if(args.sheet.name() == "bookmark") datas = me.bookmarkDatas;
         me.locateNode(datas[args.row].ID);
     },
     gljSelectionChange:function (e,args) {
@@ -307,18 +418,140 @@ let locateObject={
             me.showSubRateDatas();
         }
     },
+    bookmarkSelectionChange:function (e,args) {
+        let me = locateObject;
+        let newSel = args.newSelections[0];
+        let oldSel = args.oldSelections?args.oldSelections[0]:{};
+        if(newSel.row != oldSel.row){
+            me.showAnnotation(newSel.row);
+        }
+    },
+    showAnnotation:function (row,focus = false) {
+        let me = locateObject;
+        let recode =   me.bookmarkDatas[row];
+        if(recode){
+            $("#annotationTextarea").val(recode.bookmarkAnnotation);
+            $("#bookmarkNodeID").val(recode.ID);
+            $("#bookmarkNodeType").val(recode.type);
+            if(focus) $("#annotationTextarea").focus();
+        }
+    },
     locateNode:function (ID) {
         let node =  projectObj.project.mainTree.findNode(ID);
         if(node) projectObj.loadFocusLocation(node.serialNo(),1);
     },
+    updateProjectProperty:function(fieldID,property){
+        let updateData = {type:ModuleNames.project,data:{'ID' : projectObj.project.ID()}};//,'property.locateSetting':outstd
+        updateData.data["property."+fieldID] = property;
+        $.bootstrapLoading.start();
+        projectObj.project.updateNodes([updateData],function () {
+            $.bootstrapLoading.end();
+            projectObj.project.property[fieldID] = property;
+        });
+    },
     updateOutStanding:function (outstanding,outInp) {
         let outstd = {outstanding:outstanding,outInp:outInp};
-        let updateData = {type:ModuleNames.project,data:{'ID' : projectObj.project.ID(),'property.locateSetting':outstd}};
+        this.updateProjectProperty('locateSetting',outstd);
+    },
+    updateBookmarkSetting:function () {
+        let setting = projectObj.project.property.bookmarkSetting?projectObj.project.property.bookmarkSetting:locateObject.bookmarkPropertySetting;
+        for(let s of setting.settingList){
+            s.describe = $("#"+s.background).val();
+        }
+        setting.selected = $("#bookmarkSelected").val();
+        this.updateProjectProperty("bookmarkSetting",setting);
+    },
+    setOrCancelBookmark:function(node){
+        $("#bookmark").prop("checked", true);
+        let setting = projectObj.project.property.bookmarkSetting?projectObj.project.property.bookmarkSetting:locateObject.bookmarkPropertySetting;
+        let background = null;
+        if(_.isEmpty(node.data.bookmarkBackground)) background = setting.selected;
+        let updateData = {type:node.sourceType,data:{'ID' : node.data.ID,bookmarkBackground:background}};
+        if(background == null) updateData.data.bookmarkAnnotation = null;
+        $.bootstrapLoading.start();
         projectObj.project.updateNodes([updateData],function () {
-            projectObj.project.property.locateSetting = outstd;
+            $.bootstrapLoading.end();
+            node.data.bookmarkBackground = background;
+            projectObj.mainController.refreshTreeNode([node]);
+            locateObject.seletedNodeID = node.data.ID;
+            //如果没有打开,自动打开书签批注设置
+            $('#locate').is(':visible')?locateObject.init():$("#locateTab").click();
+        });
+    },
+    initBookmarkRightClick:function(){
+        //如果当前行是无组成物的“普通材料”、“绿化苗木”、“外购砼构件”、“商品混凝土”、“商品砂浆”,则右键“添加计算材料”按钮有效。
+        let me = this;
+        $.contextMenu({
+            selector: '#bookmarkSpread',
+            build: function ($trigger, e) {
+                me.rightClickTarget = SheetDataHelper.safeRightClickSelection($trigger, e, me.bookmarkSpread);
+                return me.rightClickTarget.hitTestType === GC.Spread.Sheets.SheetArea.viewport ||
+                    me.rightClickTarget.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
+            },
+            items: {
+                "deleteBookmark": {
+                    name: "删除书签批注",
+                    icon: 'fa-remove',
+                    disabled: function () {
+                        return  me.bookmarkDatas.length == 0;
+                    },
+                    callback: function (key, opt) {
+                        me.deleteSelectedBookmark();
+                    }
+                },
+                "deleteAllBookmarks": {
+                    name: "删除所有书签批注",
+                    icon: 'fa-remove',
+                    disabled: function () {
+                        return  me.bookmarkDatas.length == 0;
+                    },
+                    callback: function (key, opt) {
+                        me.deleteBookmarkByDatas(me.bookmarkDatas);
+                    }
+                }
+            }
+        });
+    },
+    deleteSelectedBookmark:function () {
+        let sel = this.bookmarkSheet.getSelections()[0];
+        if(sel.row != -1 && this.bookmarkDatas.length>sel.row){
+            let record = this.bookmarkDatas[sel.row];
+            this.deleteBookmarkByDatas([record]);
+        }
+    },
+    deleteBookmarkByDatas:function(datas){
+        let postDatas = [];
+        for(let d of datas){
+            let tem = {
+                type:d.type,
+                data:{
+                    'ID':d.ID,
+                    'bookmarkAnnotation':null,
+                    'bookmarkBackground':null
+                }
+            };
+            postDatas.push(tem);
+        }
+        this.updateBookmarkNodes(postDatas);
+    },
+    updateBookmarkNodes:function (postDatas,refreshsNode = true) {
+        $.bootstrapLoading.start();
+        projectObj.project.updateNodes(postDatas,function () {
+            $.bootstrapLoading.end();
+            let nodes = [];
+            for(let p of postDatas){
+                let node =  projectObj.project.mainTree.findNode(p.data.ID);
+                if(!node) continue;
+                for(let key in p.data){
+                    if(key == 'ID') continue;
+                    node.data[key] = p.data[key];
+                }
+                nodes.push(node);
+            }
+            locateObject.showBookmarkDatas();
+            if(refreshsNode) projectObj.mainController.refreshTreeNode(nodes);
         });
     }
-
 };
 
 
@@ -367,3 +600,26 @@ $('#outstanding').change(function(){
     let outStd = $("#outstanding").prop("checked");
     me.updateOutStanding(outStd,scMathUtil.roundForObj(outInp,getDecimal('process')));
 });
+
+$("#annotationTextarea").change(function(){
+    let updateData = {type:$("#bookmarkNodeType").val(),data:{'ID' :$("#bookmarkNodeID").val(),'bookmarkAnnotation':$(this).val()}};
+    let node =  projectObj.project.mainTree.findNode($("#bookmarkNodeID").val());
+    if(!node) return;
+    locateObject.updateBookmarkNodes([updateData],false);
+});
+
+$('#bookmarkSettingDropdown').on('shown.bs.dropdown', function showDropdown() {
+    locateObject.initBookmarkSetting();
+    let setting = projectObj.project.property.bookmarkSetting?projectObj.project.property.bookmarkSetting:locateObject.bookmarkPropertySetting;
+    $("#"+setting.selected).focus();
+});
+
+$("#bookmarkSettingConfirm").click(function () {
+    locateObject.updateBookmarkSetting();
+    $("#bookmarkSettingDropdown").click();
+});
+
+
+function bookmarkSelected() {
+    $("#bookmarkSelected").val($(this)[0].id);
+}

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

@@ -1,7 +1,6 @@
 /**
  * Created by Mai on 2017/7/25.
  */
-
 let MainTreeCol = {
     getText: {
         subType: function (node) {
@@ -115,6 +114,10 @@ let MainTreeCol = {
             // 2018-11-15 zhang
             let Bills =projectObj.project.Bills;
             if(node.sourceType == ModuleNames.bills){//针对清单、分项部分
+                // 属于标题清单,只读
+                if (calcTools.isTitleBills(node)) {
+                    return true;
+                }
                 // 当前属于分部分项、施工技术措施项目,综合单价只读。
                 if(Bills.isFBFX(node)||Bills.isTechMeasure(node)) return true;
                 // 不属于分部分项、施工技术措施项目的部分,如果不是叶子清单,或有基数计算/定额/量价/人材机 只读
@@ -125,6 +128,11 @@ let MainTreeCol = {
             return false;
         },
         commonTotalFee: function (node) {
+            // 属于标题清单,只读
+            if (node.sourceType == ModuleNames.bills &&
+                calcTools.isTitleBills(node)) {
+                return true;
+            }
             // 09-29 zhang
             let Bills =projectObj.project.Bills;
             // 当前属于分部分项、施工技术措施项目,综合单价只读。
@@ -196,6 +204,11 @@ let MainTreeCol = {
             return (!MainTreeCol.readOnly.billsParent(node)) && (node.children.length > 0);
         },
         forCalcBase: function (node) {
+            // 属于标题清单,只读
+            if (node.sourceType == ModuleNames.bills &&
+                calcTools.isTitleBills(node)) {
+                return true;
+            }
             // to do according to billsParentType
             return MainTreeCol.readOnly.billsParent(node) || MainTreeCol.readOnly.non_bills(node) || MainTreeCol.readOnly.leafBillsWithDetail(node) || MainTreeCol.readOnly.calcBaseType(node);
         },
@@ -210,6 +223,11 @@ let MainTreeCol = {
         },
         forFeeRate: function (node) {
             let readOnly = true;
+            // 属于标题清单,只读
+            if (node.sourceType == ModuleNames.bills &&
+                calcTools.isTitleBills(node)) {
+                return true;
+            }
             let Bills = projectObj.project.Bills;
             //只有当选中节点是清单,并且不属于“分部分项工程”或“技术措施项目”,并且是叶子清单且没有定额/量价/工料机时,费率列才可编辑
             if(MainTreeCol.readOnly.bills(node)){
@@ -221,6 +239,10 @@ let MainTreeCol = {
         },
         forQuantity: function (node) {
             if(node.sourceType === projectObj.project.Bills.getSourceType()){
+                // 属于标题清单,只读
+                if (calcTools.isTitleBills(node)) {
+                    return true;
+                }
                 if(node.data.type==billType.DXFY||node.data.type==billType.FB||(node.data.type==billType.BILL&&MainTreeCol.readOnly.billsParent(node))){//大项费用、分部、清单父项行,工程量只读。
                     return true;
                 }
@@ -248,6 +270,11 @@ let MainTreeCol = {
             return false;
         },
         forName:function (node) {
+            // 属于标题清单,只读
+            if (node.sourceType == ModuleNames.bills &&
+                calcTools.isTitleBills(node)) {
+                return true;
+            }
             if(projectObj.project.Bills.isTopThreeNode(node)){//是大项1、2、3项的编号设置为只读
                 return true;
             }
@@ -260,7 +287,13 @@ let MainTreeCol = {
                return calcTools.isRationItem(node);
             }
         },
-        forContentCharacter: function (node) {
+        forContentCharacter: function (node, field) {
+            // 定额工作内容下拉
+            if (_compilationConfig.dynamicRationWorkContent &&
+                node.sourceType === projectObj.project.Ration.getSourceType() &&
+                field === 'jobContentText') {
+                return false;
+            }
             return !MainTreeCol.readOnly.bills(node) || (node.data.type !== billType.BILL && node.data.type !== billType.FX);
         },
         forRuleText: function (node) {
@@ -295,7 +328,16 @@ let MainTreeCol = {
         units: function () {
             this.unit;
         },
-
+        jobContent: function (node) {
+            // 定额工作内容下拉
+            if (_compilationConfig.dynamicRationWorkContent &&
+                node.sourceType === projectObj.project.Ration.getSourceType()) {
+                let combo = sheetCommonObj.getDynamicCombo();
+                combo._maxDropDownItems = 10;
+                combo.items(contentOprObj.getContentTexts(node.parent));
+                return combo;
+            }
+        },
         feeRate: function () {
             return feeRateObject.getFeeRateEditCellType();
         },

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

@@ -927,7 +927,6 @@ var projectObj = {
                 // that.project.initCalcFields();
                 let str = JSON.stringify(that.project.projSetting.main_tree_col);
                 that.project.projSetting.mainGridSetting = JSON.parse(str);
-                console.log(that.project.projSetting.mainGridSetting);
                 that.project.projSetting.mainGridSetting.frozenCols = 4;
                 TREE_SHEET_HELPER.initSetting($('#billsSpread')[0], that.project.projSetting.mainGridSetting);
                 that.project.projSetting.mainGridSetting.setAutoFitRow = MainTreeCol.getEvent("setAutoFitRow");
@@ -1460,6 +1459,20 @@ var projectObj = {
                         return false;
                     },
                 },
+                "setBookMark": {
+                    name: '设置/取消书签批注',
+                    icon: 'fa-flag',
+                    disabled: function () {
+                        if (projectReadOnly || project.mainTree.selected.sourceType == ModuleNames.ration_glj) {
+                            return true;
+                        }
+                        return false
+                    },
+                    callback: function () {
+                        locateObject.setOrCancelBookmark(project.mainTree.selected)
+                    }
+
+                },
                 "delete": {
                     name: '删除',
                     icon: 'fa-remove',
@@ -1900,6 +1913,7 @@ var projectObj = {
         if(node === tree.selected){
              style.backColor = colorSetting[mapping.SELECTED]['backColor'];
         }
+        if(_ && !_.isEmpty(node.data.bookmarkBackground)) style.backColor = "#"+ node.data.bookmarkBackground;//设置书签和批注背景色
         //大项费用加粗(数字与中文字符大小不一问题由字体造成,暂时不考虑分别设置大小)
        /* if(node.sourceType === this.project.Bills.getSourceType() && node.data.type === billType.DXFY){
             style.font = 'bold 0.9rem Arial';

+ 25 - 0
web/building_saas/main/js/views/side_tools.js

@@ -98,6 +98,31 @@ SlideResize.verticalSlide(locateLibResize.eleObj, locateLibResize.limit, functio
     locateObject.refreshWorkBook();
 });
 
+function getBookmarkResize() {
+    let resizeObj = {};
+    resizeObj.eleObj = {
+        module: 'bookmark',
+        resize: $('#bookmark_resize'),
+        top: $('#bookmarkTopDiv'),
+        topSpread: $('#bookmarkSpread'),
+        bottom: $('#annotationDiv'),
+        bottomSpread: $('#annotationTextarea')
+    };
+    resizeObj.limit = {
+        min: 150,
+        max: `$(window).height()-$('.header').height()-$('.toolsbar').height()-$('#searchPanel').height()-100-5`,//5: resize.height()
+        notTopSpread: 35,
+        notBottomSpread: 15,
+        totalHeight: `$(window).height()-$('.header').height()-$('.toolsbar').height()-$('#searchPanel').height()-5`//5: resize.height()
+    };
+    return resizeObj;
+}
+
+let bookMarkLibResize = getBookmarkResize();
+SlideResize.verticalSlide(bookMarkLibResize.eleObj, bookMarkLibResize.limit, function () {
+    locateObject.refreshWorkBook();
+});
+
 // 块模板库上下拖动(上中)
 let blockLibTopMid = {};
 blockLibTopMid.eleObj = {

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

@@ -666,8 +666,8 @@ const billsGuidance = (function () {
                 elfItem.workBook = null;
             }
             initViews();
+            initTree(bills, bills.workBook.getActiveSheet(), bills.treeSetting, rstData.bills);
             let callback = function () {
-                initTree(bills, bills.workBook.getActiveSheet(), bills.treeSetting, rstData.bills);
                 if(doAfterLoadGuidance){
                     doAfterLoadGuidance();
                 }

+ 0 - 34
web/building_saas/pm/js/pm_newMain.js

@@ -1044,15 +1044,6 @@ const projTreeObj = {
             let node = tree.items[hitinfo.row];
             let centerX = hitinfo.cellRect.x + offset + node.depth() * indent + node.depth() * levelIndent + indent / 2;
             let centerY = (hitinfo.cellRect.y + offset + (hitinfo.cellRect.y + offset + hitinfo.cellRect.height)) / 2;
-            let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
-            let value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
-            let acStyle = hitinfo.sheet.getActualStyle(hitinfo.row, hitinfo.col),
-                zoom = hitinfo.sheet.zoom();
-            let textLength = this.getAutoFitWidth(value, text, acStyle, zoom, {sheet: hitinfo.sheet, row: hitinfo.row, col: hitinfo.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport});
-            //(图标+名字)区域
-            function withingClickArea(){
-                return hitinfo.x > centerX + halfBoxLength && hitinfo.x < centerX + halfBoxLength + imgWidth + indent/2+3 + textLength;
-            }
             if(hitinfo.sheet.name() === "projectSheet"){//只有项目管理界面才执行
                 if(node.data.projType === projectType.tender){
                     let thisClick = Date.now(),
@@ -1087,25 +1078,9 @@ const projTreeObj = {
                 hitinfo.sheet.repaint();
             }
         };
-        /*TreeNodeCellType.prototype.processMouseMove = function (hitInfo) {
-            let sheet = hitInfo.sheet;
-            let div = sheet.getParent().getHost();
-            let canvasId = div.id + "vp_vp";
-            let canvas = $(`#${canvasId}`)[0];
-            let node = tree.items[hitInfo.row];
-            //改变鼠标图案
-            if (sheet && hitInfo.isReservedLocation) {
-                canvas.style.cursor='pointer';
-                return true;
-            }else{
-                canvas.style.cursor='default';
-            }
-            return false;
-        };*/
         TreeNodeCellType.prototype.processMouseEnter = function (hitinfo) {
             let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
             let value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
-            let tag = hitinfo.sheet.getTag(hitinfo.row, hitinfo.col);
             let acStyle = hitinfo.sheet.getActualStyle(hitinfo.row, hitinfo.col),
                 zoom = hitinfo.sheet.zoom();
             let node = me.tree.items[hitinfo.row];
@@ -1117,7 +1092,6 @@ const projTreeObj = {
             }
         };
         TreeNodeCellType.prototype.processMouseLeave = function (hitinfo) {
-            let me = this;
             TREE_SHEET_HELPER.tipDiv = 'hide';
             if (TREE_SHEET_HELPER._toolTipElement) {
                 $(TREE_SHEET_HELPER._toolTipElement).hide();
@@ -1528,7 +1502,6 @@ const projTreeObj = {
             children.push(newNode);
             for(let c of children){
                 sheet.getCell(c.serialNo(), 0).cellType(me.getTreeNodeCell(me.tree));
-                // me.refreshNodeData(c);
             }
             refreshNodes = refreshNodes.concat(children);
             me.refreshNodeData(refreshNodes);
@@ -1676,7 +1649,6 @@ $(document).ready(function() {
     //列宽随着屏幕改变
     $(window).resize(function () {
         sheetCommonObj.setColumnWidthByRate(getWorkBookWidth(), gcTreeObj.workBook, gcTreeObj.setting.header);
-        //sheetCommonObj.setColumnWidthByRate(getWorkBookWidth(), pmShare.spreadObj.workBook, pmShare.headers);
 
     });
 
@@ -1690,12 +1662,6 @@ $(document).ready(function() {
         init();
     });
 
-    // 侧滑数据
-   /* $(".poj-list").on('click', ".open-sidebar", function() {
-        setDataToSideBar();
-        $(".slide-sidebar").animate({width:"800"}).addClass("open");
-    });*/
-
     //单价、费率文件删除确认
     $('#fileDelConfirm').click(function () {
         if(fileDelObj && fileDelObj.id){

+ 0 - 35
web/building_saas/pm/js/pm_share.js

@@ -353,25 +353,8 @@ const pmShare = (function () {
             let node = tree.items[hitinfo.row];
             let centerX = hitinfo.cellRect.x + offset + node.depth() * indent + node.depth() * levelIndent + indent / 2;
             let centerY = (hitinfo.cellRect.y + offset + (hitinfo.cellRect.y + offset + hitinfo.cellRect.height)) / 2;
-            let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
-            let value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
-            let acStyle = hitinfo.sheet.getActualStyle(hitinfo.row, hitinfo.col),
-                zoom = hitinfo.sheet.zoom();
-            let textLength = this.getAutoFitWidth(value, text, acStyle, zoom, {sheet: hitinfo.sheet, row: hitinfo.row, col: hitinfo.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport});
-            //(图标+名字)区域
-            function withingClickArea(){
-                return hitinfo.x > centerX + halfBoxLength && hitinfo.x < centerX + halfBoxLength + imgWidth + indent/2+3 + textLength;
-            }
             //点击单位工程
             if(node.data.projType === projectType.tender){
-                /*let newTab = window.open('about:blank');
-                //打开项目的实际ID
-                BeforeOpenProject(node.data.actualTreeInfo.ID, {'fullFolder': GetFullFolder(node.parent)}, function () {
-                    let mainUrl = `/main?project=${node.data.actualTreeInfo.ID}`;
-                    CommonAjax.get(mainUrl, [], function () {
-                        newTab.location.href = mainUrl;
-                    });
-                });*/
                 let thisClick = Date.now(),
                     open = false;
                 if (this.preNode === node && this.preClick && thisClick - this.preClick <= 300) {
@@ -399,24 +382,9 @@ const pmShare = (function () {
                 hitinfo.sheet.repaint();
             }
         };
-        /*TreeNodeCellType.prototype.processMouseMove = function (hitInfo) {
-            let sheet = hitInfo.sheet;
-            let div = sheet.getParent().getHost();
-            let canvasId = div.id + "vp_vp";
-            let canvas = $(`#${canvasId}`)[0];
-            //改变鼠标图案
-            if (sheet && hitInfo.isReservedLocation) {
-                canvas.style.cursor='pointer';
-                return true;
-            }else{
-                canvas.style.cursor='default';
-            }
-            return false;
-        };*/
         TreeNodeCellType.prototype.processMouseEnter = function (hitinfo) {
             let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
             let value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
-            let tag = hitinfo.sheet.getTag(hitinfo.row, hitinfo.col);
             let acStyle = hitinfo.sheet.getActualStyle(hitinfo.row, hitinfo.col),
                 zoom = hitinfo.sheet.zoom();
             let node = tree.items[hitinfo.row];
@@ -748,9 +716,6 @@ const pmShare = (function () {
         //新建未分类建设项目及单项工程
         let ungroupedProj = {ID: uuid.v1(), ParentID: -1, NextSiblingID: firstID, name: '未分类建设项目', projType: projectType.project};
         let ungroupedEng = {ID: uuid.v1(), ParentID: ungroupedProj.ID, NextSiblingID: -1, name: '未分类单项工程', projType: projectType.engineering};
-        /*if (groupedDatas.length > 0) {
-            groupedDatas[groupedDatas.length - 1].NextSiblingID = ungroupedProj.ID;
-        }*/
         //将未分类的数据归类
         sortSameDepthData(engs, ungroupedProj.ID);
         sortSameDepthData(tenders, ungroupedEng.ID);

+ 53 - 0
web/over_write/config/compilation_config.js

@@ -0,0 +1,53 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/8/14
+ * @version
+ */
+
+/*
+* 不同运行环境下(CommonJS、浏览器)的费用定额功能模块配置
+* 如果运行环境使用了CommonJS,通过module.exports工厂函数返回内容
+* 否则将工厂函数返回内容挂载在浏览器宿主对象
+* @param  {Object}root 浏览器宿主对象
+ *        {String}overWriteUrl 费用定额重写路径(eg:/web/over_write/js/chongqing_2018.js)
+ *        {Function}factory 工厂函数
+* @return {Object}
+* */
+(function (root, overWriteUrl, factory) {
+    if (typeof module !== 'undefined') {
+        module.exports = factory(overWriteUrl);
+    } else {
+        root._compilationConfig = factory(overWriteUrl);
+    }
+}(window, overWriteUrl, function (url) {
+    if (!url) {
+        return {};
+    }
+    const COMPILATION_MAP = {
+        chongqing_2018: 1,
+        neimenggu_2017: 2,
+        jiangxi_2017: 3,
+        guangdong_2018: 4,
+        gansu_2013: 5
+    };
+    // 费用定额定制需求配置
+    let config = {
+        // 定额工作内容列可下拉选择(0关闭、1开启)(广东)
+        dynamicRationWorkContent: 0
+    };
+    // 提取重写路径中的关键信息 eg: chongqing_2018
+    let compilation = url.split('/').pop().slice(0, -3),
+        compilationNum = COMPILATION_MAP[compilation];
+    if (!compilationNum) {
+        return {};
+    }
+    // 广东18
+    if (compilationNum === COMPILATION_MAP.guangdong_2018) {
+        config.dynamicRationWorkContent = 1;
+    }
+    return config;
+}));

+ 18 - 0
web/over_write/js/guangdong_2018.js

@@ -0,0 +1,18 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2019/8/15
+ * @version
+ */
+
+if (typeof module !== 'undefined') {
+
+} else {
+    // 广东接口排版规则,添加内容默认项目特征
+    $(function () {
+        $('#add-content').val('3');
+    });
+}

+ 11 - 1
web/over_write/js/neimenggu_2017.js

@@ -211,4 +211,14 @@ function overwriteRationCalcBases (taxType){
                 multiRef: [fixedFlag.SUB_ENGINERRING, fixedFlag.MEASURE, fixedFlag.OTHER, fixedFlag.CHARGE]},//相关固定行
         }
     }
-})();
+})();
+
+if(typeof module !== 'undefined'){
+    module.exports = {
+        getBillsCalcMode: getBillsCalcMode
+    };
+}
+
+function getBillsCalcMode() {
+  return 1
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2 - 2
web/users/html/login.html


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

@@ -42,8 +42,8 @@ $(document).ready(function () {
         captchaObj.appendTo('#captcha-box');
         captchaObj.onSuccess(function () {
             $(".btn-area").slideDown("fast");
-            $('#login').click();
-            captchaObj.getValidate();
+            // $('#login').click();
+            // captchaObj.getValidate();
         });
 
         $("#login").click(function () {
@@ -479,7 +479,7 @@ function setVersion(versionData) {
     let html = '';
     for (let version of versionData) {
         let description = version.description ? version.description : '介绍内容';
-        let tmpHtml = '<div class="col-sm-6">' +
+        let tmpHtml = '<div class="col-sm-6 mb-3">' +
             '<div class="card card-block">' +
             '<div class="card-body">' +
             '<h3 class="card-title">'+ version.name +'</h3>' +