ソースを参照

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

TonyKang 5 年 前
コミット
c15cfa1c61
33 ファイル変更1539 行追加1182 行削除
  1. 2 0
      modules/all_models/import_logs.js
  2. 18 0
      modules/glj/controllers/glj_controller.js
  3. 1 0
      modules/glj/routes/glj_router.js
  4. 7 3
      modules/main/controllers/bills_controller.js
  5. 191 10
      modules/pm/facade/pm_facade.js
  6. 3 2
      modules/pm/models/project_model.js
  7. 15 10
      modules/ration_glj/facade/glj_calculate_facade.js
  8. 40 0
      public/common_util.js
  9. 0 0
      public/static/分部分项工程项目清单计价表.xlsx
  10. BIN
      public/static/算量示例-清单汇总表.xlsx
  11. 4 0
      public/web/gljUtil.js
  12. 5 3
      public/web/sheet/sheet_common.js
  13. 191 183
      web/building_saas/complementary_ration_lib/js/ration_glj.js
  14. 1 22
      web/building_saas/css/style.css
  15. 198 137
      web/building_saas/main/js/models/calc_program.js
  16. 64 17
      web/building_saas/main/js/models/exportStdInterfaceBase.js
  17. 5 3
      web/building_saas/main/js/views/calc_program_view.js
  18. 12 0
      web/building_saas/main/js/views/export_view.js
  19. 10 2
      web/building_saas/main/js/views/glj_col.js
  20. 107 8
      web/building_saas/main/js/views/glj_view.js
  21. 59 1
      web/building_saas/main/js/views/glj_view_contextMenu.js
  22. 42 33
      web/building_saas/main/js/views/project_glj_view.js
  23. 2 0
      web/building_saas/main/js/views/project_info.js
  24. 55 35
      web/building_saas/main/js/views/project_property_basicInfo.js
  25. 28 481
      web/building_saas/main/js/views/project_property_projFeature.js
  26. 1 1
      web/building_saas/main/js/views/project_view.js
  27. 14 2
      web/building_saas/main/js/views/sub_view.js
  28. 1 8
      web/building_saas/main/js/views/tender_price_view.js
  29. 1 2
      web/building_saas/pm/js/pm_import.js
  30. 76 16
      web/building_saas/pm/js/pm_newMain.js
  31. 43 17
      web/over_write/js/chongqing_2018_export.js
  32. 316 169
      web/over_write/js/guangdong_2018_export.js
  33. 27 17
      web/over_write/js/guangdong_2018_import.js

+ 2 - 0
modules/all_models/import_logs.js

@@ -13,6 +13,8 @@ let modelSchema = {
     userID: String,
     // 关联用户id
     status:String,
+    // 建设项目ID
+    projectID: Number,
     // 创建时间
     create_time: Number,
     errorMsg:""

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

@@ -63,6 +63,24 @@ class GLJController extends BaseController {
             callback(result.err,consts.projectConst.PROJECTGLJ,result.data);
         })
     }
+    async updateRatio(request, response) {
+      let data = JSON.parse(request.body.data);
+      let model = new MixRatioModel();
+      if(data.type == 'delete'){
+        await model.deleteById(data.id);
+      }else{
+        await model.updateById(data.id, data.doc);
+      }
+      let unitPriceUpdate = {
+        base_price: data.base_price,
+          market_price: data.market_price
+      };
+      let unitPriceModel = new UnitPriceModel();
+      let unitPriceResult = await unitPriceModel.updatePrice({id:data.pid}, unitPriceUpdate);
+      response.json(unitPriceResult);
+    }
+
+
 
     /**
      * 更新数据

+ 1 - 0
modules/glj/routes/glj_router.js

@@ -15,6 +15,7 @@ let gljController = new GLJController();
 // action定义区域
 router.post('/getData', gljController.init, gljController.getGljList);
 router.post('/update', gljController.init, gljController.updateData);
+router.post('/updateRatio', gljController.init, gljController.updateRatio);
 router.post('/get-ratio', gljController.init, gljController.getRatio);
 router.post('/delete-ratio', gljController.init, gljController.deleteMixRatio);
 router.post('/add-ratio', gljController.init, gljController.addMixRatio);

+ 7 - 3
modules/main/controllers/bills_controller.js

@@ -193,14 +193,18 @@ module.exports = {
         res.json(result);
     },
     //下载导入清单示例
-    downloadExample: async function(request, response) {
+    downloadExample: function(request, response) {
         try {
-            const filePath = './public/static/uploadExample.xlsx';
+            //导入类型(09表、广联达)对应的示例文件名
+            const uploadTypeMap = { lj: '分部分项工程项目清单计价表.xlsx', gld: '算量示例-清单汇总表.xlsx' };
+            const type = request.query.type;
+            const fileName = uploadTypeMap[type];
+            const filePath = `./public/static/${fileName}`;
             const stats = fs.statSync(filePath);
             // 下载相关header
             response.set({
                 'Content-Type': 'application/octet-stream',
-                'Content-Disposition': 'attachment; filename=uploadExample.xlsx',
+                'Content-Disposition': `attachment; filename=${encodeURI(fileName)}`,
                 'Content-Length': stats.size
             });
             fs.createReadStream(filePath).pipe(response);

+ 191 - 10
modules/pm/facade/pm_facade.js

@@ -19,6 +19,7 @@ module.exports={
     moveProject:moveProject,
     copyProject:copyProject,
     copyExample: copyExample,
+    setupSummaryFields: setupSummaryFields,
     getSummaryInfo: getSummaryInfo,
     getSummaryInfoByTender: getSummaryInfoByTender,
     getIndexReportData: getIndexReportData,
@@ -1025,7 +1026,169 @@ async function getTendersFeeInfo(tenders) {
     return IDMapping;
 }
 
-async function getSummaryInfo(projectIDs, feeFields = null, engineeringCostFields = null){
+const defaultSummaryField = {
+    [billsFlags.ENGINEERINGCOST]: {
+        items: [
+            { name: 'engineeringCost', feeName: 'common' },
+            { name: 'estimate', feeName: 'estimate' }
+        ]
+    },
+    [billsFlags.SUB_ENGINERRING]: {
+        items: [{ name: 'subEngineering', feeName: 'common' }]
+    },
+    [billsFlags.MEASURE]: {
+        items: [{ name: 'measure', feeName: 'common' }]
+    },
+    [billsFlags.SAFETY_CONSTRUCTION]: {
+        items: [{ name: 'safetyConstruction', feeName: 'common' }]
+    },
+    [billsFlags.OTHER]: {
+        items: [{ name: 'other', feeName: 'common' }]
+    },
+    [billsFlags.CHARGE]: {
+        items: [{ name: 'charge', feeName: 'common' }]
+    },
+    [billsFlags.TAX]: {
+        items: [{ name: 'tax', feeName: 'common' }]
+    }
+};
+
+// 项目管理界面需要显示的汇总字段
+function setupSummaryFields(summaryInfo, projects, fields = null) {
+    if (!fields) {
+        fields = [
+            'engineeringCost',
+            'subEngineering',
+            'measure',
+            'safetyConstruction',
+            'other',
+            'charge',
+            'tax',
+            'rate',
+            'buildingArea',
+            'perCost'
+        ];
+    }
+    for(const proj of projects){
+        const summaryProj = summaryInfo[proj.ID];
+        if(summaryProj){
+            for (const field of fields) {
+                proj[field] = summaryProj[field];
+            }
+        }
+    }
+}
+
+async function getSummaryInfo(projectIDs, summaryField = defaultSummaryField){
+    function initFees(target, summaryField) {
+        for (const flag in summaryField) {
+            const summaryItem = summaryField[flag];
+            summaryItem.items.forEach(item => {
+                target[item.name] = 0;
+            });
+        }
+    }
+    //ID与汇总信息映射
+    let IDMapping = {};
+    let projects = await projectModel.find({ID: {$in : projectIDs}, projType: projectType.project, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]});
+    //设置建设项目的总建筑面积
+    for(let project of projects){
+        let grossArea = '';
+        if(project.property && project.property.basicInformation){
+            for(let basicInfo of project.property.basicInformation){
+                if(basicInfo.key === 'basicInfo'){
+                    for(let k of basicInfo.items){
+                        if(k.key === 'grossArea'){
+                            grossArea = k.value;
+                        }
+                    }
+                }
+            }
+        }
+        IDMapping[project.ID] = {rate: 0, buildingArea: grossArea, perCost: ''};
+        initFees(IDMapping[project.ID], summaryField);
+    }
+
+    //单项工程
+    let engineerings = await projectModel.find({ParentID: {$in : projectIDs}, projType: projectType.engineering, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]});
+    let tenders = [];
+    let engIDs = [];
+    
+    for(let eng of engineerings){
+        engIDs.push(eng.ID);
+        IDMapping[eng.ID] = {rate: 0, buildingArea: '', perCost: ''};
+        initFees(IDMapping[eng.ID], summaryField);
+    }
+    //单位工程
+    if(engIDs.length > 0){
+        tenders = await projectModel.find({ParentID: {$in : engIDs}, projType: projectType.tender, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]});
+    }
+    let tenderIDs = [];
+    if(tenders.length > 0){
+        for(let tender of tenders){
+            tenderIDs.push(tender.ID);
+            IDMapping[tender.ID] = {rate: 0, buildingArea: '', perCost: '', changeMark:tender.changeMark,property:tender.property};
+            initFees(IDMapping[tender.ID], summaryField);
+            let buildingArea = getBuildingArea(tender.property.projectFeature);
+            if(buildingArea){
+                IDMapping[tender.ID]['buildingArea'] = buildingArea;
+            }
+        }
+        //需要获取的清单固定类别综合合价:工程造价、分部分项、措施项目、安全文明施工专项、规费、其他项目、税金...
+        let needFlags = Object.getOwnPropertyNames(summaryField);
+        //获取单位工程汇总金额需要用到的所有清单
+        let allBills = await billsModel.find({projectID: {$in: tenderIDs}, 'flags.flag': {$in: needFlags}, $or: [{deleteInfo: null}, {'deleteInfo.deleted': false}]},
+                                            '-_id projectID fees flags');
+        //进行单位工程级别的汇总
+        for(let bills of allBills){
+            let billsFlag = bills.flags[0]['flag'];
+            // 设置上单位工程需要汇总的字段金额
+            const summaryItem = summaryField[billsFlag];
+            summaryItem.items.forEach(costItem => {
+                IDMapping[bills.projectID][costItem.name] = getTotalFee(bills, costItem.feeName);
+            });
+        }
+        const summaryNames = [];
+        for (const flag in summaryField) {
+            summaryField[flag].items.forEach(item => summaryNames.push(item.name));
+        }
+        //进行单项工程级别的汇总
+        for(let tender of tenders){
+            summarizeToParent(IDMapping[tender.ParentID], IDMapping[tender.ID], summaryNames);
+        }
+        //进行建设项目级别的汇总
+        for(let eng of engineerings){
+            summarizeToParent(IDMapping[eng.ParentID], IDMapping[eng.ID], summaryNames);
+        }
+        //占造价比例、单方造价
+        const rateDecimal = -2;
+        const perCostDecimal = -2;
+        for(let tender of tenders){
+            let tenderInfo = IDMapping[tender.ID];
+            let engInfo = IDMapping[tender.ParentID];
+            tenderInfo.rate = engInfo.engineeringCost == 0 ? 0 : scMathUtil.roundTo(tenderInfo.engineeringCost * 100 / engInfo.engineeringCost, rateDecimal);
+            //单方造价
+            tenderInfo.perCost = tenderInfo.buildingArea.toString().trim() === '' || tenderInfo.buildingArea == 0 
+                ? tenderInfo.buildingArea.toString().trim() 
+                : scMathUtil.roundTo(tenderInfo.engineeringCost / tenderInfo.buildingArea, perCostDecimal);
+        }
+        for(let eng of engineerings){
+            let engInfo = IDMapping[eng.ID];
+            let projInfo = IDMapping[eng.ParentID];
+            engInfo.rate = !isDef(projInfo) || projInfo.engineeringCost == 0 ? 0 : scMathUtil.roundTo(engInfo.engineeringCost * 100 / projInfo.engineeringCost, rateDecimal);
+        }
+        //建设项目占造价比例及单方造价
+        for(let project of projects){
+            let projectInfo = IDMapping[project.ID];
+            projectInfo.rate = 100;
+            projectInfo.perCost = projectInfo.buildingArea.toString().trim() === '' || projectInfo.buildingArea == 0 
+            ? projectInfo.buildingArea.toString().trim() 
+            : scMathUtil.roundTo(projectInfo.engineeringCost / projectInfo.buildingArea, perCostDecimal);
+        }
+    }
+    return IDMapping;
+}
+/* async function getSummaryInfo(projectIDs, feeFields = null, engineeringCostFields = null){
     function initFees(obj, feeFields, engineeringCostFields) {
         for (let data of feeFields) {
             obj[data.v] = 0;
@@ -1154,7 +1317,7 @@ async function getSummaryInfo(projectIDs, feeFields = null, engineeringCostField
         }
     }
     return IDMapping;
-}
+} */
 
 //根据项目ID获取所属建设项目
 //@param {Number}projectID @return {Object}
@@ -1215,9 +1378,15 @@ async function getUpChainIDs(projectID) {
     return rst;
 }
 
-//获取projectIDs文件下所有子项目(不包括projectIDs本身)
-async function getPosterityProjects(projectIDs) {
+//获取projectIDs文件下所有子项目(默认不包括projectIDs本身)
+async function getPosterityProjects(projectIDs, includeSelf = false) {
     let rst = [];
+    if (includeSelf) {
+        const projects = await projectModel.find({ID: {$in: projectIDs}, $or: notDeleted}, '-_id').lean();
+        if (projects) {
+            rst.push(...projects);
+        }
+    }
     async function getProjects(parentIDs) {
         if (parentIDs.length > 0) {
             let newIDs = [];
@@ -1422,7 +1591,7 @@ async function getProjectFeature(valuationID, engineeringName, feeName) {
 
 //根据单位工程,获取建设项目-单项工程-单位工程,不包含无单位工程的单项工程
 //@param {Number}tenderID(单位工程ID) {Number}granularity(颗粒度 1:建设项目 2:单项工程 3:单位工程)
-async function getProjectByGranularity(tenderID, granularity, summaryObj, userID, versionName) {
+async function getProjectByGranularity(tenderID, granularity, summaryField, userID, versionName) {
     const GRANULARITY = {
         PROJECT: 1,
         ENGINEERING: 2,
@@ -1456,8 +1625,7 @@ async function getProjectByGranularity(tenderID, granularity, summaryObj, userID
     }
     constructionProject.children = engineerings;
     //获取汇总信息
-    const { feeFields, engineeringCostFields } = summaryObj;
-    constructionProject.summaryInfo = await getSummaryInfo([constructionProject.ID], feeFields, engineeringCostFields);
+    constructionProject.summaryInfo = await getSummaryInfo([constructionProject.ID], summaryField);
     //获取编制软件信息: 软件公司;软件名;版本号;授权信息; base64
     let product = await productModel.findOne({});
     let company = product.company || '珠海纵横创新软件有限公司',
@@ -1949,6 +2117,7 @@ async function doDownLoadAndImport(privateDownloadUrl,info) {
         try {
             let data = fs.readFileSync(stream.path,'utf-8');
             let result = await importProjects(data,{session:info.session},info.updateData);
+            doc.projectID = result.constructionProjectID;
             if(result.error == 1){
                 doc.errorMsg = result.msg;
                 doc.status = "error";
@@ -1971,6 +2140,13 @@ async function importProcessChecking(data){
         if(log.status == "finish"){
             result.status = "complete";
             await importLogsModel.remove({key:data.key});
+            // 获取导入的项目数据
+            if (log.projectID) {
+                const projects = await getPosterityProjects([log.projectID], true);
+                const summaryInfo = await getSummaryInfo([log.projectID]);
+                setupSummaryFields(summaryInfo, projects);
+                result.data = projects;
+            }
         }else if(log.status == "start"){
             result.status = "processing";
         }else if(log.status == "error"){
@@ -2065,7 +2241,8 @@ async function importProjects(data,req,updateData) {
                 result.error = 1;
                 result.msg = `您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。`;
             }
-            let [projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap] = await handleMainProjectDatas(mainData,updateData,req.session.sessionUser.id);
+            let [constructionProjectID,projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap] = await handleMainProjectDatas(mainData,updateData,req.session.sessionUser.id);
+            result.constructionProjectID = constructionProjectID;
             if(datas.length > 1 ){
                 for(let i = 1;i<datas.length;i++){
                     await handleEachProject(datas[i],projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap)
@@ -2193,11 +2370,15 @@ async function handleMainProjectDatas(mainData,updateData,userID) {
     let projectIDMap = {},feeRateFileIDMap={},unitPriceFileIDMap={},labourCoeFileIDMap={},calcProgramFileIDMap={};
     let tasks = [];
     projectIDMap[-1] = -1;//最后一个项目的nextID为-1的情况
+    let constructionProjectID;
     //生成新的projectID
     for(let p of mainData.projects){
         let newProjectID = await getCounterID("projects");
         projectIDMap[p.ID] = newProjectID;
-        if(p.projType == "Project") mainProjectID =  newProjectID;
+        if(p.projType == "Project") {
+            mainProjectID =  newProjectID;
+            constructionProjectID = newProjectID;
+        }
         if(p.projType == "Tender"){
             if(p.property.calcProgramFile) calcProgramFileIDMap[p.property.calcProgramFile.ID] = uuidV1();//新的计算程序文件ID
             if(p.property.labourCoeFile) labourCoeFileIDMap[p.property.labourCoeFile.ID] = uuidV1();//新的人工调整系数文件ID
@@ -2246,7 +2427,7 @@ async function handleMainProjectDatas(mainData,updateData,userID) {
     await importUnitPriceFiles(mainData,projectIDMap,unitPriceFileIDMap,userID);
 
 
-    return [projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap]
+    return [constructionProjectID,projectIDMap,labourCoeFileIDMap,calcProgramFileIDMap]
 }
 
 

+ 3 - 2
modules/pm/models/project_model.js

@@ -83,7 +83,8 @@ ProjectsDAO.prototype.getUserProjects = async function (userId, compilation, cal
         });
         // 设置汇总字段
         let summaryInfo = await pmFacade.getSummaryInfo(projIDs);
-        for(let proj of projects){
+        pmFacade.setupSummaryFields(summaryInfo, projects);
+        /* for(let proj of projects){
             let summaryProj = summaryInfo[proj.ID];
             if(summaryProj){
                 proj.engineeringCost = summaryProj.engineeringCost;
@@ -97,7 +98,7 @@ ProjectsDAO.prototype.getUserProjects = async function (userId, compilation, cal
                 proj.buildingArea = summaryProj.buildingArea;
                 proj.perCost = summaryProj.perCost;
             }
-        }
+        } */
         callback(0, '', projects);
     }
     catch (err) {

+ 15 - 10
modules/ration_glj/facade/glj_calculate_facade.js

@@ -68,6 +68,7 @@ async function calculateQuantity(query,noNeedCal,refreshRationName = false,areaI
             areaSetting = project.property.areaSetting;
         }
          if(impactRation._doc.hasOwnProperty("rationAssList")&&impactRation.rationAssList.length>0){
+             prepareAss(impactRation.rationAssList);
              let temTimes = [];
              let thirdRationCodes=[];
              for(let i=0;i<impactRation.rationAssList.length;i++){
@@ -376,19 +377,23 @@ function getContent(coes) {
 }
 
 function prepareAss(assList) {//处理辅助定额,支持多个辅助定额的情况
-    for(let a of assList){
-        if(a.groupList && a.groupList.length > 1){//组里有多个定额的情况
-            let newList = _.sortByAll(a.groupList,['param']);//先按参数排序
-            for(let n of newList){
-                if(a.actualValue > n.stdValue && a.actualValue <= parseFloat(n.param)){//落在中间,则用组里的这条定额
-                    a._doc.param = n.param;
-                    a._doc.paramName = n.paramName;
-                    a._doc.assistCode = n.assistCode;
-                    break;
-                }
+  for(let a of assList){
+    if(a.groupList && a.groupList.length > 1){//组里有多个定额的情况
+        let newList = _.sortByAll(a.groupList,[function(item){
+           return parseFloat(item.param)
+        }]);//先按参数排序
+        let pre = 0;
+        for(let n of newList){
+            if(a.actualValue > pre && a.actualValue <= parseFloat(n.param)){//落在中间,则用组里的这条定额
+                a._doc.param = n.param;
+                a._doc.paramName = n.paramName;
+                a._doc.assistCode = n.assistCode;
+                break;
             }
+            pre = parseFloat(n.param);
         }
     }
+  }
 }
 
 function calculateTimes(ass){

+ 40 - 0
public/common_util.js

@@ -46,11 +46,51 @@ function deleteEmptyObject(arr) {
         }
         return a == b;
     }
+    // 递归获取必填项(基本信息、工程特征)
+    function getRequired(rst, datas) {
+        if (!datas) {
+            return rst;
+        }
+        for (const data of datas) {
+            const required = typeof data.required === 'string' ? JSON.parse(data.required) : data.required;
+            const readOnly = typeof data.readOnly === 'string' ? JSON.parse(data.readOnly) : data.readOnly;
+            if (required && !readOnly) {
+                rst.push(data);
+            }
+            if (data.items && data.items.length) {
+                getRequired(rst, data.items);
+            }
+        }
+        return rst;
+    }
+    // 将树数据排序好
+    function getSortedTreeData(rootID, items) {
+        return sortSameDedth(rootID, items).reverse();
+        
+        function sortSameDedth(parentID, items) {
+            const sameDepthItems = items.filter(item => item.ParentID === parentID);
+            if (!sameDepthItems.length) {
+                return [];
+            }
+            const NextIDMapping = {};
+            sameDepthItems.forEach(item => NextIDMapping[item.NextSiblingID] = item);
+            let curItem = sameDepthItems.length > 1 ? sameDepthItems.find(item => item.NextSiblingID === -1) : sameDepthItems[0];
+            const sorted = [];
+            while (curItem) {
+                sorted.push(...sortSameDedth(curItem.ID, items));
+                sorted.push(curItem);
+                curItem = NextIDMapping[curItem.ID] || null;
+            }
+            return sorted;
+        }
+    }
 
     return {
         isDef,
         isEmptyVal,
         isNumber,
         similarEqual,
+        getRequired,
+        getSortedTreeData,
     };
 });

public/static/uploadExample.xlsx → public/static/分部分项工程项目清单计价表.xlsx


BIN
public/static/算量示例-清单汇总表.xlsx


+ 4 - 0
public/web/gljUtil.js

@@ -127,12 +127,14 @@ let gljUtil = {
         let coe = 1;
         coe = ration.quantityCoe&&this.isNotEmpty(ration.quantityCoe[coeField])?ration.quantityCoe[coeField]:1;
         coe = parseFloat(coe);
+        if (coe == 0) coe = 1;
         let glj_quantity = scMathUtil.roundForObj(ration_glj.quantity, q_decimal);
         return scMathUtil.roundForObj(glj_quantity * coe,q_decimal);
     },
     getRationTenderQuantity:function (ration,q_decimal,scMathUtil) {
         let rationQuantityCoe = this.isNotEmpty(ration.rationQuantityCoe)?ration.rationQuantityCoe:1;
         rationQuantityCoe = parseFloat(rationQuantityCoe);
+        if (rationQuantityCoe == 0) rationQuantityCoe = 1;
         let r_quantity = ration?scMathUtil.roundForObj(ration.quantity,q_decimal):0;
         return scMathUtil.roundForObj(r_quantity * rationQuantityCoe,q_decimal);
     },
@@ -203,6 +205,7 @@ let gljUtil = {
         let quantity_decimal = decimalObj.glj.quantity;
         let process_decimal = decimalObj.process;
         let priceCoe = this.isDef(tenderCoe)?tenderCoe:1;
+        if (priceCoe == '0' || priceCoe == 0) priceCoe = 1;   // 这里加个保护
         if (this.notEditType.indexOf(glj.unit_price.type)!=-1&&glj.ratio_data.length>0) {//对于混凝土、配合比、砂浆、机械台班等有组成物的材料,价格需根据组成物计算得出。
             let p =0;
             for(let ratio of glj.ratio_data){
@@ -578,6 +581,7 @@ let gljUtil = {
         let property = tproperty?tproperty:projectObj.project.property;
         if (!glj.is_adjust_price&&property.tenderSetting && isDef(property.tenderSetting.gljPriceTenderCoe) ){
             tenderCoe = parseFloat(property.tenderSetting.gljPriceTenderCoe);
+            if (tenderCoe == 0) tenderCoe = 1;
         }
         return tenderCoe;
 

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

@@ -1016,7 +1016,8 @@ var sheetCommonObj = {
         }
         return new getTipsCombo();
     },
-    getTreeNodeCellType:function (datas,row,parentMap) {// 2018-09-26  不用spreadjs默认的树结构,自定义控件
+    // paintFunc,需要追加到paint方法中的自定义paint方法
+    getTreeNodeCellType:function (datas,row,parentMap,paintFunc) {// 2018-09-26  不用spreadjs默认的树结构,自定义控件
         var ns = GC.Spread.Sheets;
         let rectW = 10;
         let rectH = 10;
@@ -1029,6 +1030,9 @@ var sheetCommonObj = {
         }
         TreeNodeCellType.prototype = new ns.CellTypes.Text();
         TreeNodeCellType.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
+            if (paintFunc) {
+                paintFunc(ctx, value, x, y, w, h, style, { rectW, rectH, margin }, datas[row]);
+            }
             let offset = 0;
             let step = 7;
             let level = getTreeLevel(datas[row],datas);//从0开始,取当前节点是第几级的
@@ -1494,11 +1498,9 @@ var sheetCommonObj = {
         }
     },
     setRowsAutoFit(sheet, rows, col, wordWrap) {
-        console.time('rowFit');
         rows.forEach(row => {
             sheet.getCell(row, col).wordWrap(wordWrap);
             sheet.autoFitRow(row);
         });
-        console.timeEnd('rowFit');
     }
 }

+ 191 - 183
web/building_saas/complementary_ration_lib/js/ration_glj.js

@@ -10,24 +10,24 @@ var rationGLJOprObj = {
     tempCacheArr: [],//被更新的工料机,若更新的工料机不存在,则恢复
     cache: {},
     setting: {
-        header:[
-            {headerName:"编码",headerWidth:120,dataCode:"code", dataType: "String", formatter: "@"},
-            {headerName:"名称",headerWidth:400,dataCode:"name", dataType: "String"},
-            {headerName:"规格型号",headerWidth:120,dataCode:"specs", dataType: "String"},
-            {headerName:"单位",headerWidth:160,dataCode:"unit", dataType: "String", hAlign: "center", vAlign: "center"},
-            {headerName:"定额价",headerWidth:160, dataCode:"basePrice", dataType: "Number", formatter:"0.00",  precision: 2},
-            {headerName:"定额消耗",headerWidth:160, dataCode:"consumeAmt", dataType: "Number", formatter: "0.000", precision: 3},
-            {headerName:"类型",headerWidth:160,dataCode:"gljType", dataType: "String", hAlign: "center", vAlign: "center"}
+        header: [
+            { headerName: "编码", headerWidth: 120, dataCode: "code", dataType: "String", formatter: "@" },
+            { headerName: "名称", headerWidth: 400, dataCode: "name", dataType: "String" },
+            { headerName: "规格型号", headerWidth: 120, dataCode: "specs", dataType: "String" },
+            { headerName: "单位", headerWidth: 160, dataCode: "unit", dataType: "String", hAlign: "center", vAlign: "center" },
+            { headerName: "定额价", headerWidth: 160, dataCode: "basePrice", dataType: "Number", formatter: "0.00", precision: 2 },
+            { headerName: "定额消耗", headerWidth: 160, dataCode: "consumeAmt", dataType: "Number", formatter: "0.000", precision: 3 },
+            { headerName: "类型", headerWidth: 160, dataCode: "gljType", dataType: "String", hAlign: "center", vAlign: "center" }
         ],
-        view:{
-            comboBox:[],
-            lockColumns:[1,2,3,4,6]
+        view: {
+            comboBox: [],
+            lockColumns: [1, 2, 3, 4, 6]
         }
     },
     getDistTypeTree: function (gljDistType) {
         let distType;
         let distTypeTree = {
-            prefix : 'gljDistType',
+            prefix: 'gljDistType',
             distTypes: {},
             comboDatas: [],
             distTypesArr: []
@@ -44,18 +44,18 @@ var rationGLJOprObj = {
         gljDistType.forEach(function (typeData) {
             distType = distTypeTree.distTypes[distTypeTree.prefix + typeData.ID];
             let parent = distTypeTree.distTypes[distTypeTree.prefix + typeData.ParentID];
-            if(parent){
+            if (parent) {
                 distType.parent = parent;
                 parent.children.push(distType);
             }
         });
         distTypeTree.distTypesArr.forEach(function (distTypeObj) {
-            if(distTypeObj.children.length === 0 && distTypeObj.data.fullName !== '普通机械' &&distTypeObj.data.fullName !== '机械组成物'
-                && distTypeObj.data.fullName !== '机上人工'){
-                distTypeTree.comboDatas.push({text: distTypeObj.data.fullName, value: distTypeObj.data.ID});
+            if (distTypeObj.children.length === 0 && distTypeObj.data.fullName !== '普通机械' && distTypeObj.data.fullName !== '机械组成物'
+                && distTypeObj.data.fullName !== '机上人工') {
+                distTypeTree.comboDatas.push({ text: distTypeObj.data.fullName, value: distTypeObj.data.ID });
             }
-            if(distTypeObj.data.fullName === '机械'){
-                distTypeTree.comboDatas.push({text: distTypeObj.data.fullName, value: distTypeObj.data.ID});
+            if (distTypeObj.data.fullName === '机械') {
+                distTypeTree.comboDatas.push({ text: distTypeObj.data.fullName, value: distTypeObj.data.ID });
             }
         });
         return distTypeTree;
@@ -67,14 +67,14 @@ var rationGLJOprObj = {
             url: "api/getGljDistType",
             dataType: 'json',
             success: function (result) {
-                if(!result.error && callback){
+                if (!result.error && callback) {
                     me.distTypeTree = me.getDistTypeTree(result.data);
                     callback();
                 }
             }
         })
     },
-    buildSheet: function(sheet) {
+    buildSheet: function (sheet) {
         this.sheet = sheet;
         this.distTypeTree = this.getDistTypeTree(this.distTypeTree);
         sheetCommonObj.initSheet(this.sheet, this.setting, 30);
@@ -90,117 +90,127 @@ var rationGLJOprObj = {
         let raCoe = rationCoeOprObj;
         $.contextMenu({
             selector: '#rdSpread',
-            build: function($triggerElement, e){
+            build: function ($triggerElement, e) {
                 //控制允许右键菜单在哪个位置出现
                 let target = SheetDataHelper.safeRightClickSelection($triggerElement, e, me.sheet.getParent());
                 let sheet = me.sheet;
                 let addDis = false, delDis = false;
                 let rationGlj = [];
-                if(me.sheet.getParent().getActiveSheetIndex() === 0 && target.hitTestType === 3){//在表格内&& typeof target.row !== 'undefined' && typeof target.col !== 'undefined'
+                if (me.sheet.getParent().getActiveSheetIndex() === 0 && target.hitTestType === 3) {//在表格内&& typeof target.row !== 'undefined' && typeof target.col !== 'undefined'
                     //rationGlj表
-                    if(typeof target.row !== 'undefined'){
+                    if (typeof target.row !== 'undefined') {
                         //控制按钮是否可用
                         sheet.setActiveCell(target.row, target.col);
                         console.log(me.currentRationItem);
-                        if(me.currentRationItem){
-                            rationGlj =  me.cache['_GLJ_' + me.currentRationItem.ID];
-                            if(!rationGlj ||target.row >= rationGlj.length){//右键定位在有数据的行,删除键才显示可用
+                        if (me.currentRationItem) {
+                            rationGlj = me.cache['_GLJ_' + me.currentRationItem.ID];
+                            if (!rationGlj || target.row >= rationGlj.length) {//右键定位在有数据的行,删除键才显示可用
                                 delDis = true;
                             }
-                            else{//有数据
-                                if(typeof target.col === 'undefined'){//定位不在表格内
+                            else {//有数据
+                                if (typeof target.col === 'undefined') {//定位不在表格内
                                     delDis = true;
                                 }
                             }
                         }
-                        else{
+                        else {
                             addDis = true;
                             delDis = true;
                         }
                     }
-                    else{
+                    else {
                         addDis = true;
                         delDis = true;
                     }
                     return {
-                        callback: function(){},
+                        callback: function () { },
                         items: {
-                            "add": {name: "添加人材机", disabled: addDis, icon: "fa-plus", callback: function (key, opt) {
-                                //默认radio所有工料机
-                                gljSelOprObj.initRadio();
-                                gljSelOprObj.gljCurTypeId = null;
-                                gljSelOprObj.initClassTree('std', gljSelOprObj.treeData.std);
-                                //弹出窗口
-                                $('#selGlj').modal('show');
-                            }},
-                            "delete": {name: "删除人材机", disabled: delDis, icon: "fa-remove", callback: function (key, opt) {
-                                rationGlj.splice(target.row, 1);
-                                me.updateRationItem(function(){
-                                    me.sheet.getParent().focus();
-                                });
-                                sheetCommonObj.cleanData(me.sheet, me.setting, -1);
-                                me.showGljItems(me.currentRationItem.ID);
-                            }},
+                            "add": {
+                                name: "添加人材机", disabled: addDis, icon: "fa-plus", callback: function (key, opt) {
+                                    //默认radio所有工料机
+                                    gljSelOprObj.initRadio();
+                                    gljSelOprObj.gljCurTypeId = null;
+                                    gljSelOprObj.initClassTree('std', gljSelOprObj.treeData.std);
+                                    //弹出窗口
+                                    $('#selGlj').modal('show');
+                                }
+                            },
+                            "delete": {
+                                name: "删除人材机", disabled: delDis, icon: "fa-remove", callback: function (key, opt) {
+                                    rationGlj.splice(target.row, 1);
+                                    me.updateRationItem(function () {
+                                        me.sheet.getParent().focus();
+                                    });
+                                    sheetCommonObj.cleanData(me.sheet, me.setting, -1);
+                                    me.showGljItems(me.currentRationItem.ID);
+                                }
+                            },
                         }
                     };
                 }
                 //rationCoe表
-                else if(me.sheet.getParent().getActiveSheetIndex() === 2 && target.hitTestType === 3 && typeof target.row !== 'undefined' && typeof target.col !== 'undefined'){
-                    let currentCache = raCoe.curRation && raCoe.isDef(raCoe.cache["_Coe_" + raCoe.curRation.ID])  ? raCoe.cache["_Coe_" + raCoe.curRation.ID] : [];
+                else if (me.sheet.getParent().getActiveSheetIndex() === 2 && target.hitTestType === 3 && typeof target.row !== 'undefined' && typeof target.col !== 'undefined') {
+                    let currentCache = raCoe.curRation && raCoe.isDef(raCoe.cache["_Coe_" + raCoe.curRation.ID]) ? raCoe.cache["_Coe_" + raCoe.curRation.ID] : [];
                     sheet.setActiveCell(target.row, target.col);
                     //控制按钮是否可用
                     let upDis = false,
                         downDis = false,
                         refDis = false;
-                    if(target.row >= currentCache.length){
+                    if (target.row >= currentCache.length) {
                         upDis = true;
                         downDis = true;
                         refDis = true;
                     }
                     else {
-                        if(!raCoe.isDef(currentCache[target.row - 1])){
+                        if (!raCoe.isDef(currentCache[target.row - 1])) {
                             upDis = true;
                         }
-                        if(!raCoe.isDef(currentCache[target.row + 1])){
+                        if (!raCoe.isDef(currentCache[target.row + 1])) {
                             downDis = true;
                         }
                     }
                     return {
-                        callback: function(){},
+                        callback: function () { },
                         items: {
-                            "upMove": {name: "上移", disabled: upDis, icon: "fa-arrow-up", callback: function (key, opt) {
-                                raCoe.upMove(currentCache[target.row], currentCache[target.row - 1], {row: target.row - 1, col: target.col});
-                            }},
-                            "downMove": {name: "下移", disabled: downDis, icon: "fa-arrow-down", callback: function (key, opt) {
-                                raCoe.downMove(currentCache[target.row], currentCache[target.row + 1], {row: target.row + 1, col: target.col});
-                            }},
-                            "ref": {name: "添加到本节其他定额", disabled: refDis, icon: "fa-arrow-left", callback: function (key, opt) {
-                                raCoe.updateSectionRation(rationOprObj.currentRations["_SEC_ID_" + rationOprObj.currentSectionId], currentCache[target.row], function (updateArr) {
-                                    for(let i = 0, len = updateArr.length; i < len; i++){
-                                        let ration = updateArr[i];
-                                        let rationCoeList = updateArr[i].rationCoeList;
-                                        let newNo = 1;
-                                        for(let j = 0, jLen = rationCoeList.length; j < jLen; j++){
-                                            if(rationCoeList[j].no >= newNo){
-                                                newNo = rationCoeList[j].no + 1;
+                            "upMove": {
+                                name: "上移", disabled: upDis, icon: "fa-arrow-up", callback: function (key, opt) {
+                                    raCoe.upMove(currentCache[target.row], currentCache[target.row - 1], { row: target.row - 1, col: target.col });
+                                }
+                            },
+                            "downMove": {
+                                name: "下移", disabled: downDis, icon: "fa-arrow-down", callback: function (key, opt) {
+                                    raCoe.downMove(currentCache[target.row], currentCache[target.row + 1], { row: target.row + 1, col: target.col });
+                                }
+                            },
+                            "ref": {
+                                name: "添加到本节其他定额", disabled: refDis, icon: "fa-arrow-left", callback: function (key, opt) {
+                                    raCoe.updateSectionRation(rationOprObj.currentRations["_SEC_ID_" + rationOprObj.currentSectionId], currentCache[target.row], function (updateArr) {
+                                        for (let i = 0, len = updateArr.length; i < len; i++) {
+                                            let ration = updateArr[i];
+                                            let rationCoeList = updateArr[i].rationCoeList;
+                                            let newNo = 1;
+                                            for (let j = 0, jLen = rationCoeList.length; j < jLen; j++) {
+                                                if (rationCoeList[j].no >= newNo) {
+                                                    newNo = rationCoeList[j].no + 1;
+                                                }
                                             }
-                                        }
-                                        let theCache = raCoe.cache["_Coe_" + ration.ID];
-                                        if(theCache !== undefined && theCache !== null){
-                                            let newCoe = {};
-                                            for(let attr in currentCache[target.row]){
-                                                newCoe[attr] = currentCache[target.row][attr];
+                                            let theCache = raCoe.cache["_Coe_" + ration.ID];
+                                            if (theCache !== undefined && theCache !== null) {
+                                                let newCoe = {};
+                                                for (let attr in currentCache[target.row]) {
+                                                    newCoe[attr] = currentCache[target.row][attr];
+                                                }
+                                                newCoe.no = newNo;
+                                                theCache.push(newCoe);
                                             }
-                                            newCoe.no = newNo;
-                                            theCache.push(newCoe);
                                         }
-                                    }
-                                });
-                            }}
+                                    });
+                                }
+                            }
                         }
                     };
                 }
-                else{
+                else {
                     return false;
                 }
             }
@@ -209,26 +219,26 @@ var rationGLJOprObj = {
     bindRationGljDelOpr: function () {
         let me = rationGLJOprObj, spreadBook = me.sheet.getParent();
         spreadBook.commandManager().register('rationGljDelete', function () {
-            if(!me.currentRationItem){
+            if (!me.currentRationItem) {
                 return;
             }
             let sels = me.sheet.getSelections(), lockCols = me.setting.view.lockColumns;
             let cacheSection = me.cache["_GLJ_" + me.currentRationItem.ID], isUpdate = false;
-            if(sels.length > 0){
-                for(let sel = 0; sel < sels.length; sel++){
-                    if(sels[sel].colCount === me.setting.header.length){
-                        if(cacheSection && sels[sel].row < cacheSection.length){
+            if (sels.length > 0) {
+                for (let sel = 0; sel < sels.length; sel++) {
+                    if (sels[sel].colCount === me.setting.header.length) {
+                        if (cacheSection && sels[sel].row < cacheSection.length) {
                             isUpdate = true;
                             cacheSection.splice(sels[sel].row, sels[sel].rowCount);
                         }
                     }
-                    else{
-                         if(sels[sel].col !== 0 && sels[sel].col !== 5 && !(sels[sel].col === 1 && sels.col + sels[sel].colCount -1 === 3)){
-                            if(cacheSection){
-                                for(let i = sels[sel].row === -1 ? 1 : 0; i < sels[sel].rowCount; i++){
-                                    if(sels[sel].row + i < cacheSection.length){
-                                        for(let col = sels[sel].col; col <= sels[sel].col + sels[sel].colCount - 1; col++){
-                                            if(lockCols.indexOf(col) === -1){
+                    else {
+                        if (sels[sel].col !== 0 && sels[sel].col !== 5 && !(sels[sel].col === 1 && sels.col + sels[sel].colCount - 1 === 3)) {
+                            if (cacheSection) {
+                                for (let i = sels[sel].row === -1 ? 1 : 0; i < sels[sel].rowCount; i++) {
+                                    if (sels[sel].row + i < cacheSection.length) {
+                                        for (let col = sels[sel].col; col <= sels[sel].col + sels[sel].colCount - 1; col++) {
+                                            if (lockCols.indexOf(col) === -1) {
                                                 isUpdate = true;
                                                 cacheSection[sels[sel].row + i][me.setting.header[col].dataCode] = 0;
                                                 me.sheet.setValue(sels[sel].row + i, col, 0.00);
@@ -241,7 +251,7 @@ var rationGLJOprObj = {
                     }
                 }
             }
-            if(isUpdate){
+            if (isUpdate) {
                 me.updateRationItem(function () {
                     me.sheet.getParent().focus(true);
                 });
@@ -252,67 +262,65 @@ var rationGLJOprObj = {
         spreadBook.commandManager().setShortcutKey(null, GC.Spread.Commands.Key.del, false, false, false, false);
         spreadBook.commandManager().setShortcutKey('rationGljDelete', GC.Spread.Commands.Key.del, false, false, false, false);
     },
-    onClipboardPasting: function(sender, args) {
+    onClipboardPasting: function (sender, args) {
         var me = rationGLJOprObj;
         let rationSection = rationOprObj.getCache();
         let rationRow = rationOprObj.workBook.getSheet(0).getSelections()[0].row;
         me.currentRationItem = rationRow < rationSection.length ? rationSection[rationRow] : null;
-        if(me.currentRationItem && typeof me.cache["_GLJ_" + me.currentRationItem.ID] === 'undefined'){
+        if (me.currentRationItem && typeof me.cache["_GLJ_" + me.currentRationItem.ID] === 'undefined') {
             me.cache["_GLJ_" + me.currentRationItem.ID] = [];
         }
         if (!(args.cellRange.col === 0 || args.cellRange.col === 5) || !(me.currentRationItem)) {
             args.cancel = true;
         }
     },
-    onClipboardPasted: function(e, info) {
+    onClipboardPasted: function (e, info) {
         var me = rationGLJOprObj, repId = pageOprObj.rationLibId;
         me.tempCacheArr = [];
-        if (repId) {
-            let gljLibId = pageOprObj.gljLibId;
-            console.log(gljLibId);
-            if(gljLibId){
-                if (info.cellRange.col == 0) {
-                    let cacheArr = me.cache["_GLJ_" + me.currentRationItem.ID];
-                    var tmpCodes = sheetCommonObj.analyzePasteData({header:[{dataCode: "code"}] }, info);
-                    var codes = [];
-                    for (var i = 0; i < tmpCodes.length; i++) {
-                        let rowIdx = info.cellRange.row + i;
-                        if(rowIdx < cacheArr.length){//更新
-                            me.tempCacheArr.push({org: cacheArr[rowIdx], newCode: tmpCodes[i].code});
-                            cacheArr.splice(rowIdx--, 1);
-                        }
-                        codes.push(tmpCodes[i].code);
+        let gljLibId = pageOprObj.gljLibId;
+        console.log(gljLibId);
+        if (gljLibId) {
+            if (info.cellRange.col == 0) {
+                let cacheArr = me.cache["_GLJ_" + me.currentRationItem.ID];
+                var tmpCodes = sheetCommonObj.analyzePasteData({ header: [{ dataCode: "code" }] }, info);
+                var codes = [];
+                for (var i = 0; i < tmpCodes.length; i++) {
+                    let rowIdx = info.cellRange.row + i;
+                    if (rowIdx < cacheArr.length) {//更新
+                        me.tempCacheArr.push({ org: cacheArr[rowIdx], newCode: tmpCodes[i].code });
+                        cacheArr.splice(rowIdx--, 1);
                     }
-                    me.addGljItems(codes, gljLibId, info.cellRange);
-                } else {
-                    //修改用量
-                    if(me.cache["_GLJ_" + me.currentRationItem.ID] && info.cellRange.row < me.cache["_GLJ_" + me.currentRationItem.ID].length){
-                        let tempConsumes = sheetCommonObj.analyzePasteData(me.setting, info);
-                        let maxCount = info.cellRange.row + info.cellRange.rowCount -1 > me.cache["_GLJ_" + me.currentRationItem.ID].length -1 ?
+                    codes.push(tmpCodes[i].code);
+                }
+                me.addGljItems(codes, gljLibId, info.cellRange);
+            } else {
+                //修改用量
+                if (me.cache["_GLJ_" + me.currentRationItem.ID] && info.cellRange.row < me.cache["_GLJ_" + me.currentRationItem.ID].length) {
+                    let tempConsumes = sheetCommonObj.analyzePasteData(me.setting, info);
+                    let maxCount = info.cellRange.row + info.cellRange.rowCount - 1 > me.cache["_GLJ_" + me.currentRationItem.ID].length - 1 ?
                         me.cache["_GLJ_" + me.currentRationItem.ID].length - info.cellRange.row : info.cellRange.rowCount;
-                        for(let i = 0; i < maxCount; i++){
-                            let roundCons = scMathUtil.roundTo(tempConsumes[i].consumeAmt, -3);
-                            me.cache["_GLJ_" + me.currentRationItem.ID][info.cellRange.row + i].consumeAmt = roundCons;
-                        }
-                        me.updateRationItem(function () {
-                            me.sheet.getParent().focus(true);
-                        });
-                        if(info.cellRange.row + info.cellRange.rowCount -1 >= me.cache["_GLJ_" + me.currentRationItem.ID].length -1){
-                            me.sheet.suspendPaint();
-                            for(let rowIdx = me.cache["_GLJ_" + me.currentRationItem.ID].length; rowIdx <= info.cellRange.row + info.cellRange.rowCount -1; rowIdx++){
-                                me.sheet.setValue(rowIdx, info.cellRange.col, '');
-                            }
-                            me.sheet.resumePaint();
-                        }
+                    for (let i = 0; i < maxCount; i++) {
+                        let roundCons = scMathUtil.roundTo(tempConsumes[i].consumeAmt, -3);
+                        me.cache["_GLJ_" + me.currentRationItem.ID][info.cellRange.row + i].consumeAmt = roundCons;
                     }
-                    else if(info.cellRange.row >= me.cache["_GLJ_" + me.currentRationItem.ID].length){
+                    me.updateRationItem(function () {
+                        me.sheet.getParent().focus(true);
+                    });
+                    if (info.cellRange.row + info.cellRange.rowCount - 1 >= me.cache["_GLJ_" + me.currentRationItem.ID].length - 1) {
                         me.sheet.suspendPaint();
-                        for(let rowIdx = info.cellRange.row; rowIdx <= info.cellRange.row + info.cellRange.rowCount -1; rowIdx ++){
+                        for (let rowIdx = me.cache["_GLJ_" + me.currentRationItem.ID].length; rowIdx <= info.cellRange.row + info.cellRange.rowCount - 1; rowIdx++) {
                             me.sheet.setValue(rowIdx, info.cellRange.col, '');
                         }
                         me.sheet.resumePaint();
                     }
                 }
+                else if (info.cellRange.row >= me.cache["_GLJ_" + me.currentRationItem.ID].length) {
+                    me.sheet.suspendPaint();
+                    for (let rowIdx = info.cellRange.row; rowIdx <= info.cellRange.row + info.cellRange.rowCount - 1; rowIdx++) {
+                        me.sheet.setValue(rowIdx, info.cellRange.col, '');
+                    }
+                    me.sheet.resumePaint();
+                }
             }
         }
     },
@@ -321,21 +329,21 @@ var rationGLJOprObj = {
         let rationSection = rationOprObj.getCache();
         let rationRow = rationOprObj.workBook.getSheet(0).getSelections()[0].row;
         me.currentRationItem = rationRow < rationSection.length ? rationSection[rationRow] : null;
-        if(me.currentRationItem && typeof me.cache["_GLJ_" + me.currentRationItem.ID] === 'undefined'){
+        if (me.currentRationItem && typeof me.cache["_GLJ_" + me.currentRationItem.ID] === 'undefined') {
             me.cache["_GLJ_" + me.currentRationItem.ID] = [];
         }
-        if(!me.currentRationItem){
+        if (!me.currentRationItem) {
             args.cancel = true;
         }
         else {
-            if(args.col !== 0 && args.col !== 5 || args.col === 5 && args.row >= me.cache["_GLJ_" + me.currentRationItem.ID].length){
+            if (args.col !== 0 && args.col !== 5 || args.col === 5 && args.row >= me.cache["_GLJ_" + me.currentRationItem.ID].length) {
                 args.cancel = true;
             }
         }
     },
-    onCellEditEnd: function(sender, args){
+    onCellEditEnd: function (sender, args) {
         var me = rationGLJOprObj;
-        if(me.currentRationItem) {
+        if (me.currentRationItem) {
             var cacheArr = me.cache["_GLJ_" + me.currentRationItem.ID];
             me.tempCacheArr = [];
             if (args.col != 0) {
@@ -378,7 +386,7 @@ var rationGLJOprObj = {
                     }
                     else {
                         if (args.row < cacheArr.length && args.editingText !== cacheArr[args.row].code) {//更新
-                            me.tempCacheArr.push({org: cacheArr[args.row], newCode: args.editingText.toString().trim()});
+                            me.tempCacheArr.push({ org: cacheArr[args.row], newCode: args.editingText.toString().trim() });
                             cacheArr.splice(args.row, 1);
                             let gljLibID = pageOprObj.gljLibId;
                             let codes = [];
@@ -406,32 +414,32 @@ var rationGLJOprObj = {
     },
     getRecoveryArr: function (tempDelArr, newArr) {//获得更新的code不存在,恢复删除的被更新数据
         let rst = [];
-        for(let i = 0, len = tempDelArr.length; i < len; i++){
+        for (let i = 0, len = tempDelArr.length; i < len; i++) {
             let isExist = false;
-            for(let j = 0, jLen = newArr.length; j < jLen; j++){
-                if(tempDelArr[i].newCode == newArr[j].code){
+            for (let j = 0, jLen = newArr.length; j < jLen; j++) {
+                if (tempDelArr[i].newCode == newArr[j].code) {
                     isExist = true;
                     break;
                 }
             }
-            if(!isExist){
+            if (!isExist) {
                 rst.push(tempDelArr[i].org);
             }
         }
         return rst;
     },
-    addGljItems: function(codes, repId, args) {
+    addGljItems: function (codes, repId, args) {
         let me = this;
-        CommonAjax.post('api/getGljItemsByCodes', {gljCodes: codes, rationRepId: repId}, function (rstData) {
-            if(rstData.length > 0){
-                if(priceProperties && priceProperties.length > 0){
+        CommonAjax.post('api/getGljItemsByCodes', { gljCodes: codes, rationRepId: repId }, function (rstData) {
+            if (rstData.length > 0) {
+                if (priceProperties && priceProperties.length > 0) {
                     let priceField = priceProperties[0].price.dataCode;
-                    for(let glj of rstData){
+                    for (let glj of rstData) {
                         glj.basePrice = glj.priceProperty && glj.priceProperty[priceField] ? glj.priceProperty[priceField] : 0;
                     }
                 }
                 sheetCommonObj.cleanData(me.sheet, me.setting, -1);
-                let rstArr = [], dummyR = {gljId: 0, consumeAmt:0}, newAddArr = [];
+                let rstArr = [], dummyR = { gljId: 0, consumeAmt: 0 }, newAddArr = [];
                 for (let i = 0; i < rstData.length; i++) {
                     dummyR.gljId = rstData[i].ID;
                     dummyR.type = rstData[i].type;
@@ -453,10 +461,10 @@ var rationGLJOprObj = {
                     }
                     me.cache["_GLJ_" + me.currentRationItem.ID] = cacheArr.concat(newAddArr);
                     let recoveryArr = me.getRecoveryArr(me.tempCacheArr, rstData);
-                    if(recoveryArr.length > 0){
+                    if (recoveryArr.length > 0) {
                         me.cache["_GLJ_" + me.currentRationItem.ID] = me.cache["_GLJ_" + me.currentRationItem.ID].concat(recoveryArr);
                     }
-                    me.cache["_GLJ_" + me.currentRationItem.ID].sort(function(a, b) {
+                    me.cache["_GLJ_" + me.currentRationItem.ID].sort(function (a, b) {
                         let aV = a.gljType + a.code,
                             bV = b.gljType + b.code;
                         if (aV > bV) {
@@ -474,14 +482,14 @@ var rationGLJOprObj = {
                     });
                 }
             }
-            else{
-                let cacheArr = me.cache["_GLJ_" + me.currentRationItem.ID]?  me.cache["_GLJ_" + me.currentRationItem.ID] : [];
+            else {
+                let cacheArr = me.cache["_GLJ_" + me.currentRationItem.ID] ? me.cache["_GLJ_" + me.currentRationItem.ID] : [];
                 let recoveryArr = me.getRecoveryArr(me.tempCacheArr, []);
-                if(recoveryArr.length > 0){
+                if (recoveryArr.length > 0) {
                     me.cache["_GLJ_" + me.currentRationItem.ID] = cacheArr.concat(recoveryArr);
                 }
                 //更新的工料机不存在
-                me.cache["_GLJ_" + me.currentRationItem.ID].sort(function(a, b) {
+                me.cache["_GLJ_" + me.currentRationItem.ID].sort(function (a, b) {
                     let aV = a.gljType + a.code,
                         bV = b.gljType + b.code;
                     if (aV > bV) {
@@ -492,7 +500,7 @@ var rationGLJOprObj = {
                     return 0;
                 });
                 $('#alertModalBtn').click();
-                $('#alertText').text("人材机"+ codes + "不存在,请查找你所需要的人材机,或新增人材机");
+                $('#alertText').text("人材机" + codes + "不存在,请查找你所需要的人材机,或新增人材机");
                 $('#alertModalCls').click(function () {
                     me.showGljItems(me.currentRationItem.ID);
                 });
@@ -502,29 +510,29 @@ var rationGLJOprObj = {
             }
         });
     },
-    round(v, e){
-        var t=1;
-        for(;e>0;t*=10,e--);
-        for(;e<0;t/=10,e++);
-        return Math.round(v*t)/t;
+    round(v, e) {
+        var t = 1;
+        for (; e > 0; t *= 10, e--);
+        for (; e < 0; t /= 10, e++);
+        return Math.round(v * t) / t;
     },
     rationCal: function () {
         let me = rationGLJOprObj;
-        let price = {gljType1: [], gljType2: [], gljType3: []}, rst = {labourPrice: 0, materialPrice: 0, machinePrice: 0}, rationBasePrc = 0;
-        if(me.currentRationItem && me.cache['_GLJ_' + me.currentRationItem.ID]){
+        let price = { gljType1: [], gljType2: [], gljType3: [] }, rst = { labourPrice: 0, materialPrice: 0, machinePrice: 0 }, rationBasePrc = 0;
+        if (me.currentRationItem && me.cache['_GLJ_' + me.currentRationItem.ID]) {
             let cacheArr = me.cache['_GLJ_' + me.currentRationItem.ID];
             cacheArr.forEach(function (gljData) {
-                if(gljData.gljType && gljData.basePrice && gljData.consumeAmt){
+                if (gljData.gljType && gljData.basePrice && gljData.consumeAmt) {
                     let parent = me.distTypeTree.distTypes[me.distTypeTree.prefix + gljData.gljType].parent;
-                    if(parent && parent.data.ID <= 3){
-                        price['gljType' + parent.data.ID].push(scMathUtil.roundTo( gljData.basePrice * gljData.consumeAmt, -3));//取三位
+                    if (parent && parent.data.ID <= 3) {
+                        price['gljType' + parent.data.ID].push(scMathUtil.roundTo(gljData.basePrice * gljData.consumeAmt, -3));//取三位
                     }
-                    if(!parent && gljData.gljType <= 3){
-                        price['gljType' + gljData.gljType].push(scMathUtil.roundTo( gljData.basePrice * gljData.consumeAmt, -3));//取三位
+                    if (!parent && gljData.gljType <= 3) {
+                        price['gljType' + gljData.gljType].push(scMathUtil.roundTo(gljData.basePrice * gljData.consumeAmt, -3));//取三位
                     }
                 }
             });
-            if(price.gljType1.length > 0){
+            if (price.gljType1.length > 0) {
                 let labourPrice = 0;
                 price.gljType1.forEach(function (singlePrc) {
                     labourPrice = scMathUtil.roundTo(labourPrice + singlePrc, me.processDecimal);
@@ -533,7 +541,7 @@ var rationGLJOprObj = {
                 rst.labourPrice = roundPrice;
                 rationBasePrc = scMathUtil.roundTo(rationBasePrc + roundPrice, -2);
             }
-            if(price.gljType2.length > 0){
+            if (price.gljType2.length > 0) {
                 let materialPrice = 0;
                 price.gljType2.forEach(function (singlePrc) {
                     materialPrice = scMathUtil.roundTo(materialPrice + singlePrc, me.processDecimal);
@@ -542,7 +550,7 @@ var rationGLJOprObj = {
                 rst.materialPrice = roundPrice;
                 rationBasePrc = scMathUtil.roundTo(rationBasePrc + roundPrice, -2);
             }
-            if(price.gljType3.length > 0){
+            if (price.gljType3.length > 0) {
                 let machinePrice = 0;
                 price.gljType3.forEach(function (singlePrc) {
                     machinePrice = scMathUtil.roundTo(machinePrice + singlePrc, me.processDecimal);
@@ -555,7 +563,7 @@ var rationGLJOprObj = {
         }
         return rst;
     },
-    updateRationItem: function(callback) {
+    updateRationItem: function (callback) {
         var me = this, updateArr = [];
         if (me.currentRationItem) {
             me.currentRationItem.rationGljList = me.buildRationItemGlj();
@@ -567,23 +575,23 @@ var rationGLJOprObj = {
             me.currentRationItem.basePrice = price.rationBasePrc;
             updateArr.push(me.currentRationItem);
             rationOprObj.mixUpdateRequest(updateArr, [], [], function () {
-                if(callback) callback();
+                if (callback) callback();
             });
         }
     },
 
-    buildRationItemGlj: function(){
+    buildRationItemGlj: function () {
         var me = this, rst = [];
         if (me.currentRationItem && me.cache["_GLJ_" + me.currentRationItem.ID]) {
             var cacheArr = me.cache["_GLJ_" + me.currentRationItem.ID];
             for (var i = 0; i < cacheArr.length; i++) {
-                rst.push({gljId: cacheArr[i].gljId, consumeAmt: cacheArr[i].consumeAmt, type: cacheArr[i].type});
+                rst.push({ gljId: cacheArr[i].gljId, consumeAmt: cacheArr[i].consumeAmt, type: cacheArr[i].type });
             }
         }
         return rst;
     },
 
-    createRationGljDisplayItem: function(rItem, repGlj) {
+    createRationGljDisplayItem: function (rItem, repGlj) {
         var rst = {};
         rst.gljId = rItem.gljId;
         rst.type = rItem.type;
@@ -596,7 +604,7 @@ var rationGLJOprObj = {
         rst.gljType = repGlj.gljType;
         return rst;
     },
-    getGljItems: function(rationItem, callback) {
+    getGljItems: function (rationItem, callback) {
         let me = this, rationID = rationItem.ID, rationGljList = rationItem.rationGljList ? rationItem.rationGljList : [], rationType = rationItem.type;
         me.currentRationItem = rationItem;
         if (me.cache["_GLJ_" + rationID]) {
@@ -609,11 +617,11 @@ var rationGLJOprObj = {
                 idObj.id = rationGljList[i].gljId;
                 gljIds.push(idObj);
             }
-            CommonAjax.post('api/getGljItemsByIds', {ids: gljIds}, function (rstData) {
+            CommonAjax.post('api/getGljItemsByIds', { ids: gljIds }, function (rstData) {
                 sheetCommonObj.cleanSheet(me.sheet, me.setting, -1);
-                if(priceProperties && priceProperties.length > 0){
+                if (priceProperties && priceProperties.length > 0) {
                     let priceField = priceProperties[0].price.dataCode;
-                    for(let glj of rstData){
+                    for (let glj of rstData) {
                         glj.basePrice = glj.priceProperty && glj.priceProperty[priceField] ? glj.priceProperty[priceField] : 0;
                     }
                 }
@@ -626,7 +634,7 @@ var rationGLJOprObj = {
                         }
                     }
                 }
-                function compare(){
+                function compare() {
                     return function (a, b) {
                         let aV = a.gljType + a.code,
                             bV = b.gljType + b.code;
@@ -641,11 +649,11 @@ var rationGLJOprObj = {
                 cacheArr.sort(compare());
                 me.cache["_GLJ_" + rationID] = cacheArr;
                 me.showGljItems(rationID);
-                if(callback) callback();
+                if (callback) callback();
             });
         }
     },
-    showGljItems: function(rationID) {
+    showGljItems: function (rationID) {
         var me = this;
         if (me.cache["_GLJ_" + rationID]) {
             sheetCommonObj.cleanData(me.sheet, me.setting, -1);

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

@@ -1,25 +1,4 @@
-/***** Google fonts import ************/
-@import url("https://fonts.googleapis.com/css?family=Gothic+A1:300,400,500,600,700,900");
-/*------------------------------------------------------------------
-[Layout]
-* body
-+ Global styles
-+ Header / .header
-+ hero / benner
-+ Start Call Action
-+ What people say
-+ price
-+ Our Team
-+ newsletter
-+ Form
-+ Footer / #footer
-+ Responsive
-/*
-============================================
-Global styles
-============================================
-*/
-/*
+/*
 01 -  Global styles
 */
 html,

+ 198 - 137
web/building_saas/main/js/models/calc_program.js

@@ -210,7 +210,8 @@ let calcTools = {
         else if (this.isBill(treeNode)){
             let nodeQ = this.uiNodeQty(treeNode);
             let q = nodeQ ? nodeQ : 1;
-            let rNodes = projectObj.project.Ration.getRationNodes(treeNode);
+            let allNodes = projectObj.project.Ration.getRationNodes(treeNode);
+            let rNodes = allNodes.filter(function (node) {return node.data.type != rationType.volumePrice});
             let rations = rNodes.map(function (node) {return node.data});
             treeNode.data.gljList = projectObj.project.ration_glj.getGatherGljArrByRations(rations, needOneBill, q);
         };
@@ -275,7 +276,7 @@ let calcTools = {
 
         // 初始化前先拦截末定义的情况
         if (!treeNode.data.feesIndex || !treeNode.data.feesIndex[feeObj.fieldName]){
-            if (feeObj.unitFee == 0 && feeObj.totalFee == 0) return;
+            if (feeObj.unitFee == 0 && feeObj.totalFee == 0 && feeObj.tenderUnitFee == 0 && feeObj.tenderTotalFee == 0) return;
         }
 
         this.initFeeField(treeNode, feeObj.fieldName);
@@ -358,25 +359,13 @@ let calcTools = {
         if ((priceType == priceTypes.ptDiffPrice) && (gljTypes.includes(gljType.MACHINE_LABOUR) || gljTypes.includes(gljType.FUEL_POWER_FEE))){
             for (let glj of treeNode.data.gljList) {
                 if ([gljType.GENERAL_MACHINE, gljType.INSTRUMENT].includes(glj.type)){
-                    if (isTender){
-                        calcTools.calcGLJTenderPrice(glj);
-                        calcTools.calcGLJTenderQty(treeNode, glj);
-                    };
                     let mds = projectObj.project.composition.getCompositionByGLJ(glj);
                     if (!mds) mds = [];
                     for (let md of mds){
                         if (gljTypes.includes(md.type)){
-                            let gljQ = me.uiGLJQty(glj["quantity"]);
-                            let mdMP = md["marketPrice"];
-                            if (isTender){
-                                calcTools.calcGLJTenderPrice(md);
-                                mdMP = md["tenderPrice"];
-
-                                calcTools.calcGLJTenderQty(treeNode, glj);
-                                gljQ = me.uiGLJQty(glj["tenderQuantity"]);
-                            };
+                            let gljQ = isTender ? me.uiGLJQty(glj["tenderQuantity"]) : me.uiGLJQty(glj["quantity"]);
+                            let mdMP = isTender ? md["tenderPrice"] : md["marketPrice"];
                             let mdQ = me.uiGLJQty(md.consumption);
-
                             let mdAP = calcTools.hasAdjustPrice() ? md["adjustPrice"] : md["basePrice"];
                             // if (aprice != mprice){
                             temp = (temp + (gljQ * mdQ * mdMP).toDecimal(decimalObj.process)).toDecimal(decimalObj.process);
@@ -391,13 +380,11 @@ let calcTools = {
         else{
             for (let glj of treeNode.data.gljList) {
                     if (gljTypes.indexOf(glj.type) >= 0) {
-                    if (isTender){
-                        calcTools.calcGLJTenderPrice(glj);
-                        calcTools.calcGLJTenderQty(treeNode, glj);
-                    };
                     let qty = isTender ? me.uiGLJQty(glj["tenderQuantity"]) : me.uiGLJQty(glj["quantity"]);
-                    let mprice = isTender ? me.uiGLJPrice(glj["tenderPrice"], glj) : me.uiGLJPrice(glj["marketPrice"], glj);
-                    let aprice = calcTools.hasAdjustPrice() ? me.uiGLJPrice(glj["adjustPrice"], glj) : me.uiGLJPrice(glj["basePrice"], glj);
+                    // let mprice = isTender ? me.uiGLJPrice(glj["tenderPrice"], glj) : me.uiGLJPrice(glj["marketPrice"], glj);
+                    // let aprice = calcTools.hasAdjustPrice() ? me.uiGLJPrice(glj["adjustPrice"], glj) : me.uiGLJPrice(glj["basePrice"], glj);
+                    let mprice = isTender ? glj["tenderPrice"] : glj["marketPrice"];
+                    let aprice = calcTools.hasAdjustPrice() ? glj["adjustPrice"] : glj["basePrice"];
 
                     if (priceType == priceTypes.ptDiffPrice){
                         // if (aprice != mprice){
@@ -406,7 +393,8 @@ let calcTools = {
                         // }
                     }
                     else {
-                        if (priceType == priceTypes.ptBasePrice){ price = me.uiGLJPrice(glj["basePrice"], glj);}
+                        // if (priceType == priceTypes.ptBasePrice){ price = me.uiGLJPrice(glj["basePrice"], glj);}
+                        if (priceType == priceTypes.ptBasePrice){ price = glj["basePrice"];}
                         else if (priceType == priceTypes.ptAdjustPrice){price = aprice;}
                         else if (priceType == priceTypes.ptMarketPrice){price = mprice;}
                         /*if (projectObj.project.property.areaSetting && treeNode.data.areaIncreaseFee){
@@ -455,13 +443,7 @@ let calcTools = {
                 // 机型不符
                 if ((masterTypeFilter.length > 0) && (glj.model && !masterTypeFilter.includes(glj.model))) continue;
 
-                let gljQ;
-                if (isTender){
-                    calcTools.calcGLJTenderQty(treeNode, glj);
-                    gljQ = glj.tenderQuantity;
-                }
-                else
-                    gljQ = glj.quantity;
+                let gljQ = isTender ? glj.tenderQuantity : glj.quantity;
                 // 获取机械组成物(调价不深入到组成物)
                 let mds = projectObj.project.composition.getCompositionByGLJ(glj);
                 if (!mds) mds = [];
@@ -483,91 +465,112 @@ let calcTools = {
         result = (result).toDecimal(decimalObj.ration.unitPrice);
         return result;
     },
-    // 总造价清单、叶子清单、定额的暂估费。父清单是汇总子清单的暂估费,走计算程序逻辑,不在这里。
-    estimateFee: function (treeNode, isBase, isTender){
-        let me = this, sumU = 0, sumT = 0;
-        let nodeQ = me.uiNodeQty(treeNode, isTender);
+    // 叶子清单、定额、总造价清单的暂估费。(父级清单是汇总子清单的暂估费,走计算程序逻辑,不在这里)
+    estimateFee: function (treeNode, isBase, isTender){   // isBase, isTender 这两个参数用于基数计算
+        let me = this, sumU = 0, sumT = 0, sumTU = 0, sumTT = 0;
+        let nodeQ = me.uiNodeQty(treeNode);
+        let nodeTQ = me.uiNodeTenderQty(treeNode);
         let isGather = (projectObj.project.property.zanguCalcMode == zanguCalcType.gatherMaterial);
 
         // 先汇总数量,再乘市场价。如果是叶子清单,进入这里的gljList中的材料,已经是同类材料跨定额汇总过的了。
         function eTFee(){
-            if (!treeNode.data.gljList) return 0;
+            let rst = {eT: 0, eTT: 0};
+            if (!treeNode.data.gljList) return rst;
+
             let GLJObjs = [];
             for (let glj of treeNode.data.gljList) {
                 if (!allMaterialTypes.includes(glj.type)) continue;
                 if (glj.isEstimate){
-                    GLJObjs.push({code: glj.code, name: glj.name, specs: glj.specs, unit: glj.unit, type: glj.type,
-                        // quantity: (nodeQ * glj.quantity).toDecimal(decimalObj.process),
-                        quantity: me.uiGLJQty((glj.totalQuantity)).toDecimal(decimalObj.process),
-                        marketPrice: glj.marketPrice});
+                    let q = me.uiGLJQty((glj.totalQuantity)).toDecimal(decimalObj.process);
+                    GLJObjs.push({code: glj.code, name: glj.name, specs: glj.specs, unit: glj.unit, type: glj.type, quantity: q,
+                        marketPrice: glj.marketPrice, tenderQuantity: glj.tenderQuantity, tenderPrice: glj.tenderPrice});
                 }
                 else{   // 组成物
                     if (!compositionTypes.includes(glj.type)) continue;
                     let mds = projectObj.project.composition.getCompositionByGLJ(glj);
                     if (!mds) mds = [];
                     for (let md of mds){
-                        if (md.isEstimate){
-                            let isExist = false;
-                            // let totalQ = (nodeQ * me.uiGLJQty(glj.quantity)).toDecimal(decimalObj.glj.quantity);
-                            let totalQ = me.uiGLJQty((glj.totalQuantity)).toDecimal(decimalObj.glj.quantity);
-                             let mdQ = (totalQ * me.uiGLJQty(md.consumption)).toDecimal(decimalObj.process);
-
-                            for (let obj of GLJObjs){
-                                if (gljOprObj.getIndex(md, gljKeyArray) == gljOprObj.getIndex(obj, gljKeyArray)){
-                                    isExist = true;
-                                    obj.quantity = (obj.quantity + mdQ).toDecimal(decimalObj.glj.quantity);
-                                    break;
-                                }
-                            };
-                            if (!isExist)
-                                GLJObjs.push({code: md.code, name: md.name, specs: md.specs, unit: md.unit, type: md.type,
-                                    quantity: mdQ, marketPrice: md.marketPrice});
-                        }
+                        if (!md.isEstimate) continue;
+                        let isExist = false;
+                        // let glj_totalQ = (nodeQ * me.uiGLJQty(glj.quantity)).toDecimal(decimalObj.glj.quantity);
+                        let glj_totalQ = me.uiGLJQty((glj.totalQuantity)).toDecimal(decimalObj.glj.quantity);
+                        let glj_tender_totalQ = (nodeTQ * me.uiGLJQty(glj.tenderQuantity)).toDecimal(decimalObj.glj.quantity);
+
+                         let mdQ = (glj_totalQ * me.uiGLJQty(md.consumption)).toDecimal(decimalObj.process);
+                         let mdTQ = (glj_tender_totalQ * me.uiGLJQty(md.consumption)).toDecimal(decimalObj.process);
+
+                        for (let obj of GLJObjs){
+                            if (gljOprObj.getIndex(md, gljKeyArray) == gljOprObj.getIndex(obj, gljKeyArray)){
+                                isExist = true;
+                                obj.quantity = (obj.quantity + mdQ).toDecimal(decimalObj.glj.quantity);
+                                obj.tenderQuantity = (obj.tenderQuantity + mdTQ).toDecimal(decimalObj.glj.quantity);
+                                break;
+                            }
+                        };
+
+                        if (!isExist)
+                            GLJObjs.push({code: md.code, name: md.name, specs: md.specs, unit: md.unit, type: md.type, quantity: mdQ,
+                                marketPrice: md.marketPrice, tenderQuantity: mdTQ, tenderPrice: md.tenderPrice});
+
                     }
                 }
             };
 
-            let rst = 0;
             for (let obj of GLJObjs){
-                let tp = (me.uiGLJQty(obj.quantity) * me.uiGLJPrice(obj.marketPrice, obj)).toDecimal(decimalObj.bills.totalPrice);
-                rst = (rst + tp).toDecimal(decimalObj.bills.totalPrice);
+                let t = (me.uiGLJQty(obj.quantity) * me.uiGLJPrice(obj.marketPrice, obj)).toDecimal(decimalObj.bills.totalPrice);
+                rst.eT = (rst.eT + t).toDecimal(decimalObj.bills.totalPrice);
+
+                let tt = (me.uiGLJQty(obj.tenderQuantity) * me.uiGLJPrice(obj.tenderPrice, obj)).toDecimal(decimalObj.bills.totalPrice);
+                rst.eTT = (rst.eTT + tt).toDecimal(decimalObj.bills.totalPrice);
             };
             return rst;
         };
         // 汇总子结点的暂估合价
         function eTFeeByChildren(){
-            let rst = 0;
+            let rst = {eT: 0, eTT: 0};
             for (let node of treeNode.children){
                 if (node.data.feesIndex && node.data.feesIndex['estimate']) {
-                    rst = (rst + parseFloatPlus(node.data.feesIndex['estimate'].totalFee)).toDecimal(decimalObj.process);
+                    rst.eT = (rst.eT + parseFloatPlus(node.data.feesIndex['estimate'].totalFee)).toDecimal(decimalObj.process);
+                    rst.eTT = (rst.eTT + parseFloatPlus(node.data.feesIndex['estimate'].tenderTotalFee)).toDecimal(decimalObj.process);
                 };
             };
-            rst = (rst).toDecimal(decimalObj.bills.totalPrice);
+            rst.eT = (rst.eT).toDecimal(decimalObj.bills.totalPrice);
+            rst.eTT = (rst.eTT).toDecimal(decimalObj.bills.totalPrice);
             return rst;
         };
         // 先数量乘市场价,再汇总
         function eUFee(){
             if (!treeNode.data.gljList) return 0;
-            let rst = 0;
+            let rst = {eU: 0, eTU: 0};
             for (let glj of treeNode.data.gljList) {
                 if (!allMaterialTypes.includes(glj.type)) continue;
                 if (glj.isEstimate){
-                    rst = rst + (me.uiGLJQty(glj.quantity) * me.uiGLJPrice(glj.marketPrice, glj)).toDecimal(decimalObj.process);
-                    rst = rst.toDecimal(decimalObj.process);
+                    rst.eU = rst.eU + (me.uiGLJQty(glj.quantity) * me.uiGLJPrice(glj.marketPrice, glj)).toDecimal(decimalObj.process);
+                    rst.eU = rst.eU.toDecimal(decimalObj.process);
+                    // 不能直接用glj.tenderPrice,这个值不可靠。当调价界面删除单价系数后,tenderPrice没有实时计算,取得的值为0
+                    rst.eTU = rst.eTU + (me.uiGLJQty(glj.tenderQuantity) * glj.tenderPrice).toDecimal(decimalObj.process);
+                    rst.eTU = rst.eTU.toDecimal(decimalObj.process);
                 }
-                else{   // 组成物
+                else{   // 组成物
                     if (!compositionTypes.includes(glj.type)) continue;
                     let mds = projectObj.project.composition.getCompositionByGLJ(glj);
                     if (!mds) mds = [];
                     for (let md of mds){
                         if (!md.isEstimate) continue;
+
                         let mdU = (me.uiGLJQty(md.consumption) * me.uiGLJPrice(md.marketPrice)).toDecimal(decimalObj.glj.unitPrice);
-                        rst = rst + (mdU * me.uiGLJQty(glj.quantity)).toDecimal(decimalObj.process);
-                        rst = rst.toDecimal(decimalObj.process);
+                        rst.eU = rst.eU + (mdU * me.uiGLJQty(glj.quantity)).toDecimal(decimalObj.process);
+                        rst.eU = rst.eU.toDecimal(decimalObj.process);
+
+                        // 数量只调到工料机级别,工料机下的组成物不调量(如机械、混凝土)。调价调的是工料机下的组成物的价。
+                        let mdTU = (me.uiGLJQty(md.consumption) * md.tenderPrice).toDecimal(decimalObj.glj.unitPrice);
+                        rst.eTU = rst.eTU + (mdTU * glj.tenderQuantity).toDecimal(decimalObj.process);
+                        rst.eTU = rst.eTU.toDecimal(decimalObj.process);
                     }
                 }
             };
-            rst = rst.toDecimal(decimalObj.bills.unitPrice);
+            rst.eU = rst.eU.toDecimal(decimalObj.bills.unitPrice);
+            rst.eTU = rst.eTU.toDecimal(decimalObj.bills.unitPrice);
             return rst;
         };
 
@@ -576,56 +579,87 @@ let calcTools = {
             let nodes = projectObj.project.mainTree.roots;
             for (let node of nodes){
                 if (me.isTotalCostBill(node)) break;
-                let eU = 0, eT = 0;
+                let eU = 0, eT = 0, eTU = 0, eTT = 0;
                 if (node.data.feesIndex && node.data.feesIndex.estimate){
                     eU = node.data.feesIndex.estimate.unitFee;
                     eT = node.data.feesIndex.estimate.totalFee;
+                    eTU = node.data.feesIndex.estimate.tenderUnitFee;
+                    eTT = node.data.feesIndex.estimate.tenderTotalFee;
                 }
                 else {
-                    eU = 0, eT = 0;
+                    eU = 0, eT = 0, eTU = 0, eTT = 0;
                 };
                 sumU = (sumU + parseFloatPlus(eU)).toDecimal(decimalObj.process);
                 sumT = (sumT + parseFloatPlus(eT)).toDecimal(decimalObj.process);
+                sumTU = (sumTU + parseFloatPlus(eTU)).toDecimal(decimalObj.process);
+                sumTT = (sumTT + parseFloatPlus(eTT)).toDecimal(decimalObj.process);
             };
             sumU = (sumU).toDecimal(decimalObj.bills.unitPrice);
             sumT = (sumT).toDecimal(decimalObj.bills.totalPrice);
+            sumTU = (sumTU).toDecimal(decimalObj.bills.unitPrice);
+            sumTT = (sumTT).toDecimal(decimalObj.bills.totalPrice);
         }
         else if (me.isParentBill(treeNode)){  // 父清单不要汇总单价。
-            sumT = eTFeeByChildren();
+            let eTFBC = eTFeeByChildren();
+            sumT = eTFBC.eT;
+            sumTT = eTFBC.eTT;
             sumU = undefined;
+            sumTU = undefined;
         }
         else if (me.isLeafBill(treeNode)){
             if (projectObj.project.Bills.isEngineerEst(treeNode)){
                 if (treeNode.data.feesIndex['common'] != undefined){
                     sumT = treeNode.data.feesIndex['common'].totalFee;
                     sumU = treeNode.data.feesIndex['common'].unitFee;
+                    sumTT = treeNode.data.feesIndex['common'].tenderTotalFee;
+                    sumTU = treeNode.data.feesIndex['common'].tenderUnitFee;
                 }
             }
             else{
                 if (isGather){
                     me.getGLJList(treeNode, false);
-                    sumT = eTFee();
+                    let eTF = eTFee();
+                    sumT = eTF.eT;
+                    sumTT = eTF.eTT;
                 }
-                else
-                    sumT = eTFeeByChildren();
+                else{
+                    let eTFBC = eTFeeByChildren();
+                    sumT = eTFBC.eT;
+                    sumTT = eTFBC.eTT;
+                };
 
                 let q = nodeQ ? nodeQ : 1;
                 sumU = (sumT / q).toDecimal(decimalObj.bills.totalPrice);
+                let tq = nodeTQ ? nodeTQ : 1;
+                sumTU = (sumTT / tq).toDecimal(decimalObj.bills.totalPrice);
             }
         }
         else if (me.isRationCategory(treeNode)){
             me.getGLJList(treeNode, false);
 
-            sumU = eUFee();
-            if (isBase) return sumU;
+            let eUF = eUFee();
+            sumU = eUF.eU;
+            sumTU = eUF.eTU;
+            if (isBase) {
+                if (isTender)
+                    return sumTU
+                else
+                    return sumU;
+            };
 
-            if (isGather)
-                sumT = eTFee()
-            else
+            if (isGather){
+                let eTF = eTFee();
+                sumT = eTF.eT;
+                sumTT = eTF.eTT;
+            }
+            else{
                 sumT = (nodeQ * sumU).toDecimal(decimalObj.ration.totalPrice);
+                sumTT = (nodeTQ * sumTU).toDecimal(decimalObj.ration.totalPrice);
+            }
         };
 
-        me.checkFeeField(treeNode, {'fieldName': 'estimate', 'unitFee': sumU, 'totalFee': sumT});
+        me.checkFeeField(treeNode, {'fieldName': 'estimate', 'unitFee': sumU, 'totalFee': sumT,
+            'tenderUnitFee': sumTU, 'tenderTotalFee': sumTT});
     },
     marketPriceToBase: function (treeNode, baseName, isTender) {
         if (treeNode.data.type != rationType.volumePrice && treeNode.data.type != rationType.gljRation) return;
@@ -667,8 +701,17 @@ let calcTools = {
         }
         else {
             if (isRCJZC(treeNode, baseName)) {
-                if (treeNode.data.type == rationType.volumePrice)
-                    result = treeNode.data.marketUnitFee ? parseFloat(treeNode.data.marketUnitFee).toDecimal(decimalObj.ration.unitPrice) : 0
+                if (treeNode.data.type == rationType.volumePrice){
+                    if (isTender){
+                        let coe = this.tenderCoe_GLJPrice();
+                        if (treeNode.data.marketUnitFee)
+                            result =  (parseFloat(treeNode.data.marketUnitFee) * coe).toDecimal(decimalObj.ration.unitPrice)
+                        else
+                            result =  0;
+                    }
+                    else
+                        result = treeNode.data.marketUnitFee ? parseFloat(treeNode.data.marketUnitFee).toDecimal(decimalObj.ration.unitPrice) : 0
+                }
                 else if (treeNode.data.type == rationType.gljRation)
                 // result = treeNode.data.basePrice ? parseFloat(treeNode.data.basePrice).toDecimal(decimalObj.ration.unitPrice) : 0;
                 // 这里因为是算基数所以要取基价,但不能直接取basePrice,受限于项目属性的三个选项。
@@ -730,14 +773,7 @@ let calcTools = {
 
         let sum = 0;
         for (let glj of treeNode.data.gljList){
-            let gljQ, gljP;
-            if (isTender){
-                calcTools.calcGLJTenderQty(treeNode, glj);
-                gljQ = glj.tenderQuantity;
-            }
-            else
-                gljQ = glj.quantity;
-
+            let gljQ = isTender ? glj.tenderQuantity : glj.quantity;
             gljP = isRationPirce ? glj.basePrice : glj.marketPrice;
 
             let X = 1;    // 部分甲供系数(默认1,即完全甲供)
@@ -851,6 +887,58 @@ let calcTools = {
     uiNodeQty: function (treeNode){
         return parseFloatPlus(treeNode.data.quantity).toDecimal(decimalObj.decimal("quantity", treeNode));
     },
+
+    // 在项目工料机里检查该工料机是否参与调价
+    isTenderGLJ: function (glj){
+        let projGLJ = this.getProjectGLJ(glj);
+        return !(projGLJ && projGLJ.is_adjust_price == 1);
+    },
+    // 取单价调价系数
+    tenderCoe_GLJPrice: function (){
+        let coe = 1;
+        if (projectObj.project.property.tenderSetting && projectObj.project.property.tenderSetting.gljPriceTenderCoe){
+            coe = projectObj.project.property.tenderSetting.gljPriceTenderCoe;
+            if (coe == '0') coe = 1;  // 这里加个保护
+        };
+        return coe;
+    },
+    tenderCoe_NodeQty: function (treeNode){
+        let coe = 1;
+        if (treeNode.data.rationQuantityCoe){
+            coe = treeNode.data.rationQuantityCoe;
+            if (coe == '0') coe = 1;  // 这里加个保护
+        };
+        return coe;
+    },
+    tenderCoe_GLJQty: function (treeNode, glj){
+        let coe = 1;
+        if (!treeNode.data.quantityCoe) return coe;
+
+        if (gljType.LABOUR == glj.type){
+            if (treeNode.data.quantityCoe.labour)
+                coe = treeNode.data.quantityCoe.labour;
+        }
+        else if (baseMaterialTypes.indexOf(glj.type)){
+            if (treeNode.data.quantityCoe.material)
+                coe = treeNode.data.quantityCoe.material;
+        }
+        else if (baseMachineTypes.indexOf(glj.type)){
+            if (treeNode.data.quantityCoe.machine)
+                coe = treeNode.data.quantityCoe.machine;
+        }
+        else if (gljType.MAIN_MATERIAL == glj.type){
+            if (treeNode.data.quantityCoe.main)
+                coe = treeNode.data.quantityCoe.main;
+        }
+        else if (gljType.EQUIPMENT == glj.type){
+            if (treeNode.data.quantityCoe.equipment)
+                coe = treeNode.data.quantityCoe.equipment;
+        };
+
+        if (coe == '0') coe = 1;   // 这里加个保护
+        return coe;
+    },
+
     uiNodeTenderQty: function (treeNode){
         return this.calcNodeTenderQty(treeNode);
     },
@@ -872,6 +960,7 @@ let calcTools = {
                 if (treeNode.data.rationQuantityCoe)
                     qCoe = treeNode.data.rationQuantityCoe;
             };
+            if (qCoe == '0' || qCoe == 0) qCoe = 1;
             treeNode.data.tenderQuantity = (this.uiNodeQty(treeNode) * qCoe).toDecimal(decimalObj.decimal("quantity", treeNode));
             return treeNode.data.tenderQuantity;
         }
@@ -879,34 +968,14 @@ let calcTools = {
     calcGLJTenderQty: function (treeNode, glj){
         if (treeNode.data.quantityCoe == undefined){
             glj.tenderQuantity = glj.quantity;
-            return;
-        };
-
-        let qCoe = 1;
-        let projGLJ = calcTools.getProjectGLJ(glj);
-        if (projGLJ.is_adjust_price != 1) {      // 先检查项目工料机里,该工料机是否参与调价
-            if (gljType.LABOUR == glj.type){
-                if (treeNode.data.quantityCoe.labour)
-                    qCoe = treeNode.data.quantityCoe.labour;
-            }
-            else if (baseMaterialTypes.indexOf(glj.type)){
-                if (treeNode.data.quantityCoe.material)
-                    qCoe = treeNode.data.quantityCoe.material;
-            }
-            else if (baseMachineTypes.indexOf(glj.type)){
-                if (treeNode.data.quantityCoe.machine)
-                    qCoe = treeNode.data.quantityCoe.machine;
-            }
-            else if (gljType.MAIN_MATERIAL == glj.type){
-                if (treeNode.data.quantityCoe.main)
-                    qCoe = treeNode.data.quantityCoe.main;
-            }
-            else if (gljType.EQUIPMENT == glj.type){
-                if (treeNode.data.quantityCoe.equipment)
-                    qCoe = treeNode.data.quantityCoe.equipment;
-            };
         }
-        glj.tenderQuantity = (glj.quantity * qCoe).toDecimal(decimalObj.glj.quantity);
+        else{
+            let coe = 1;
+            if (this.isTenderGLJ(glj))
+                coe = this.tenderCoe_GLJQty(treeNode, glj);
+            glj.tenderQuantity = (glj.quantity * coe).toDecimal(decimalObj.glj.quantity);
+        }
+        return glj.tenderQuantity;
     },
     calcGLJTenderPrice: function (glj) {
         let projGLJ = calcTools.getProjectGLJ(glj);
@@ -914,13 +983,13 @@ let calcTools = {
             glj.tenderPrice = projectObj.project.projectGLJ.getTenderMarketPrice(projGLJ);
         }else{
             let pCoe = 1;
-            if (projGLJ.is_adjust_price != 1){      // 先检查项目工料机里,该工料机是否参与调价
-                if (projectObj.project.property.tenderSetting && projectObj.project.property.tenderSetting.gljPriceTenderCoe)
-                    pCoe = projectObj.project.property.tenderSetting.gljPriceTenderCoe;
-            };
+            // 先从项目工料机里检查该工料机是否参与调价
+            if (projGLJ.is_adjust_price != 1) pCoe = this.tenderCoe_GLJPrice();
             glj.tenderPrice = (glj.marketPrice * pCoe).toDecimal(decimalObj.glj.unitPrice);
         };
+        return glj.tenderPrice;
     },
+
     // 界面显示的工料机价格,包括定额价、市场价等。参数 price 传入一个普通的价格数值即可。
     uiGLJPrice: function (price, glj){
         if (price){
@@ -994,16 +1063,8 @@ let calcTools = {
         calcTools.uiNodeQty(node);
         for (let glj of node.data.gljList) {
             if (glj.type == gljType.LABOUR) {
-                let gljQ;
-                if (isTender){
-                    calcTools.calcGLJTenderQty(node, glj);
-                    gljQ = glj.tenderQuantity.toDecimal(decimalObj.glj.quantity);
-                }
-                else
-                    gljQ = glj.quantity.toDecimal(decimalObj.glj.quantity);
-
-                // rst = rst + (gljQ * calcTools.uiNodeQty(node)).toDecimal(decimalObj.process);
-                // rst = rst.toDecimal(decimalObj.process);
+                let gljQ = isTender ? glj.tenderQuantity : glj.quantity;
+                gljQ = gljQ.toDecimal(decimalObj.glj.quantity);
                 rst = (rst + gljQ).toDecimal(decimalObj.process);
             }
         };
@@ -1724,7 +1785,7 @@ class CalcProgram {
     };
 
     // 只计算treeNode自身。changedArr: 外部传来的一个数组,专门存储发生变动的节点。
-    innerCalc(treeNode, changedArr, tender){
+    innerCalc(treeNode, changedArr, tenderType){
         let me = this;
         // 仅用作树节点显示的工料机不能参与计算。
         if (treeNode.sourceType === ModuleNames.ration_glj) return;
@@ -1967,7 +2028,7 @@ class CalcProgram {
                     calcItem.unitFee = (eval(calcItem.compiledExpr) * feeRate * 0.01).toDecimal(decimalObj.decimal('unitPrice', treeNode));
                     calcItem.totalFee = (calcItem.unitFee * calcTools.uiNodeQty(treeNode)).toDecimal(decimalObj.decimal('totalPrice', treeNode));
 
-                    // if (tender == tenderTypes.ttCalc) {
+                    // if (tenderType == tenderTypes.ttCalc) {
                         let tExpr = analyzer.getCompiledTenderExpr(calcItem.compiledExpr);
                         calcItem.tenderUnitFee = (eval(tExpr) * feeRate * 0.01).toDecimal(decimalObj.decimal('unitPrice', treeNode));
                         calcItem.tenderTotalFee = (calcItem.tenderUnitFee * treeNode.data.tenderQuantity).toDecimal(decimalObj.decimal('totalPrice', treeNode));
@@ -1979,8 +2040,8 @@ class CalcProgram {
                     };
                 };
 
-                if (tender == tenderTypes.ttReverseRation || tender == tenderTypes.ttReverseGLJ)
-                    this.calcTenderReverse(treeNode, tender);
+                if (tenderType == tenderTypes.ttReverseRation || tenderType == tenderTypes.ttReverseGLJ)
+                    this.calcTenderReverse(treeNode, tenderType);
 
                 deleteUselessFees(treeNode, fnArr);
             };
@@ -2367,7 +2428,7 @@ class CalcProgram {
     distributeTargetTotalFee(treeNode){
         if (!treeNode) return;
 
-        if (treeNode.data.feesIndex['common']){     // 空清单忽略
+        if (treeNode.data.feesIndex && treeNode.data.feesIndex['common']){     // 空清单忽略
             // 默认能够执行到这里时每个节点已经被初始化,缓存已删除
             treeNode.data.tender_activeTotal = treeNode.data.feesIndex['common'].totalFee;
 

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

@@ -371,15 +371,15 @@ const XML_EXPORT_BASE = (() => {
      * @return {String}
      * @example getValueByKey(source.basicInformation, 'projectScale')
      * */
-    function getValueByKey(data, key) {
-        for (let d of data) {
-            if (d.key === key) {
-                return d.value;
+    function getValueByKey(items, key) {
+        for (const item of items) {
+            if (item.key === key) {
+                return item.value;
             }
-            if (d.items && d.items.length > 0) {
-                let findData = d.items.find(x => x.key === key);
-                if (findData) {
-                    return findData.value;
+            if (item.items && item.items.length) {
+                const value = getValueByKey(item.items, key);
+                if (value) {
+                    return value;
                 }
             }
         }
@@ -525,7 +525,7 @@ const XML_EXPORT_BASE = (() => {
         let projectData = _cache.projectData;
         // 没有数据,需要拉取
         if (!Object.keys(projectData).length) {
-            projectData = await ajaxPost('/pm/api/getProjectByGranularity', { user_id: userID, tenderID, granularity, summaryObj});
+            projectData = await ajaxPost('/pm/api/getProjectByGranularity', { user_id: userID, tenderID, granularity, summaryObj });
             _cache.projectData = projectData;
         }
         return projectData;
@@ -776,6 +776,52 @@ const XML_EXPORT_BASE = (() => {
     }
 
     /**
+     * 项目属性基本信息、工程特征的自检
+     * 有错误的话,将先直接弹窗提示,不会进行其他的自检
+     *   */
+    function propertyCheck() {
+        const type = {
+            info: '基本信息',
+            feature: '工程特征'
+        };
+        const failList = ['<span style="font-weight: bold">建设项目下:</span>'];
+        let haveHandledProjectData = false;
+        const tenderDetailMap = _cache.tenderDetailMap;
+        // 按照获取顺序serialNo(根据树结构)排序
+        Object.values(tenderDetailMap)
+            .sort((a, b) => a.serialNo - b.serialNo)
+            .forEach(tenderDetail => {
+                if (!haveHandledProjectData) {
+                    haveHandledProjectData = true;
+                    const basicInformation = tenderDetail.projectInfo.property.basicInformation || [];
+                    const infoFailList = itemsCheck(basicInformation, type.info);
+                    if (!infoFailList.length) {
+                        failList.splice(0, failList.length);
+                    } else {
+                        failList.push(...infoFailList);
+                    }
+                }
+                failList.push(`<span style="font-weight: bold">单位工程“${tenderDetail.projectInfo.name}”下:</span>`);
+                const projectFeature = tenderDetail.projectInfo.property.projectFeature || [];
+                const featureFailList = itemsCheck(projectFeature, type.feature);
+                if (!featureFailList.length) {
+                    failList.splice(-1, 1);
+                } else {
+                    failList.push(...featureFailList);
+                }
+            });
+        return failList;
+
+        // 条目检查
+        function itemsCheck(items, type) {
+            const requiredData = commonUtil.getRequired([], items);
+            return requiredData
+                .filter(item => commonUtil.isEmptyVal(item.value))
+                .map(item => `${type}-“${item.dispName}”不能为空。`);
+        }
+    }
+
+    /**
      * 弱自检自检,有错误非强制不可导的自检
      * failList里存的是导出规定属性时,不符合标准规定的错误,存在这种错误就不允许导出
      * 这里的错误是业务上的提示错误,与标准文件规定无关,存在这种错误是允许导出的
@@ -786,12 +832,12 @@ const XML_EXPORT_BASE = (() => {
         // 检查清单综合单价是否大于最高限价,大于则提示
         function checkMaxPrice(tenderDetail) {
             return tenderDetail.mainTree.items
-                    .filter(node => calcTools.unitFeeGTMaxPrice(node, 'common.unitFee'))
-                    .map(node => {
-                        const code = node.data.code || '';
-                        const name = node.data.name || '';
-                        return `第${node.serialNo()}行“${code + name}”,清单综合单价 > 最高限价`;
-                    });
+                .filter(node => calcTools.unitFeeGTMaxPrice(node, 'common.unitFee'))
+                .map(node => {
+                    const code = node.data.code || '';
+                    const name = node.data.name || '';
+                    return `第${node.serialNo()}行“${code + name}”,清单综合单价 > 最高限价`;
+                });
         }
         const infos = [];
         // 按照获取顺序serialNo(根据树结构)排序
@@ -807,7 +853,7 @@ const XML_EXPORT_BASE = (() => {
             });
         return infos;
     }
-    
+
     const UTIL = Object.freeze({
         deWeightHints,
         isDef,
@@ -836,6 +882,7 @@ const XML_EXPORT_BASE = (() => {
         setAttr,
         getParsedData,
         setupCode,
+        propertyCheck,
         softCheck
     });
 
@@ -945,7 +992,7 @@ const XML_EXPORT_BASE = (() => {
     async function getExtractData() {
         const fileKind = projectObj.project.projectInfo.property.fileKind;
         const projectID = projectObj.project.ID();
-        const extractData = await extractExportData(XMLStandard.entry, GRANULARITY.PROJECT, 
+        const extractData = await extractExportData(XMLStandard.entry, GRANULARITY.PROJECT,
             XMLStandard.summaryObj, fileKind, projectID, userID);
         // projects表数据
         const projectData = _.cloneDeep(_cache.projectData);

+ 5 - 3
web/building_saas/main/js/views/calc_program_view.js

@@ -16,13 +16,15 @@ let calcProgramObj = {
             {headerName: "费率", headerWidth: CP_Col_Width.feeRate, dataCode: "feeRate", dataType: "Number"},
             {headerName: "单价", headerWidth: CP_Col_Width.unitFee, dataCode: "unitFee", dataType: "Number"},
             {headerName: "合价", headerWidth: CP_Col_Width.totalFee, dataCode: "totalFee", dataType: "Number"},
+            {headerName: "调后单价", headerWidth: CP_Col_Width.unitFee, dataCode: "tenderUnitFee", dataType: "Number"},
+            {headerName: "调后合价", headerWidth: CP_Col_Width.totalFee, dataCode: "tenderTotalFee", dataType: "Number"},
             {headerName: "费用类别", headerWidth:CP_Col_Width.displayFieldName, dataCode:"displayFieldName", dataType: "String", hAlign: "center"},
             {headerName: "基数说明", headerWidth: CP_Col_Width.statement, dataCode: "statement", dataType: "String"},
             {headerName: "备注", headerWidth: CP_Col_Width.memo, dataCode: "memo", dataType: "String"}
         ],
         view: {
             comboBox: [],
-            lockColumns: [0,1,2,3,4,5,6,7,8,9],
+            lockColumns: [0,1,2,3,4,5,6,7,8,9,10],
             colHeaderHeight: CP_Col_Width.colHeader,
             rowHeaderWidth: CP_Col_Width.rowHeader
         }
@@ -33,8 +35,8 @@ let calcProgramObj = {
         me.sheet = sheet;
         for (let col of me.setting.header){
             if (col.headerName == '费率') col.tofix = decimalObj.feeRate;
-            if (col.headerName == '单价') col.tofix = decimalObj.ration.unitPrice;
-            if (col.headerName == '合价') col.tofix = decimalObj.ration.totalPrice;
+            if (col.headerName == '单价' || col.headerName == '调后单价') col.tofix = decimalObj.ration.unitPrice;
+            if (col.headerName == '合价' || col.headerName == '调后合价') col.tofix = decimalObj.ration.totalPrice;
         };
         sheetCommonObj.initSheet(me.sheet, me.setting, 1);
     },

+ 12 - 0
web/building_saas/main/js/views/export_view.js

@@ -124,6 +124,12 @@ const ExportView = (() => {
                         _exportCache.push(...exportData);
                     }
                 }
+                // 基本信息、工程特征必填项自检,这里的必填项不由xsd文件规定,而是后台根据业务需要配置
+                const requiredFails = _util.propertyCheck();
+                if (requiredFails.length) {
+                    setHeightByInfo(requiredFails);
+                    throw requiredFails.join('<br/>');
+                }
                 failList = _util.deWeightHints(failList);
                 if (failList.length) {
                     //设置提示弹窗
@@ -172,6 +178,12 @@ const ExportView = (() => {
                     }
                     pr.end();
                 }
+                // 基本信息、工程特征必填项自检,这里的必填项不由xsd文件规定,而是后台根据业务需要配置
+                const requiredFails = _util.propertyCheck();
+                if (requiredFails.length) {
+                    setHeightByInfo(requiredFails);
+                    throw requiredFails.join('<br/>');
+                }
                 failList = _util.deWeightHints(failList);
                 if (failList.length) {
                     //错误-设置提示弹窗

+ 10 - 2
web/building_saas/main/js/views/glj_col.js

@@ -18,10 +18,10 @@ let gljCol = {
             {headerName: "总消耗量", headerWidth: 80, dataCode: "totalQuantity", dataType: "Number", hAlign: "right", decimalField: "glj.quantity"},
             {headerName: "暂估", headerWidth: 45, dataCode: "isEstimate", dataType: "String", hAlign: "center", vAlign: "center", cellType: "checkBox"},
             {headerName: "调后市场价", headerWidth: 80, dataCode: "tenderPrice", dataType: "Number", hAlign: "right", visible: false},
-            {headerName: "调后消耗量", headerWidth: 80, dataCode: "tenderQuantity", dataType: "Number", hAlign: "right", visible: false}
+            {headerName: "调后消耗量", headerWidth: 80, dataCode: "tenderQuantity", dataType: "Number", hAlign: "right", decimalField: "glj.quantity", visible: false}
         ],
         view: {
-            lockColumns: [ "adjustPrice", "rationItemQuantity", "quantity", "totalQuantity", "isEstimate",
+            lockColumns: [ "adjustPrice", "quantity", "totalQuantity", "isEstimate",
                 "tenderPrice", "tenderQuantity"],//这里以后改成dataCode好一点
             rowHeaderWidth:25
         },
@@ -279,6 +279,13 @@ let gljCol = {
             }
         };
 
+        let CalcProgramHeader = calcProgramObj.setting.header;
+        for (let e of CalcProgramHeader){
+            if (e.dataCode == 'tenderUnitFee' || e.dataCode == 'tenderTotalFee'){
+                e.visible = showFields;
+            }
+        };
+
         if (needRefresh){
             if(projectGljObject.projectGljSpread) {
                 projectGljObject.projectGljSheet = projectGljObject.projectGljSpread .getSheet(0);
@@ -286,6 +293,7 @@ let gljCol = {
             }
             if (subSpread) {
                 gljOprObj.initSheet(subSpread.getSheet(0), false);
+                gljOprObj.initSheet(subSpread.getSheet(2), false);
             }
         };
     },

+ 107 - 8
web/building_saas/main/js/views/glj_view.js

@@ -137,10 +137,8 @@ var gljOprObj = {
     onRationGLJSelectionChange:function(sender,args){
         let me = gljOprObj;
         let selected = args.newSelections[0] ? args.newSelections[0] : {row: 0, col: 0};
-        console.log("selected changed-------");
         //这主要记录是否点击了sheet以外的地方,如果点击了sheet里的单元格,则将cancelUpdate设置为true不触发提交更新操作
         me.cancelUpdate = true;//取消延时任务由这里进行判断处理
-        console.log("change to true");
         me.sheetInitSelection(selected);
         if(me.rationGljEditObj){
             if(ifNeedUpdate(selected)){
@@ -454,9 +452,10 @@ var gljOprObj = {
         if(me.sheet.getTag(args.row,args.col)=="locked") return false;//如果是双击树节点编号里设置了锁定标记,不能编辑
         if (_.includes(me.setting.view.lockColumns, args.col))  return false;//如果是锁定的列,不能编辑
         if(recode != undefined){
-            if(recode.isMixRatio){//对于组成物列
-               return dataCode == 'marketPrice'//允许修改组成物市单价,其它的不可以
+            if(recode.isMixRatio){//对于组成物列 - 可修改市场价和
+              return dataCode == 'marketPrice' || dataCode == 'rationItemQuantity'
             }else {
+                if(dataCode == 'rationItemQuantity') return false;
                 if (dataCode && dataCode == 'marketPrice') {
                     return !me.marketPriceReadOnly({data:me.sheetData[args.row]});
                 }
@@ -775,7 +774,7 @@ var gljOprObj = {
                     ration_gljs[i]=this.setGLJPrice(ration_gljs[i],glj);//设置工料机价格
                     let connect_index = this.getIndex(glj, gljKeyArray);
                     if (needRatio==true&&mixRatioMap.hasOwnProperty(connect_index)) {
-                        let mixRatios = this.getMixRationShowDatas(mixRatioMap[connect_index], projectGljs);
+                        let mixRatios = this.getMixRationShowDatas(mixRatioMap[connect_index], projectGljs,glj);
                         ration_gljs[i].subList = mixRatios;
                     }
                     if(ration) gljOprObj.getTotalQuantity(ration_gljs[i], ration);
@@ -843,7 +842,7 @@ var gljOprObj = {
         }
         return obj;
     },
-    getMixRationShowDatas: function (mixRatioList, projectGljs) {
+    getMixRationShowDatas: function (mixRatioList, projectGljs,parentGLJ) {
         var temRationGLJs = [];
         for (var i = 0; i < mixRatioList.length; i++) {
             let mIndex = gljOprObj.getIndex(mixRatioList[i],gljKeyArray);
@@ -852,6 +851,7 @@ var gljOprObj = {
             });//改关联关系
             if(pg){
                 let tem = {
+                    mixRatioId:mixRatioList[i].id,
                     projectGLJID: pg.id,
                     code: pg.code,
                     name: pg.name,
@@ -868,7 +868,8 @@ var gljOprObj = {
                     //isEstimate: pg.is_evaluate,
                     isMixRatio: true,
                     isAdd: pg.unit_price.is_add,
-                    GLJID: pg.glj_id
+                    GLJID: pg.glj_id,
+                    parentGLJ:parentGLJ
                 };
                 if(projectObj.project.projectGLJ.isEstimateType(pg.type)){
                     tem.isEstimate =  pg.is_evaluate;
@@ -924,7 +925,7 @@ var gljOprObj = {
             if (args.editingText == null) {
                 updateField == 'marketPrice' ? newval = 0 : newval = "";
             } else {
-                var decimal = updateField == 'customQuantity' ? getDecimal("glj.quantity") : 6;//对于市场价和定额价,这里只是中间的小数位数,后面更新前会根据有没有组成物再取值
+                var decimal = updateField == 'customQuantity'||updateField == 'rationItemQuantity'  ? getDecimal("glj.quantity") : 6;//对于市场价和定额价,这里只是中间的小数位数,后面更新前会根据有没有组成物再取值
                 newval = number_util.checkNumberValue(args.editingText, decimal);
                 if (newval == null) {
                     me.sheet.getCell(args.row, args.col).value(recode[updateField]);
@@ -956,6 +957,9 @@ var gljOprObj = {
             projectObj.project.ration_glj.updateRationGLJByChangeCode(recode, updateField, newval);
         }else if(me.setting.navigationRightCol.indexOf(updateField) != -1){//对于名称、规格、单位先跳到下一列,保存至缓存
             me.setToEditCache(recode,updateField, newval,args);
+        }else if(updateField == 'rationItemQuantity'){//修改组成物的消耗量
+            me.updateRatio(recode,{consumption:newval})
+            console.log(recode);
         } else {
             projectObj.project.ration_glj.updateRationGLJByEdit(recode, updateField, newval);
         }
@@ -986,6 +990,100 @@ var gljOprObj = {
         }
        return false;
     },
+    getParentPrice:function(subList,value,radio,type = 'modify'){
+      let marketPrice = 0,basePrice=0; 
+      let decimal = getDecimal('glj.unitPrice');
+      for(let s of subList){
+        let c = parseFloat(s.consumption);
+        if(s.mixRatioId == radio.mixRatioId) c = value;
+        if(type == 'delete' && s.mixRatioId == radio.mixRatioId) continue;
+        let m = scMathUtil.roundForObj(s.marketPrice *  c,decimal);
+        let b = scMathUtil.roundForObj(s.basePrice *  c,decimal);
+        marketPrice += m;
+        basePrice +=b;
+      }
+      return [scMathUtil.roundForObj(marketPrice,decimal),scMathUtil.roundForObj(basePrice,decimal)]
+    },
+    deleteRatio:async function(row){
+      let ratio = this.sheetData[row];
+      let pgljData = projectObj.project.projectGLJ.datas;
+      for(let rg of  this.sheetData){
+        if(rg.projectGLJID == ratio.parentGLJ.id) {
+          subList = rg.subList;
+          break;
+        }
+      }  
+      let [marketPrice,basePrice] = this.getParentPrice(subList,0,ratio,"delete");
+      try {
+        $.bootstrapLoading.start()
+        projectData = await ajaxPost('/glj/updateRatio', {id: ratio.mixRatioId,type:'delete',pid:ratio.parentGLJ.id,market_price: marketPrice,base_price: basePrice});
+
+        //更新缓存
+        let pk = gljUtil.getIndex(ratio.parentGLJ);
+        if(pgljData.mixRatioMap && pgljData.mixRatioMap[pk]){
+          _.remove(pgljData.mixRatioMap[pk],{"id":ratio.mixRatioId});
+        }
+        for(let r of ratio.parentGLJ.ratio_data){
+          _.remove(ratio.parentGLJ.ratio_data,{"id":ratio.mixRatioId});
+        }
+        ratio.parentGLJ.unit_price.market_price = marketPrice;
+        ratio.parentGLJ.unit_price.base_price = basePrice;
+        //重新计算消耗量
+        projectObj.project.projectGLJ.calcQuantity();
+        let priceData = {};
+        priceData =this.setGLJPrice(priceData,ratio.parentGLJ);
+        projectGljObject.updateParentNodes(ratio.parentGLJ.id,priceData.marketPrice);
+        projectGljObject.onUnitFileChange(ratio);
+      } catch (error) {
+        console.log(error)
+      }finally{
+        $.bootstrapLoading.end()
+      }
+    },
+    updateRatio:async function(ratio,doc){
+      let pgljData = projectObj.project.projectGLJ.datas;
+      let subList = [];
+      for(let rg of  this.sheetData){
+        if(rg.projectGLJID == ratio.parentGLJ.id) {
+          subList = rg.subList;
+          break;
+        }
+      }  
+      let [marketPrice,basePrice] = this.getParentPrice(subList,doc.consumption,ratio);
+
+      try {
+        $.bootstrapLoading.start()
+        projectData = await ajaxPost('/glj/updateRatio', {id: ratio.mixRatioId, doc:doc,pid:ratio.parentGLJ.id,market_price: marketPrice,base_price: basePrice});
+
+        //更新缓存
+        let pk = gljUtil.getIndex(ratio.parentGLJ);
+        if(pgljData.mixRatioMap && pgljData.mixRatioMap[pk]){
+          for(let m of pgljData.mixRatioMap[pk]){
+            if(m.id == ratio.mixRatioId) gljUtil.setProperty(m,doc);
+          } 
+        }
+        for(let r of ratio.parentGLJ.ratio_data){
+          if(r.id == ratio.mixRatioId) gljUtil.setProperty(r,doc);
+        }
+        ratio.parentGLJ.unit_price.market_price = marketPrice;
+        ratio.parentGLJ.unit_price.base_price = basePrice;
+      
+
+        //重新计算消耗量
+        projectObj.project.projectGLJ.calcQuantity();
+        let priceData = {};
+        priceData =this.setGLJPrice(priceData,ratio.parentGLJ);
+        projectGljObject.updateParentNodes(ratio.parentGLJ.id,priceData.marketPrice);
+        projectGljObject.onUnitFileChange(ratio);
+      } catch (error) {
+        console.log(error)
+      }finally{
+        $.bootstrapLoading.end()
+      }
+      
+    },
+
+
     updateRationTypeGLJ: function (value, node, fieldName,editingText) {
         let newval;
         let updatePrice = false;
@@ -1637,6 +1735,7 @@ $(function () {
     $('#glj_tree_div').on('hidden.bs.modal', function () {
         // 清空搜索框
         $('#gljSearchKeyword').val('');
+        projectGljObject.subList = [];
     });
     $('#glj_tree_div').on('shown.bs.modal', function (e) {
         if (gljOprObj.gljLibSpresd == undefined) {

+ 59 - 1
web/building_saas/main/js/views/glj_view_contextMenu.js

@@ -114,7 +114,65 @@ var gljContextMenu = {
                         return subSpread.getActiveSheet().name()=='ration_glj';
                     }
                 },
-                "add_to_lib": {
+                "addMixRatio": {
+                  name: '添加组成物',
+                  icon: 'fa-sign-in',
+                  disabled: function () {
+                      let sheetData = gljOprObj.sheetData;
+                      const selected = projectObj.project.mainTree.selected;
+                      if(gljOprObj.isInstallationNode(selected) || OVER_HEIGHT.isOverHeight(selected)){
+                          return true;
+                      }
+                      if(subSpread.getActiveSheetIndex()==0&&sheetData!=null&&sheetData.length>0&&gljContextMenu.selectedRow<sheetData.length){
+                          if(sheetData[gljContextMenu.selectedRow].isMixRatio == true) return false;
+                          if(!sheetData[gljContextMenu.selectedRow].subList)  return true;
+                          return false;
+                      }
+                      return true;
+                  },
+                  callback: function () {
+                      let t = gljOprObj.sheetData[gljContextMenu.selectedRow];
+                      if(t.isMixRatio){
+                        projectGljObject.selectedProjectGLJ = t.parentGLJ;
+                        for(let rg of  gljOprObj.sheetData){
+                          if(rg.projectGLJID == t.parentGLJ.id) {
+                            projectGljObject.subList = rg.subList;
+                            break;
+                          }
+                        } 
+
+                      }else{
+                        projectGljObject.selectedProjectGLJ = t;
+                        projectGljObject.subList = t.subList;
+                      }
+                      getGLJData('addMix');
+                  },
+                  visible: function(key, opt){
+                      return subSpread.getActiveSheet().name()=='ration_glj';
+                  }
+                },
+                "deleteMixRatio": {
+                  name: '删除组成物',
+                  icon: 'fa-sign-in',
+                  disabled: function () {
+                      let sheetData = gljOprObj.sheetData;
+                      const selected = projectObj.project.mainTree.selected;
+                      if(gljOprObj.isInstallationNode(selected) || OVER_HEIGHT.isOverHeight(selected)){
+                          return true;
+                      }
+                      if(subSpread.getActiveSheetIndex()==0&&sheetData!=null&&sheetData.length>0&&gljContextMenu.selectedRow<sheetData.length){
+                          if(sheetData[gljContextMenu.selectedRow].isMixRatio == true) return false;
+                      }
+                      return true;
+                  },
+                  callback: function () {
+                     gljOprObj.deleteRatio(gljContextMenu.selectedRow);
+                  },
+                  visible: function(key, opt){
+                      return subSpread.getActiveSheet().name()=='ration_glj';
+                  }
+                },
+               "add_to_lib": {
                     name: '保存到我的人材机库',
                     icon: 'fa-sign-in',
                     disabled: function () {

+ 42 - 33
web/building_saas/main/js/views/project_glj_view.js

@@ -41,6 +41,7 @@ let projectGljObject={
     mixRatioSpread:null,
     mixRatioSheet:null,
     mixRatioData:[],
+    subList:[],//定额工料机,添加组成物时临时保存组成物信息的位置
     usedTenderList:[],
     usedUnitPriceInfo:null,
     displayTypeMap:[
@@ -178,7 +179,9 @@ let projectGljObject={
     },
     addMixRatio:function () {
         let me = this, projectGLJ = projectObj.project.projectGLJ;
-        for(let mix of me.mixRatioData){
+        let tdatas = me.mixRatioData;
+        if(me.subList.length > 0) tdatas = me.subList;
+        for(let mix of tdatas){
             let m_key = gljOprObj.getIndex(mix, gljKeyArray);
             let t_index = gljOprObj.GLJSelection.indexOf(m_key);
             t_index != -1?gljOprObj.GLJSelection.splice(t_index,1):'';
@@ -194,6 +197,7 @@ let projectGljObject={
     },
     showMixRatioData:function () {
         let me = this,gljId = null,gljType = null;
+        if(!me.projectGljSpread) return;
         let sheet = me.projectGljSpread.getActiveSheet();
         let oldSel = me.mixRatioSheet.getSelections()[0];
         if(sheet.name() == 'projectGljSheet'){//projectGljSheet/materialSheet 工料机汇总和三材汇总表
@@ -510,6 +514,7 @@ let projectGljObject={
 
     },
     showProjectGljData:function () {
+        if(!this.projectGljSpread) return;
         this.projectGljSpread.setActiveSheetIndex(0);
         let sel = this.projectGljSheet.getSelections()[0];
         let oldData = sel.row<this.projectGljSheetData.length?this.projectGljSheetData[sel.row]:"";
@@ -593,7 +598,7 @@ let projectGljObject={
             me.showMaterialTreeData();
         }else {
             me.showProjectGljData();
-            me.showMixRatioData();
+            me.showMixRatioData(); 
         }
     },
     createMaterialTree:function (gljList) {
@@ -847,38 +852,42 @@ let projectGljObject={
 
         // 更新组成物缓存
         projectObj.project.composition.loadData();
-        //先查找使用了父项目工料机的定额工料机
-        let updateNodes=[];
-        let ration_gljs = _.filter(projectObj.project.ration_glj.datas,{'projectGLJID':pid});
-        for(let rg of ration_gljs){
-            let node = projectObj.project.mainTree.getNodeByID(rg.rationID);
-            if(node){
-                updateNodes.push(node);
-            }
-        }
-        //或者是使用了父项目工料机的工料机类型的定额
-        let rations = _.filter(projectObj.project.Ration.datas,{'type':3,'projectGLJID':pid});
-        for(let r of rations){
-            let r_node = projectObj.project.mainTree.getNodeByID(r.ID);
-            if(r_node){
-                r_node.data.marketUnitFee = parantData?parantData.marketPrice:'';//parentMarketPrice;//这里用显示的价格
-                updateNodes.push(r_node);
-            }
-        }
-        if(sid){
-            let subRations = calcTools.getRationsByProjectGLJ(sid);
-            updateNodes = updateNodes.concat(subRations);
-        }
-        if(updateNodes.length>0){
-            projectObj.project.calcProgram.calcNodesAndSave(updateNodes,async function () {
-                projectObj.mainController.refreshTreeNode(projectObj.project.mainTree.roots);
-                installationFeeObj.calcInstallationFee();//计算安装增加费
-               await OVER_HEIGHT.reCalcOverHeightFee();
-                await itemIncreaseFeeObj.calcItemIncreaseFeeByNodes(updateNodes);
-            });
-        }
-        gljOprObj.refreshView();
+        me.updateParentNodes(pid,parantData.marketPrice,sid);
+    },
+    updateParentNodes:function(pid,marketPrice,sid){
+      //先查找使用了父项目工料机的定额工料机
+      let updateNodes=[];
+      let ration_gljs = _.filter(projectObj.project.ration_glj.datas,{'projectGLJID':pid});
+      for(let rg of ration_gljs){
+          let node = projectObj.project.mainTree.getNodeByID(rg.rationID);
+          if(node){
+              updateNodes.push(node);
+          }
+      }
+      //或者是使用了父项目工料机的工料机类型的定额
+      let rations = _.filter(projectObj.project.Ration.datas,{'type':3,'projectGLJID':pid});
+      for(let r of rations){
+          let r_node = projectObj.project.mainTree.getNodeByID(r.ID);
+          if(r_node){
+              r_node.data.marketUnitFee = marketPrice;//parentMarketPrice;//这里用显示的价格
+              updateNodes.push(r_node);
+          }
+      }
+      if(sid){
+          let subRations = calcTools.getRationsByProjectGLJ(sid);
+          updateNodes = updateNodes.concat(subRations);
+      }
+      if(updateNodes.length>0){
+          projectObj.project.calcProgram.calcNodesAndSave(updateNodes,async function () {
+              projectObj.mainController.refreshTreeNode(projectObj.project.mainTree.roots);
+              installationFeeObj.calcInstallationFee();//计算安装增加费
+              await OVER_HEIGHT.reCalcOverHeightFee();
+              await itemIncreaseFeeObj.calcItemIncreaseFeeByNodes(updateNodes);
+          });
+      }
+      gljOprObj.refreshView();
     },
+
     onUnitFileChange:function (data) {
         projectObj.project.markUpdateProject({projectID:projectObj.project.ID(),'unitFileID':socketObject.getUnitFileRoomID()},"unitFile",function(){
             //socket.emit('unitFileChangeNotify', JSON.stringify(data));

+ 2 - 0
web/building_saas/main/js/views/project_info.js

@@ -46,6 +46,8 @@ var projectInfoObj = {
             billsQuanDecimal.datas = data.property.billsQuantityDecimal || [billsDecimalView.angleDecimal];
             basicInfoView.orgDatas = data.property.basicInformation ? basicInfoView.toViewDatas(data.property.basicInformation) : [];
             projFeatureView.orgDatas = data.property.projectFeature ? projFeatureView.toViewDatas(data.property.projectFeature) : [];
+            console.log(`me.orgDatas`);
+            console.log(projFeatureView.orgDatas);
             $('#fullpath').html(this.getFullPathHtml(data));
             $("[data-toggle='tooltip']").tooltip();
         }

+ 55 - 35
web/building_saas/main/js/views/project_property_basicInfo.js

@@ -243,44 +243,47 @@ let basicInfoView = {
     },
 
     initDatas: function (datas) {
-        this.datas = [];
-        for(let i = 0, len = datas.length; i < len; i++){
-            this.datas.push(this.copyObj(datas[i]));
-        }
+        this.datas = _.cloneDeep(datas);
     },
 
     //数据库读到的数据转换为展示的结构
     toViewDatas: function (datas) {
-        let rst = [];
-        for(let i = 0, len = datas.length; i < len; i++){
-            let items = datas[i].items || null;
-            if(items){
-                rst.push(datas[i]);
-                for(let j = 0, jLen = items.length; j < jLen; j++){
-                    rst.push(items[j]);
+        // 将数据打平(原数据一些子项数据会嵌套在items数组中)
+        let curID = 1;
+        return extractDataFromItems(datas);
+        // 为了与sheet_common.js -> getTreeNodeCellType共用树结构功能
+        // 需要设置数据的ID、ParentID
+
+        function extractDataFromItems(items, parentID) {
+            const rst = [];
+            items.forEach(item => {
+                // 为了与sheet_common.js -> getTreeNodeCellType共用树结构功能
+                // 需要设置数据的ID、ParentID
+                item.ID = curID++;
+                item.ParentID = parentID || null;
+                rst.push(item);
+                if (item.items && item.items.length) {
+                    rst.push(...extractDataFromItems(item.items, item.ID));
                 }
-            }
+            });
+            return rst;
         }
-        return rst;
     },
 
     //展示的结构转换为入库的结构
     toSaveDatas: function (datas) {
-        let rst = [];
-        let index = -1;
-        for(let i = 0, len = datas.length; i < len; i++){
-            let items = datas[i].items || null;
-            if(items){
-                delete datas[i].collapsed;
-                datas[i].items = [];
-                rst.push(datas[i]);
-                index++;
-            }
-            else {
-                rst[index]['items'].push(datas[i]);
-            }
+        const saveData = datas.filter(item => item.ParentID === null);
+        clearJunkAttrs(saveData);
+
+        function clearJunkAttrs(items) {
+            items.forEach(item => {
+                delete item.ID && delete item.ParentID && delete item.collapsed;
+                if (item.items && item.items.length) {
+                    clearJunkAttrs(item.items);
+                }
+            })
         }
-        return rst;
+        return saveData;
     },
 
     toUpdate: function (orgDatas, newDatas) {
@@ -312,19 +315,36 @@ let basicInfoView = {
         });
     },
 
-    initTree:function (sheet, init, datas) {
-        sheet.getRange(-1, 0, -1, 1).cellType(this.getTreeNodeCellType(datas));
-        for(let i =0, len = datas.length; i < len; i++){
-            if(datas[i].hasOwnProperty('items')){
+    initTree: function (sheet, init, datas) {
+        //sheet.getRange(-1, 0, -1, 1).cellType(this.getTreeNodeCellType(datas));
+        const parentType = _.groupBy(datas, 'ParentID');
+        const paint = (ctx, value, x, y, w, h, style, rectInfo, data) => {
+            const required = typeof data.required === 'string' ? JSON.parse(data.required) : data.required;
+            const readOnly = typeof data.readOnly === 'string' ? JSON.parse(data.readOnly) : data.readOnly;
+            if (required && !readOnly) {
+                const { rectW, margin } = rectInfo;
+                ctx.save();
+                ctx.fillStyle = 'red';
+                const paddingLeft = 8;
+                const paintX = x + margin + paddingLeft; // 第一层盒子处[+] / [-]
+                const paintY = y + Math.round(h / 2) + paddingLeft;
+                ctx.font = "14px Calibri"
+                ctx.fillText('*', paintX, paintY);
+                ctx.restore();
+            }
+            
+        };
+        for (let i = 0, len = datas.length; i < len; i++) {
+            if (datas[i].hasOwnProperty('items')) {
                 let collapsed = false;
-                if(init){
-                    datas[i].collapsed=false;
+                if (init) {
+                    datas[i].collapsed = false;
                     collapsed = true;
-                }else {
+                } else {
                     collapsed = datas[i].collapsed == undefined ? true : datas[i].collapsed;
                 }
-                //sheet.getRange(i+1, -1, datas[i].items.length, -1).visible(!collapsed);
             }
+            sheet.getCell(i, 0).cellType(sheetCommonObj.getTreeNodeCellType(datas, i, parentType, paint))
         }
     },
 

+ 28 - 481
web/building_saas/main/js/views/project_property_projFeature.js

@@ -302,8 +302,17 @@ let projFeatureView = {
         // ParentID为null的数据是为显示而加入的“工程特征” ParentID为1的数据即为工程特征下的第一层数据,其他数据是从ParentID为1的数据中的items递归打平出来的
         // 因此原数据格式就是ParentID为1的数据
         const saveData = datas.filter(item => item.ParentID === 1);
-        saveData.forEach(item => delete item.ID && delete item.ParentID && delete item.collapsed);
+
+        clearJunkAttrs(saveData);
         return saveData;
+        function clearJunkAttrs(items) {
+            items.forEach(item => {
+                delete item.ID && delete item.ParentID && delete item.collapsed;
+                if (item.items && item.items.length) {
+                    clearJunkAttrs(item.items);
+                }
+            })
+        }
     },
 
     toUpdate: function (orgDatas, newDatas) {
@@ -339,6 +348,22 @@ let projFeatureView = {
     initTree: function (sheet, init, datas) {
         //sheet.getRange(-1, 0, -1, 1).cellType(this.getTreeNodeCellType(datas));
         const parentType = _.groupBy(datas, 'ParentID');
+        const paint = (ctx, value, x, y, w, h, style, rectInfo, data) => {
+            const required = typeof data.required === 'string' ? JSON.parse(data.required) : data.required;
+            const readOnly = typeof data.readOnly === 'string' ? JSON.parse(data.readOnly) : data.readOnly;
+            if (required && !readOnly) {
+                const { rectW, margin } = rectInfo;
+                ctx.save();
+                ctx.fillStyle = 'red';
+                const paddingLeft = 8;
+                const paintX = x + margin + rectW + paddingLeft; // 第一层盒子处[+] / [-]
+                const paintY = y + Math.round(h / 2) + paddingLeft;
+                ctx.font = "14px Calibri"
+                ctx.fillText('*', paintX, paintY);
+                ctx.restore();
+            }
+            
+        };
         for (let i = 0, len = datas.length; i < len; i++) {
             if (datas[i].hasOwnProperty('items')) {
                 let collapsed = false;
@@ -349,7 +374,7 @@ let projFeatureView = {
                     collapsed = datas[i].collapsed == undefined ? true : datas[i].collapsed;
                 }
             }
-            sheet.getCell(i, 0).cellType(sheetCommonObj.getTreeNodeCellType(datas, i, parentType))
+            sheet.getCell(i, 0).cellType(sheetCommonObj.getTreeNodeCellType(datas, i, parentType, paint))
         }
     },
 
@@ -497,482 +522,4 @@ $(document).ready(function () {
     $('#tab_poj-settings-projFeature').on('shown.bs.tab', function () {
         sheetCommonObj.refreshWorkbookDelDefer(projFeatureView.workBook, 100);
     });
-});
-/* let projFeatureView = {
-    orgDatas: [],//for compare
-    datas: [],//just for view
-    workBook: null,
-    firstData: {dispName: '工程特征', key: 'projFeature', items: []},//for show
-    setting:{
-        header: [
-            {name: '属性', dataCode: 'dispName', width: 200, vAlign: 'center', hAlign: 'left'},
-            {name: '值', dataCode: 'value', width: 300, vAlign: 'center', hAlign: 'left'}
-        ],
-        options: {
-            allowContextMenu: false,
-            tabStripVisible:  false,
-            allowCopyPasteExcelStyle : false,
-            allowExtendPasteRange: false,
-            allowUserDragDrop : false,
-            allowUserDragFill: false,
-            scrollbarMaxAlign : true
-        },
-        numRows: [],
-        dateRows: [],
-        locked: {
-            rows: [],
-            cols: [0]
-        }
-    },
-
-    renderSheetFuc: function (sheet, fuc) {
-        sheet.suspendPaint();
-        sheet.suspendEvent();
-        fuc();
-        sheet.resumePaint();
-        sheet.resumeEvent();
-    },
-
-    setOptions: function (workbook, opts) {
-        for(let opt in opts){
-            workbook.options[opt] = opts[opt];
-        }
-    },
-
-    setCombo: function (sheet, row, items) {
-        let dynamicCombo = sheetCommonObj.getDynamicCombo();
-        dynamicCombo.items(items);
-        dynamicCombo.editable(false);
-        sheet.getCell(row, 1).cellType(dynamicCombo);
-    },
-
-    getComboItemsByRow: function (row) {
-        let data = this.datas[row];
-        if (!data) {
-            return null;
-        }
-        if (!data.cellType || data.cellType !== 'comboBox') {
-            return null;
-        }
-        return data.options ? data.options.split('@') : [];
-    },
-
-    buildHeader: function (sheet, headers) {
-        let me = projFeatureView;
-        let fuc = function () {
-            sheet.options.clipBoardOptions = GC.Spread.Sheets.ClipboardPasteOptions.values;
-            sheet.setColumnCount(headers.length);
-            sheet.setRowHeight(0, 40, GC.Spread.Sheets.SheetArea.colHeader);
-            for(let i = 0, len = headers.length; i < len; i++){
-                sheet.setValue(0, i, headers[i].name, GC.Spread.Sheets.SheetArea.colHeader);
-                sheet.setColumnWidth(i, headers[i].width, GC.Spread.Sheets.SheetArea.colHeader);
-                sheet.getRange(-1, i, -1, 1).hAlign(GC.Spread.Sheets.HorizontalAlign[headers[i]['hAlign']]);
-                sheet.getRange(-1, i, -1, 1).vAlign(GC.Spread.Sheets.VerticalAlign[headers[i]['vAlign']]);
-
-            }
-        };
-        me.renderSheetFuc(sheet, fuc);
-    },
-
-    buildSheet: function () {
-        if(!this.workBook){
-            this.workBook = new GC.Spread.Sheets.Workbook($('#projFeatureSpread')[0], {sheetCount: 1});
-            sheetCommonObj.spreadDefaultStyle(this.workBook);
-            sheetCommonObj.bindEscKey(this.workBook, [{sheet: this.workBook.getSheet(0), editStarting: this.onEditStarting, editEnded: this.onEditEnded}]);
-            this.setOptions(this.workBook, this.setting.options);
-            this.buildHeader(this.workBook.getActiveSheet(), this.setting.header);
-            this.bindEvent(this.workBook);
-        }
-    },
-
-    bindEvent: function (workBook) {
-        const _events = GC.Spread.Sheets.Events;
-        let sheet = workBook.getActiveSheet();
-        sheet.bind(_events.EditStarting, this.onEditStarting);
-        sheet.bind(_events.EditEnded, this.onEditEnded);
-        sheet.bind(_events.EnterCell, this.onEnterCell);
-        sheet.bind(_events.ClipboardPasting, this.onClipboardPasting);
-        sheet.bind(_events.ClipboardPasted, this.onClipboardPasted);
-    },
-
-    showData(datas){
-        let me = projFeatureView;
-        let sheet = this.workBook.getActiveSheet();
-        let cols = this.setting.header;
-        let fuc = function () {
-            sheet.setRowCount(datas.length);
-            me.initTree(sheet, true, datas);
-            sheet.setFormatter(-1, 1, '@');
-            //兼容旧数据
-            // org: compatLockedKeys = ['engineering']
-            let compatLockedKeys = [],
-                compatNumKeys = [
-                    'buildingArea',
-                    'basementBuildingArea',
-                    'totalFloors',
-                    'basementFloors',
-                    'buildingFloors',
-                    'buildingHeight',
-                    'basementHeight',
-                    'firstFloorHeight',
-                    'podiumBuildingHeight',
-                    'standardFloorHeight'
-                ];
-            for(let row = 0;row < datas.length ; row ++){
-                if(datas[row].cellType == 'comboBox'){
-                    let options = datas[row].options?datas[row].options.split("@"):[];
-                    me.setCombo(sheet, row, options);
-                } else if(datas[row].cellType == 'number' || compatNumKeys.includes(datas[row].key)){
-                    me.setting.numRows.push(row);
-                } else if (datas[row].cellType === 'date') {
-                    me.setting.dateRows.push(row);
-                }
-                let readOnly = typeof datas[row].readOnly === 'string' ? JSON.parse(datas[row].readOnly) : datas[row].readOnly;
-                if (readOnly || datas[row].items || compatLockedKeys.includes(datas[row].key)) {
-                    me.setting.locked.rows.push(row);
-                }
-                for(let col = 0;col < cols.length;col++){
-                    sheet.setValue(row, col, datas[row][cols[col]['dataCode']]);
-                }
-            }
-            basicInfoView.setDatePicker(sheet, me.setting.dateRows);
-        };
-        this.renderSheetFuc(sheet, fuc);
-    },
-
-    onEditStarting: function (sender, args) {
-        let me = projFeatureView;
-        if(me.setting.locked.cols.indexOf(args.col) !== -1){
-            args.cancel = true;
-        }
-        if(args.col === 1 && me.setting.locked.rows.indexOf(args.row) !== -1){
-            args.cancel = true;
-        }
-    },
-
-    onEditEnded: function (sender, args) {
-        let me = projFeatureView;
-        let v =  args.editingText ? args.editingText.toString().trim() : '';
-        if(args.row < me.datas.length){
-            let required = typeof me.datas[args.row].required === 'string' ? JSON.parse(me.datas[args.row].required) : me.datas[args.row].required;
-            if (required && !v) {
-                v = me.datas[args.row].value;
-                args.sheet.setValue(args.row, args.col, v);
-            } else if(me.setting.numRows.indexOf(args.row) !== -1){//控制数值
-                if(!me.isNum(v)){
-                    alert('只能输入数值');
-                    v = me.datas[args.row].value && me.isNum(me.datas[args.row].value) ? me.datas[args.row].value : '';
-                    args.sheet.setValue(args.row, args.col, v);
-                }
-            } else if(me.setting.dateRows.indexOf(args.row) !== -1){
-                if(v.length > 0){
-                    v = formatDate(new Date(v), 'yyyy-MM-dd');
-                    v = basicInfoView.filtDate(v);
-                    if(!v){
-                        alert('请输入正确的日期格式yyyy-mm-dd');
-                        args.sheet.setValue(args.row, args.col, me.datas[args.row].value ? me.datas[args.row].value : '');
-                        return;
-                    }
-                }
-            }
-            me.datas[args.row].value = v;
-        }
-    },
-
-    onEnterCell: function (sender, args) {
-        args.sheet.repaint();
-    },
-
-    onClipboardPasting: function (sender, args) {
-        let me = projFeatureView;
-        if(me.setting.locked.cols.indexOf(args.cellRange.col) !== -1){
-            args.cancel = true;
-        }
-    },
-
-    onClipboardPasted: function (sender, args) {
-        let me = projFeatureView;
-        let items = sheetCommonObj.analyzePasteData(me.setting, args);
-        let recRows = [];
-        if(items.length === 0){
-            return;
-        }
-        for(let i = 0, len = items.length; i < len; i++){
-            let row = i + args.cellRange.row;
-            let comboItems = me.getComboItemsByRow(row);
-            let required = typeof me.datas[row].required === 'string' ? JSON.parse(me.datas[row].required) : me.datas[row].required;
-            if(me.setting.locked.rows.indexOf(row) !== -1){
-                recRows.push(row);
-            }
-            else if (required && !items[i].value) {
-                recRows.push(row);
-            }
-            //粘贴下拉框数据过滤
-            else if(comboItems && !comboItems.includes(items[i].value)){
-                recRows.push(row);
-            }
-            else if(me.setting.numRows.indexOf(row) !== -1 && !me.isNum(items[i].value)){
-                recRows.push(row);
-            }
-            else if(me.setting.dateRows.indexOf(row) !== -1){
-                items[i].value = basicInfoView.filtDate(items[i].value);
-                if(!me.isDef(items[i].value)){
-                    recRows.push(row);
-                } else {
-                    me.datas[row].value = items[i].value;
-                }
-            }
-            else {
-                me.datas[row].value = items[i].value;
-            }
-        }
-        if(recRows.length > 0){
-            me.renderSheetFuc(args.sheet, function () {
-                for(let i = 0, len = recRows.length; i < len; i++){
-                    let staticV = me.datas[recRows[i]].value || '';
-                    args.sheet.setValue(recRows[i], args.cellRange.col, staticV);
-                }
-            })
-        }
-    },
-
-    getFeature: function (featureKey) {
-        let datas = this.datas;
-        if(datas.length === 0){
-            datas = projectObj.project.property.projectFeature;
-        }
-        for(let feature of datas){
-            if(feature.key === featureKey){
-                return feature.value;
-            }
-        }
-        return null;
-    },
-
-    isDef: function (v) {
-        return v !== undefined && v !== null;
-    },
-    isNum: function(v){
-        return this.isDef(v) && !isNaN(v);
-    },
-
-    copyObj: function(obj){
-        let newObj = {};
-        for(let attr in obj){
-            newObj[attr] = obj[attr];
-        }
-        return newObj;
-    },
-
-    initDatas: function (datas) {
-        this.datas = [];
-        for(let i = 0, len = datas.length; i < len; i++){
-            this.datas.push(this.copyObj(datas[i]));
-        }
-    },
-
-//数据库读到的数据转换为展示的结构
-    toViewDatas: function (datas) {
-        let rst = [];
-        this.firstData.items = datas;
-        rst.push(this.firstData);
-        rst = rst.concat(datas);
-       return rst;
-    },
-
-//展示的结构转换为入库的结构
-    toSaveDatas: function (datas) {
-        let rst = [].concat(datas);
-        rst.splice(0, 1);
-        return rst;
-    },
-
-    toUpdate: function (orgDatas, newDatas) {
-        if(orgDatas.length !== newDatas.length){
-            return true;
-        }
-        for(let i = 0, len = orgDatas.length; i < len; i++){
-            let orgObj = orgDatas[i], newObj = newDatas[i];
-            if(orgObj.value !== newObj.value){
-                return true;
-            }
-        }
-        return false;
-    },
-
-
-    a_updateInfo: function (datas) {
-        let me = this;
-        let url = '/pm/api/updateProjects',
-            updateData = {
-                updateType: 'update',
-                updateData: {ID: parseInt(scUrlUtil.GetQueryString('project')), 'property.projectFeature': datas}
-            },
-            postData = {
-                user_id: userID,
-                updateData: [updateData]
-            };
-        CommonAjax.post(url, postData, function (rstData) {
-            me.orgDatas = me.toViewDatas(datas);
-        });
-    },
-
-    initTree:function (sheet, init, datas) {
-        sheet.getRange(-1, 0, -1, 1).cellType(this.getTreeNodeCellType(datas));
-        for(let i =0, len = datas.length; i < len; i++){
-            if(datas[i].hasOwnProperty('items')){
-                let collapsed = false;
-                if(init){
-                    datas[i].collapsed=false;
-                    collapsed = true;
-                }else {
-                    collapsed = datas[i].collapsed == undefined ? true : datas[i].collapsed;
-                }
-                //sheet.getRange(i+1, -1, datas[i].items.length, -1).visible(!collapsed);
-            }
-        }
-    },
-
-    getTreeNodeCellType:function (data) {
-        var ns = GC.Spread.Sheets;
-        var rectW = 10;
-        var rectH = 10;
-        var margin = 3;
-        function TreeNodeCellType() {
-        }
-
-        function drowRect(ctx,x,y,w,h) {
-            ctx.save();
-            ctx.strokeStyle = "gray";
-            ctx.translate(0.5,0.5);
-            ctx.beginPath();
-            var rectX = x+margin;
-            var rectY =  y+ Math.round(h/2)-rectH/2;
-            ctx.moveTo(rectX, rectY);
-            ctx.lineTo(rectX, rectY+rectH);
-            ctx.lineTo(rectX+rectW, rectY+rectH);
-            ctx.lineTo(rectX+rectW, rectY);
-            ctx.lineTo(rectX, rectY);
-            ctx.moveTo(rectX+rectW, y+Math.round(h/2));
-            ctx.lineTo(rectX+rectW+5, y+Math.round(h/2));
-            ctx.stroke();
-            ctx.restore();
-        }
-
-        function drowSymbol(ctx,x,y,w,h,collapsed) {
-            ctx.save();
-            ctx.strokeStyle = "#000000";
-            ctx.translate(0.5, 0.5);
-            ctx.beginPath();
-            ctx.moveTo(x+margin+2, y+Math.round(h/2));
-            ctx.lineTo(x+margin+8, y+Math.round(h/2));
-            var rectY =  y+ Math.round(h/2)-rectH/2;
-            if(collapsed){
-                ctx.moveTo(x+margin+rectW/2,rectY+2);
-                ctx.lineTo(x+margin+rectW/2,rectY+2+6);
-            }
-            ctx.stroke();
-            ctx.restore();
-        }
-
-        function drowSubItem(ctx,x,y,w,h,offset,nextItem) {
-            offset+=6;
-            ctx.save();
-            ctx.strokeStyle = "gray";
-            ctx.translate(0.5, 0.5);
-            ctx.beginPath();
-            ctx.moveTo(x+offset, y);
-            ctx.lineTo(x+offset, y+Math.round(h/2));
-            offset+=9;
-            ctx.lineTo(x+offset, y+Math.round(h/2));
-            if(nextItem&&!nextItem.hasOwnProperty('items')){
-                ctx.moveTo(x+offset-9, y+Math.round(h/2));
-                ctx.lineTo(x+offset-9, y+h);
-            }
-            ctx.stroke();
-            ctx.restore();
-            return offset;
-        }
-
-        TreeNodeCellType.prototype = new ns.CellTypes.Text();
-        TreeNodeCellType.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
-            if(value!=null){
-                var offset = margin+rectW+6;
-                var recode = data[options.row];
-                if(recode&&recode.hasOwnProperty('items')){
-                    drowRect(ctx,x,y,w,h);
-                    var collapsed = recode.collapsed==undefined?true:recode.collapsed;//options.sheet.getTag(options.row,options.col);
-                    drowSymbol(ctx,x,y,w,h,collapsed);
-                }else if(recode&&!recode.hasOwnProperty('items')){
-                    offset= drowSubItem(ctx,x,y,w,h,offset,data[options.row+1]);
-                    offset+=1;
-                }
-                arguments[2] = x + offset;
-                arguments[4] = w - offset;
-                GC.Spread.Sheets.CellTypes.Text.prototype.paint.apply(this, arguments);
-            }
-        };
-        // override getHitInfo to allow cell type get mouse messages
-        TreeNodeCellType.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
-            return {
-                x: x,
-                y: y,
-                row: context.row,
-                col: context.col,
-                cellStyle: cellStyle,
-                cellRect: cellRect,
-                sheetArea: context.sheetArea
-            };
-        }
-        TreeNodeCellType.prototype.processMouseDown = function (hitinfo) {
-            var recode = data[hitinfo.row];
-            let offset = -1,
-                indent = 20,
-                halfBoxLength = 5;
-            let centerX = hitinfo.cellRect.x + offset + indent / 2;
-            let centerY = (hitinfo.cellRect.y + offset + (hitinfo.cellRect.y + offset + hitinfo.cellRect.height)) / 2
-            if(recode&&recode.hasOwnProperty('items')){
-                //方框外1像素内都有效
-                const boxLengh = 10;
-                if (hitinfo.x >= centerX - halfBoxLength - 1 && hitinfo.x <= centerX + halfBoxLength + 1 &&
-                    hitinfo.y >= centerY - halfBoxLength - 1 && hitinfo.y <= centerY + halfBoxLength + 1) {
-                    var collapsed = recode.collapsed==undefined?true:recode.collapsed;
-                    collapsed = !collapsed
-                    recode.collapsed=collapsed;
-                    //hitinfo.sheet.setTag(hitinfo.row,hitinfo.col,collapsed);
-                    hitinfo.sheet.getRange(hitinfo.row+1, -1, recode.items.length, -1).visible(!collapsed);
-                    hitinfo.sheet.invalidateLayout();
-                    hitinfo.sheet.repaint();
-                }
-            }
-        };
-        return new TreeNodeCellType()
-    },
-
-};
-
-$(document).ready(function () {
-
-    $('#poj-set').on('shown.bs.modal', function (e) {
-        //init Spread
-        projFeatureView.initDatas(projFeatureView.orgDatas);
-        projFeatureView.buildSheet();
-        projFeatureView.showData(projFeatureView.datas);
-        if(projectReadOnly){
-            disableSpread(projFeatureView.workBook);
-        }
-    });
-
-    $('#poj-set').on('hidden.bs.modal', function (e) {
-        //destroy Spread
-        if(projFeatureView.workBook){
-            projFeatureView.workBook.destroy();
-            projFeatureView.workBook = null;
-        }
-        projFeatureView.datas = [];
-    });
-
-    $('#tab_poj-settings-projFeature').on('shown.bs.tab', function () {
-        sheetCommonObj.refreshWorkbookDelDefer(projFeatureView.workBook, 100);
-    });
-}); */
+});

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

@@ -3168,7 +3168,7 @@ function doAfterImport(resData){
 }
 //下载导入清单示例文件
 $('#uploadExample').click(function () {
-    window.location.href = '/bills/downloadExamp';
+    window.location.href = `/bills/downloadExamp?type=${fileType}`;
 });
 
 $(function () {

+ 14 - 2
web/building_saas/main/js/views/sub_view.js

@@ -911,8 +911,20 @@ $('.gljSubTab ul li a').on('shown.bs.tab', function () {
 $('#tzCharacterText').blur(async function () {
   let billID = $("#xmtz_billID").val();
   let htmlString = $(this).html();
-  let value = htmlString.replace(/<br>/g,"\n");
-  value = value.replace(/&nbsp;/g," ");
+  let value = htmlString.replace(/&nbsp;/g," ");
+  if(value.indexOf("<div>")!= -1){
+      let preString = value.substring(0,value.indexOf("<div>"));
+      let arrayText =[];
+      let divArray = value.match(/<div>(.*?)<\/div>/g);
+      for(let d of divArray){
+          t = d.match(/<div>(\S*)<\/div>/)[1];
+          if(t == "<br>") t="";
+          arrayText.push(t)
+      }
+      value = preString +"\n"+ arrayText.join("\n");
+  }
+  value = value.replace(/<br>/g,"\n");
+
   let billNode = projectObj.project.mainTree.getNodeByID(billID);
   if(billNode && value == billNode.data.itemCharacterText) return;
   let datas = [{

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

@@ -470,6 +470,7 @@ $(function () {
         let process = getDecimal('process');
         var newVal = $(this).val();
         process = scMathUtil.roundForObj(newVal,process);
+        if (process == 0) process = 1;    // 清空系数时,强制给1
         let updateData = {type:ModuleNames.project,data:{'ID' : projectObj.project.ID(),'property.tenderSetting.gljPriceTenderCoe':process}};
         me.updateTenderData([updateData],function () {
             me.initPageContent();
@@ -490,14 +491,6 @@ $(function () {
         projectObj.project.calcProgram.doTenderCalc(callback);
     });
 
-    // $('#tenderGLJQuantity').on('click', function () {
-    //     tender_obj.doTenderCalc(tenderTypes.ttReverseGLJ);
-    // });
-    //
-    // $('#tenderRationQuantity').on('click', function () {
-    //     tender_obj.doTenderCalc(tenderTypes.ttReverseRation);
-    // });
-
     $('#cbShowTenderFields').on('click', function () {
         let showFields = $('#cbShowTenderFields').prop("checked");
         projectObj.project.saveProperty('tenderSetting.showTenderFields', showFields);

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

@@ -448,7 +448,6 @@ const importView = (() => {
                 $('#importInterface').modal('hide');
                 pr.start('导入文件', '正在生成文件,请稍候……');
                 let importData = await importXML.transformData(xmlObj);
-                debugger;
                 console.log(importData);
                 let blob = new Blob([JSON.stringify(importData)], { type: 'text/plain;charset=utf-8' });
                 // 转换成File实例
@@ -512,5 +511,5 @@ const importView = (() => {
             projTreeObj.workBook.getSheet(0).showRow(lastNode.serialNo(), GC.Spread.Sheets.VerticalPosition.center);
         }
     }
-    return { eventListen };
+    return { eventListen, doAfterImport };
 })();

+ 76 - 16
web/building_saas/pm/js/pm_newMain.js

@@ -937,9 +937,23 @@ const projTreeObj = {
         let shareImg = document.getElementById('share_pic'),
             shareImgWidth = 13,
             shareImgHeight = 13;
+        let defaultHeight = 17; // 单元格默认高度,getAutoFitHeight返回17时,单元格高度才是20...不清楚原因
         let TreeNodeCellType = function () {
         };
         TreeNodeCellType.prototype = new GC.Spread.Sheets.CellTypes.Text();
+        // 自动行高处理 此函数在每次调用autoFitRow后调用,返回的值作为新的单元格高度
+        // 在单元格设置为自动行高后,单元格的内容有空格时,spreadjs会将空格认为是换行,会导致高度计算不正确,因此在setCellValue中处理空格
+        TreeNodeCellType.prototype.getAutoFitHeight = function(value, text, cellStyle, zoomFactor, context){
+            if (!defaultHeight) {
+                defaultHeight = context.sheet.getCell(context.row, -1).height();
+            }
+            const node = me.tree.items[context.row];
+            const nodeIndent = node ? (node.depth() + 1) * indent +  node.depth() * levelIndent + imgWidth + 5 : 0;
+            const cellWidth = context.sheet.getCell(-1, context.col).width();
+            const textLength = this.getAutoFitWidth(...arguments);
+            const lineNum = Math.ceil(textLength / (cellWidth - nodeIndent));
+            return lineNum * defaultHeight;
+        };
         TreeNodeCellType.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
             if (style.backColor) {
                 ctx.save();
@@ -1142,7 +1156,11 @@ const projTreeObj = {
         const {row, col} = cell;
         let dataCode = setting.header[col]['dataCode'];
         let value = '';
-        if(dataCode === 'unitPriceFile'){
+        if (dataCode === 'name') {
+             // 在单元格设置为自动行高后,单元格的内容有空格时,spreadjs会将空格认为是换行,因此需要处理空格
+            value = node.data.name && node.data.name.replace(new RegExp(' ', 'g'), '\t') || '';
+        }
+        else if(dataCode === 'unitPriceFile'){
             if(node.data.projType === projectType.tender){
                 value = node.data.property && node.data.property.unitPriceFile && node.data.property.unitPriceFile.name ? node.data.property.unitPriceFile.name : '';
             }
@@ -1194,7 +1212,7 @@ const projTreeObj = {
                     sheet.setRowVisible(nodes[i].serialNo(), nodes[i].visible);
                 }
             }
-            //sheetCommonObj.setRowsAutoFit(sheet, rows, 0, true);
+            sheetCommonObj.setRowsAutoFit(sheet, rows, 0, true);
         };
         me.renderSheetFuc(sheet, fuc);
     },
@@ -1301,8 +1319,13 @@ const projTreeObj = {
     getFileListWithPath:function (list) {
         for(let n of list){
             let node = projTreeObj.tree.findNode(n.ID);
-            if(node) n.name = getPathName(node);
+            if(node) {
+                n.name = getPathName(node);
+            }
+            n.serialNo = node && node.serialNo() || 1;
         }
+        // 按照正确的树结构排序
+        list.sort((a, b) => a.serialNo - b.serialNo);
 
         function getPathName(node) {
             if(node.parent && node.parent.data){
@@ -2642,7 +2665,7 @@ $(document).ready(function() {
             dialog.modal('hide');
             select.data.name = newName;
             let sheet = projTreeObj.workBook.getActiveSheet();
-            sheet.setValue(sheet.getActiveRowIndex(), 0, newName);
+            projTreeObj.setCellValue({ row: sheet.getActiveRowIndex(), col: 0 }, select, sheet, projTreeObj.setting);
         });
     });
 
@@ -2772,20 +2795,41 @@ function changeFeeRate(engLib) {
 
 //根据文件类型筛选新的基本信息数据
 function getNeedfulBasicInfo(info, fileKind) {
-    let strMap = {
+    debugger;
+    /* let strMap = {
         1: 'tender',    //投标
         2: 'bid',       //招标
         3: 'control'    //控制价
+    }; */
+    const strMap = {
+        1: '投标',    //投标
+        2: '招标',       //招标
+        3: '控制价'    //控制价
     };
-    let needfulData = info.filter(data => !data.fileKind || data.fileKind === strMap[fileKind]);
+    return filterItems(info);
+
+    // 递归过滤
+    function filterItems(items) {
+        const filterdItems = items.filter(item => !item.fileKind || item.fileKind === strMap[fileKind]);
+        filterdItems.forEach(item => {
+            if (item.items && item.items.length) {
+                item.items = filterItems(item.items);
+            }
+        });
+        return filterdItems;
+    }
+
+    /* let needfulData = info.filter(data => !data.fileKind || data.fileKind === strMap[fileKind]);
     needfulData.forEach(nData => {
         let needfulSub = nData.items.filter(sData => !sData.fileKind || sData.fileKind === strMap[fileKind]);
         nData.items = needfulSub;
     });
-    return needfulData;
+    return needfulData; */
 }
 
 function getRequired(rst, datas) {
+    // 暂时不填必填项了
+    return [];
     if (!datas) {
         return rst;
     }
@@ -2944,6 +2988,17 @@ function initProjects(callback) {
             projTreeObj.tree = pmTree.createNew(projTreeObj.setting, datas);
             projTreeObj.tree.selected = projTreeObj.tree.items[0];
             projTreeObj.workBook = projTreeObj.buildSheet(projTreeObj.workBook,'projSpread',projTreeObj.setting);
+            // 调整列后自适应行高
+            /* projTreeObj.workBook.getSheet(0).bind(GC.Spread.Sheets.Events.ColumnWidthChanged, function (e, info) {
+                info.sheet.suspendPaint();
+                info.sheet.suspendEvent();
+                const rowCount = info.sheet.getRowCount();
+                for (let i = 0; i < rowCount; i++) {
+                    info.sheet.autoFitRow(i);
+                }
+                info.sheet.resumePaint();
+                info.sheet.resumeEvent();
+            }); */
             const sheet = projTreeObj.workBook.getSheet(0);
             sheet.options.frozenlineColor = '#ababab';
             sheet.frozenColumnCount(2);
@@ -3214,8 +3269,8 @@ function AddTenderItems(selected, projName, engName, tenderName, property, callb
                 let pojNode = projTreeObj.insert(projData, parent, next);
                 let engNode = projTreeObj.insert(engData, pojNode, null);
                 let tenderNode = projTreeObj.insert(tenderData, engNode, null);
-                /* const rows = [pojNode.serialNo(), engNode.serialNo(), tenderNode.serialNo()];
-                sheetCommonObj.setRowsAutoFit(projTreeObj.workBook.getSheet(0), rows, 0, true) */
+                const rows = [pojNode.serialNo(), engNode.serialNo(), tenderNode.serialNo()];
+                sheetCommonObj.setRowsAutoFit(projTreeObj.workBook.getSheet(0), rows, 0, true)
                 callback();
             }, errCB);
         }, errCB);
@@ -3255,8 +3310,8 @@ function AddTenderItems(selected, projName, engName, tenderName, property, callb
                 });
                 let engNode = projTreeObj.insert(engData, tempProj, next);
                 let tenderNode = projTreeObj.insert(tenderData, engNode, null);
-                /* const rows = [engNode.serialNo(), tenderNode.serialNo()];
-                sheetCommonObj.setRowsAutoFit(projTreeObj.workBook.getSheet(0), rows, 0, true); */
+                const rows = [engNode.serialNo(), tenderNode.serialNo()];
+                sheetCommonObj.setRowsAutoFit(projTreeObj.workBook.getSheet(0), rows, 0, true);
                 callback();
             }, errCB);
         }, errCB);
@@ -3278,8 +3333,8 @@ function AddTenderItems(selected, projName, engName, tenderName, property, callb
                         setInitSummaryData(data.updateData);
                         data.updateData.feeStandardName = data.updateData.property.feeStandardName || '';
                         let tenderNode = projTreeObj.insert(data.updateData, tempEng, null);
-                        /* const rows = [tenderNode.serialNo()];
-                        sheetCommonObj.setRowsAutoFit(projTreeObj.workBook.getSheet(0), rows, 0, true); */
+                        const rows = [tenderNode.serialNo()];
+                        sheetCommonObj.setRowsAutoFit(projTreeObj.workBook.getSheet(0), rows, 0, true);
                     }
                 });
                 callback();
@@ -3320,7 +3375,7 @@ function AddChildrenItem(selected, name, property, type, existCallback, sucCallb
                         data.updateData.shareInfo = [];
                         setInitSummaryData(data.updateData);
                         let node = projTreeObj.insert(data.updateData, parent, null);
-                        //sheetCommonObj.setRowsAutoFit(projTreeObj.workBook.getSheet(0), [node.serialNo()], 0, true);
+                        sheetCommonObj.setRowsAutoFit(projTreeObj.workBook.getSheet(0), [node.serialNo()], 0, true);
                     }
                 });
                 sucCallback();
@@ -3360,7 +3415,7 @@ function AddSiblingsItem(selected, name, property, type, existCallback, sucCallb
                         data.updateData.shareInfo = [];
                         setInitSummaryData(data.updateData);
                         const node = projTreeObj.insert(data.updateData, parent, next);
-                        //sheetCommonObj.setRowsAutoFit(projTreeObj.workBook.getSheet(0), [node.serialNo()], 0, true)
+                        sheetCommonObj.setRowsAutoFit(projTreeObj.workBook.getSheet(0), [node.serialNo()], 0, true)
                     }
                 });
                 sucCallback();
@@ -5059,8 +5114,13 @@ async function importProcessChecking(key) {
             if(result.status == "processing"){
                 setTimeout(checking,2000);
             }else if(result.status == "complete"){
+                if (Array.isArray(result.data)) {
+                    const rootData = result.data.find(item => item.projType === projectType.project);
+                    const sorted = commonUtil.getSortedTreeData(rootData.ParentID, result.data);
+                    importView.doAfterImport(sorted);
+                }
                 $.bootstrapLoading.progressEnd();
-                refreshAllPage();
+                //refreshAllPage();
             }
         }
     }

+ 43 - 17
web/over_write/js/chongqing_2018_export.js

@@ -140,22 +140,48 @@ const XMLStandard = (function () {
 
     // 项目汇总字段
     const summaryObj = {
-        feeFields: [
-            {k: fixedFlag.ENGINEERINGCOST, v: 'engineeringCost'},
-            {k: fixedFlag.SUB_ENGINERRING, v: 'subEngineering'},
-            {k: fixedFlag.MEASURE, v: 'measure'},
-            {k: fixedFlag.SAFETY_CONSTRUCTION, v: 'safetyConstruction'},
-            {k: fixedFlag.OTHER, v: 'other'},
-            {k: fixedFlag.CHARGE, v: 'charge'},
-            {k: fixedFlag.PROVISIONAL, v: 'provisional'},
-            {k: fixedFlag.MATERIAL_PROVISIONAL, v: 'materialProvisional'},
-            {k: fixedFlag.ENGINEERING_ESITIMATE, v: 'engineeringEstimate'},
-            {k: fixedFlag.DAYWORK, v: 'daywork'},
-            {k: fixedFlag.TURN_KEY_CONTRACT, v: 'turnKeyContract'},
-            {k: fixedFlag.CLAIM_VISA, v: 'claimVisa'},
-            {k: fixedFlag.TAX, v: 'tax'}
-        ],
-        engineeringCostFields: null
+        [fixedFlag.SUB_ENGINERRING]: {
+            items: [{ name: 'subEngineering', feeName: 'common' }]
+        },
+        [fixedFlag.MEASURE]: {
+            items: [{ name: 'measure', feeName: 'common' }]
+        },
+        [fixedFlag.SAFETY_CONSTRUCTION]: {
+            items: [{ name: 'safetyConstruction', feeName: 'common' }]
+        },
+        [fixedFlag.OTHER]: {
+            items: [{ name: 'other', feeName: 'common' }]
+        },
+        [fixedFlag.CHARGE]: {
+            items: [{ name: 'charge', feeName: 'common' }]
+        },
+        [fixedFlag.PROVISIONAL]: {
+            items: [{ name: 'provisional', feeName: 'common' }]
+        },
+        [fixedFlag.MATERIAL_PROVISIONAL]: {
+            items: [{ name: 'materialProvisional', feeName: 'common' }]
+        },
+        [fixedFlag.ENGINEERING_ESITIMATE]: {
+            items: [{ name: 'engineeringEstimate', feeName: 'common' }]
+        },
+        [fixedFlag.DAYWORK]: {
+            items: [{ name: 'daywork', feeName: 'common' }]
+        },
+        [fixedFlag.TURN_KEY_CONTRACT]: {
+            items: [{ name: 'turnKeyContract', feeName: 'common' }]
+        },
+        [fixedFlag.CLAIM_VISA]: {
+            items: [{ name: 'claimVisa', feeName: 'common' }]
+        },
+        [fixedFlag.TAX]: {
+            items: [{ name: 'tax', feeName: 'common' }]
+        },
+        [fixedFlag.ENGINEERINGCOST]: {
+            items: [
+                { name: 'engineeringCost', feeName: 'common' },
+                { name: 'estimate', feeName: 'estimate' }
+            ]
+        }
     };
 
     // 通用设置和工具
@@ -1421,7 +1447,7 @@ const XMLStandard = (function () {
                 //定额人材机排序
                 rationGljData = gljUtil.sortRationGLJ(rationGljData);
                 for (let rGlj of rationGljData) {
-                    const totalQuantity = gljUtil.getTotalQuantity(rGlj,  rationData, decimal.glj.quantity, decimal.ration.quantity);
+                    const totalQuantity = gljUtil.getTotalQuantity(rGlj,  rationData, decimal.ration.quantity, decimal.glj.quantity);
                     const parsedTotalQuantity = parseFloat(totalQuantity);
                     if (skipGLJTypes.includes(rGlj.type) || !parsedTotalQuantity) {
                         continue;

+ 316 - 169
web/over_write/js/guangdong_2018_export.js

@@ -6,6 +6,7 @@
  * @version
  */
 
+
 /*
 * 广东建设工程政府投资项目造价数据标准3.0
 * */
@@ -93,61 +94,6 @@ const XMLStandard = (function () {
         '修复': '23',
         '其他': '99',
     };
-    /*     // 需要用固定类别关联的费用字典,用固定类别来映射
-        // 数据节选自标准pdf文件《《建设工程政府投资项目造价数据标准》信息公开版》,附录C-费用名称与费用代号
-        // 若映射表中没有映射关系,则费用字典取名称首字母
-        const FlagFeeCodeMap = {
-            // 分部分项工程
-            [fixedFlag.SUB_ENGINERRING]: 'QDF',
-            // 措施项目
-            [fixedFlag.MEASURE]: 'CSF',
-            // 其他项目
-            [fixedFlag.OTHER]: 'QTF',
-            // 措施项目的子项
-            [fixedFlag.GREEN_MEASURE_FEE]: 'AQWMSGF', // 绿色施工安全防护措施费
-            [fixedFlag.OTHER_MEASURE_FEE]: 'QTCSF', // 其他措施费
-            // 其他项目的子项
-            [fixedFlag.PROVISIONAL]: 'ZLF', // 暂列金额
-            [fixedFlag.ESTIMATE]: 'ZGJ', // 暂估价
-            [fixedFlag.MATERIAL_PROVISIONAL]: 'ZGC', // 材料(工程设备)暂估价
-            [fixedFlag.ENGINEERING_ESITIMATE]: 'ZGGC', // 专业工程暂估价
-            [fixedFlag.DAYWORK]: 'LXF', // 计日工
-            [fixedFlag.TURN_KEY_CONTRACT]: 'ZCBFWF', // 总承包服务费
-            [fixedFlag.BUDGET_INCLUDE_WORK_FEE]: 'YSBGF', // 预算包干费
-            [fixedFlag.PROJECT_HIGH_QUALITY_FEE]: 'GCYZF', // 工程优质费
-            [fixedFlag.BUDGET_ESTIMATE_DIFF]: 'GSFDC', // 概算幅度差
-            [fixedFlag.CLAIM]: 'SPFY', // 索赔费用
-            [fixedFlag.VISA]: 'XCQZFY', // 现场签证
-            [fixedFlag.OTHER_FEE]: 'QTFY', // 其他费用
-            // 税金
-            [fixedFlag.TAX]: 'SJ',
-            // 工程造价
-            [fixedFlag.ENGINEERINGCOST]: 'ZZJ',
-        }; */
-    // 需要用计算基数关联的费用字典
-    /*     const FormulaFeeCodeMap = {
-            '{分部分项工程费}': 'QDF',
-            '{分部分项人工费}': 'QRG',
-            '{分部分项材料费}': 'QCL',
-            '{分部分项施工机具费}': 'QJX',
-            '{分部分项主材费}': 'QZCF',
-            '{分部分项设备费}': 'QSBF',
-            '{分部分项人工工日}': 'FBFXRGGR', // 标准没有,自增
-            '{建筑面积}': 'JZMZ', // 自增
-            '{措施项目费}': 'CSF',
-            '{其他项目费}': 'QTF',
-            '{甲供人工费}': 'JGRGF', // 自增
-            '{甲供材料费}': 'JGC',
-            '{甲供施工机具费}': 'JGSGJJF', // 自增
-            '{甲定人工费}': 'JDRGF', // 自增
-            '{甲定材料费}': 'JDCLF', // 自增
-            '{甲定施工机具费}': 'JDSGJJF', // 自增
-            '{甲定主材费}': 'JDZCF', // 自增
-            '{甲定设备费}': 'JDSBF', // 自增
-            '{暂估材料费(从子目汇总)}': 'ZGCLFCZMHZ', // 自增
-            '{税金}': 'SJ',
-    
-        }; */
     // 费用字典占用列表,普通清单根据首字母获取费用字典时,与下列占用费用字典重复时,需要加上_序号后缀
     const feeCodeList = [
         'QDF', 'QRG', 'QCL', 'QJX', 'ZCSB', 'QZCF', 'QSBF', 'QGL', 'QLR', 'QZGJ', 'CSF', 'AQWMSGF', 'AXSJSCSXMF',
@@ -171,7 +117,7 @@ const XMLStandard = (function () {
     };
     // 计算程序名称-费用代号映射
     const CalculationCodeMap = {
-        '直接费': 'DEZJF',
+        '直接费': 'ZJF',
         '人工费': 'RGF',
         '材料费': 'CLF',
         '施工机具费': 'JXF',
@@ -265,33 +211,66 @@ const XMLStandard = (function () {
 
     // 项目汇总字段
     const summaryObj = {
-        // 取固定清单的综合合价进行汇总
-        feeFields: [
-            { k: fixedFlag.ENGINEERINGCOST, v: 'engineeringCost' },
-            { k: fixedFlag.SUB_ENGINERRING, v: 'subEngineering' },
-            { k: fixedFlag.MEASURE, v: 'measure' },
-            { k: fixedFlag.GREEN_MEASURE_FEE, v: 'greenMeasureFee' },
-            { k: fixedFlag.OTHER_MEASURE_FEE, v: 'otherPreliminaries' },
-            { k: fixedFlag.OTHER, v: 'other' },
-            { k: fixedFlag.PROVISIONAL, v: 'provisional' },
-            { k: fixedFlag.MATERIAL_PROVISIONAL, v: 'materialProvisional' },
-            { k: fixedFlag.ENGINEERING_ESITIMATE, v: 'engineeringEstimate' },
-            { k: fixedFlag.DAYWORK, v: 'daywork' },
-            { k: fixedFlag.TURN_KEY_CONTRACT, v: 'turnKeyContract' },
-            { k: fixedFlag.CLAIM, v: 'claim' },
-            { k: fixedFlag.VISA, v: 'visa' },
-            { k: fixedFlag.TAX, v: 'tax' }
-        ],
-        // 取工程造价的一些费用进行汇总,k为汇总到summaryInfo的字段,v为取的工程造价费用字段
-        engineeringCostFields: [
-            { k: 'labour', v: 'labour' },
-            { k: 'material', v: 'material' },
-            { k: 'equipment', v: 'equipment' },
-            { k: 'mainMaterial', v: 'mainMaterial' },
-            { k: 'machine', v: 'machine' },
-            { k: 'overhead', v: 'manage' },
-            { k: 'profit', v: 'profit' },
-        ]
+        [fixedFlag.SUB_ENGINERRING]: {
+            items: [
+                { name: 'subEngineering', feeName: 'common' },
+                { name: 'subEngineeringLabour', feeName: 'labour' },
+                { name: 'subEngineeringMaterial', feeName: 'material' },
+                { name: 'subEngineeringEquipment', feeName: 'equipment' },
+                { name: 'subEngineeringMainMaterial', feeName: 'mainMaterial' },
+                { name: 'subEngineeringMachine', feeName: 'machine' },
+                { name: 'subEngineeringManage', feeName: 'manage' },
+                { name: 'subEngineeringProfit', feeName: 'profit' }
+            ]
+        },
+        [fixedFlag.MEASURE]: {
+            items: [
+                { name: 'measure', feeName: 'common' },
+                { name: 'measureLabour', feeName: 'labour' },
+                { name: 'measureMaterial', feeName: 'material' },
+                { name: 'measureEquipment', feeName: 'equipment' },
+                { name: 'measureMainMaterial', feeName: 'mainMaterial' },
+                { name: 'measureMachine', feeName: 'machine' },
+                { name: 'measureManage', feeName: 'manage' },
+                { name: 'measureProfit', feeName: 'profit' }
+            ]
+        },
+        [fixedFlag.GREEN_MEASURE_FEE]: {
+            items: [{ name: 'greenMeasureFee', feeName: 'common' }]
+        },
+        [fixedFlag.OTHER_MEASURE_FEE]: {
+            items: [{ name: 'otherPreliminaries', feeName: 'common' }]
+        },
+        [fixedFlag.OTHER]: {
+            items: [{ name: 'other', feeName: 'common' }]
+        },
+        [fixedFlag.PROVISIONAL]: {
+            items: [{ name: 'provisional', feeName: 'common' }]
+        },
+        [fixedFlag.MATERIAL_PROVISIONAL]: {
+            items: [{ name: 'materialProvisional', feeName: 'common' }]
+        },
+        [fixedFlag.ENGINEERING_ESITIMATE]: {
+            items: [{ name: 'engineeringEstimate', feeName: 'common' }]
+        },
+        [fixedFlag.DAYWORK]: {
+            items: [{ name: 'daywork', feeName: 'common' }]
+        },
+        [fixedFlag.TURN_KEY_CONTRACT]: {
+            items: [{ name: 'turnKeyContract', feeName: 'common' }]
+        },
+        [fixedFlag.CLAIM]: {
+            items: [{ name: 'claim', feeName: 'common' }]
+        },
+        [fixedFlag.VISA]: {
+            items: [{ name: 'visa', feeName: 'common' }]
+        },
+        [fixedFlag.TAX]: {
+            items: [{ name: 'tax', feeName: 'common' }]
+        },
+        [fixedFlag.ENGINEERINGCOST]: {
+            items: [{ name: 'engineeringCost', feeName: 'common' }]
+        }
     };
 
     // 获取工程类型:枚举单位工程的工程专业+费用标准,用“;”分隔
@@ -605,7 +584,7 @@ const XMLStandard = (function () {
                         : _util.getValueByKey(basicInformation, 'tenderCompileDate')
                 },
                 // 编制单位法定代表人或其授权人
-                { name: 'Authorizer', mustHasValue: true, value: _util.getValueByKey(basicInformation, 'authorizer') },
+                { name: 'Authorizer', mustHasValue: true, value: _util.getValueByKey(basicInformation, 'constructingUnitsPerson') },
                 // 工程总价(元)
                 {
                     name: 'Total', dName: '工程总价', type: _type.DECIMAL, required: true,
@@ -869,6 +848,15 @@ const XMLStandard = (function () {
         }
         // 费用汇总
         function SummaryOfCost(summaryInfo) {
+            // 人工费、材料费、机械费...: 取“分部分项工程”行+“措施项目”行对应的费用值
+            const totalLabour = scMathUtil.roundForObj(+summaryInfo.subEngineeringLabour + (+summaryInfo.measureLabour), Decimal.FEE);
+            const totalMaterial = scMathUtil.roundForObj(+summaryInfo.subEngineeringMaterial + (+summaryInfo.measureMaterial), Decimal.FEE);
+            const totalMachine = scMathUtil.roundForObj(+summaryInfo.subEngineeringMachine + (+summaryInfo.measureMachine), Decimal.FEE);
+            const totalEquipment = scMathUtil.roundForObj(+summaryInfo.subEngineeringEquipment + (+summaryInfo.measureEquipment), Decimal.FEE);
+            const totalMainMaterial = scMathUtil.roundForObj(+summaryInfo.subEngineeringMainMaterial + (+summaryInfo.measureMainMaterial), Decimal.FEE);
+            const totalMainMaterialEquipment = scMathUtil.roundForObj(totalMainMaterial + totalEquipment, Decimal.FEE);
+            const totalManage = scMathUtil.roundForObj(+summaryInfo.subEngineeringManage + (+summaryInfo.measureManage), Decimal.FEE);
+            const totalProfit = scMathUtil.roundForObj(+summaryInfo.subEngineeringProfit + (+summaryInfo.measureProfit), Decimal.FEE);
             const attrs = [
                 // 工程造价(元)
                 {
@@ -948,42 +936,42 @@ const XMLStandard = (function () {
                 // 人工费
                 {
                     name: 'Labor', type: _type.DECIMAL,
-                    value: summaryInfo.labour
+                    value: totalLabour
                 },
                 // 材料费
                 {
                     name: 'Material', type: _type.DECIMAL,
-                    value: summaryInfo.material
+                    value: totalMaterial
                 },
                 // 设备费
                 {
                     name: 'Equipment', type: _type.DECIMAL,
-                    value: summaryInfo.equipment
+                    value: totalEquipment
                 },
                 // 主材设备费
                 {
                     name: 'MainMaterialEquipment', type: _type.DECIMAL,
-                    value: scMathUtil.roundForObj(summaryInfo.mainMaterial + summaryInfo.equipment, Decimal.FEE)
+                    value: totalMainMaterialEquipment
                 },
                 // 主材费
                 {
                     name: 'MainMaterial', type: _type.DECIMAL,
-                    value: summaryInfo.mainMaterial
+                    value: totalMainMaterial
                 },
                 // 机械费
                 {
                     name: 'Machine', type: _type.DECIMAL,
-                    value: summaryInfo.machine
+                    value: totalMachine
                 },
                 // 管理费
                 {
                     name: 'Overhead', type: _type.DECIMAL,
-                    value: summaryInfo.overhead
+                    value: totalManage
                 },
                 // 利润
                 {
                     name: 'Profit', type: _type.DECIMAL,
-                    value: summaryInfo.profit
+                    value: totalProfit
                 },
             ];
             _base.Element.call(this, 'SummaryOfCost', attrs, '费用汇总');
@@ -1082,10 +1070,10 @@ const XMLStandard = (function () {
                     value: tenderData.name
                 },
                 // 标段 取建设项目名称
-                {
+                /* {
                     name: 'Segment',
                     value: projectData.name
-                },
+                }, */
                 // 工程类别
                 {
                     name: 'ProjectCategory', dName: '工程类别', required: true,
@@ -1094,7 +1082,7 @@ const XMLStandard = (function () {
                 // 工程类型
                 {
                     name: 'ProjectType', dName: '工程类型', required: true,
-                    value: getProjectType(tenderData, false)
+                    value: _util.getValueByKey(projectFeature, 'projType')
                 },
                 // 计价模式
                 {
@@ -1136,10 +1124,10 @@ const XMLStandard = (function () {
                     name: 'Scale', dName: '建设规模', type: _type.DECIMAL, required: true,
                     value: _util.getValueByKey(projectFeature, 'buildScale')
                 },
-                // 建设规模单位 暂取'm2'
+                // 建设规模单位 取单位工程-工程特征-建设规模单位
                 {
                     name: 'Unit', dName: '建设规模单位', required: true,
-                    value: 'm2'
+                    value: _util.getValueByKey(projectFeature, 'buildScaleUnit')
                 },
                 // 占总投资比例(%)
                 {
@@ -1313,7 +1301,8 @@ const XMLStandard = (function () {
             _base.Element.call(this, 'Preliminaries', []);
         }
         // 合计费用
-        function SummaryOfBasicCost(items, bills) {
+        function SummaryOfBasicCost(items, node) {
+            const bills = node.data;
             // 省略了一些
             const mainMaterial = _util.getFee(bills.fees, 'mainMaterial.totalFee');
             const equipment = _util.getFee(bills.fees, 'equipment.totalFee');
@@ -1342,7 +1331,7 @@ const XMLStandard = (function () {
                 // 暂估价
                 {
                     name: 'Appraisal', type: _type.DECIMAL,
-                    value: _util.getFeeByFlag(items, fixedFlag.ESTIMATE, 'common.totalFee')
+                    value: _util.getFee(bills.fees, 'estimate.totalFee')
                 },
                 // 机械费
                 {
@@ -1362,27 +1351,29 @@ const XMLStandard = (function () {
                 // 分部分项工程费
                 {
                     name: 'DivisionalAndElementalWorks', type: _type.DECIMAL,
-                    value: _util.getFeeByFlag(items, fixedFlag.SUB_ENGINERRING, 'common.totalFee')
+                    // TODO value的处理速度可能需要优化
+                    value: node.isBelongToFlags([fixedFlag.SUB_ENGINERRING]) ? _util.getFeeByFlag(items, fixedFlag.SUB_ENGINERRING, 'common.totalFee') : '0'
                 },
                 // 措施项目费
                 {
                     name: 'Preliminaries', type: _type.DECIMAL,
-                    value: _util.getFeeByFlag(items, fixedFlag.MEASURE, 'common.totalFee')
+                    value: node.isBelongToFlags([fixedFlag.MEASURE]) ? _util.getFeeByFlag(items, fixedFlag.MEASURE, 'common.totalFee') : '0'
                 },
                 // 绿色施工安全防护措施费
                 {
                     name: 'CostForHSE', type: _type.DECIMAL,
-                    value: _util.getFeeByFlag(items, fixedFlag.GREEN_MEASURE_FEE, 'common.totalFee')
+                    value: node.isBelongToFlags([fixedFlag.GREEN_MEASURE_FEE]) ? _util.getFeeByFlag(items, fixedFlag.GREEN_MEASURE_FEE, 'common.totalFee') : '0'
                 },
                 // 其他项目费
                 {
                     name: 'SundryCosts', type: _type.DECIMAL,
-                    value: _util.getFeeByFlag(items, fixedFlag.OTHER_MEASURE_FEE, 'common.totalFee')
+                    value: node.isBelongToFlags([fixedFlag.OTEHER]) ? _util.getFeeByFlag(items, fixedFlag.OTEHER, 'common.totalFee') : '0'
                 },
                 // 暂列金额
                 {
                     name: 'ProvisionalSums', type: _type.DECIMAL,
-                    value: _util.getFeeByFlag(items, fixedFlag.PROVISIONAL, 'common.totalFee')
+                    value: '0'
+                    //value: node.isBelongToFlags([fixedFlag.fixedFlag.PROVISIONAL]) ? _util.getFeeByFlag(items, fixedFlag.PROVISIONAL, 'common.totalFee') : '0'
                 },
                 // 规费
                 {
@@ -1392,7 +1383,8 @@ const XMLStandard = (function () {
                 // 税金
                 {
                     name: 'Tax', type: _type.DECIMAL,
-                    value: _util.getFeeByFlag(items, fixedFlag.TAX, 'common.totalFee')
+                    value: '0'
+                    //value: node.isBelongToFlags([fixedFlag.fixedFlag.TAX]) ? _util.getFeeByFlag(items, fixedFlag.TAX, 'common.totalFee') : '0'
                 },
             ];
             _base.Element.call(this, 'SummaryOfBasicCost', attrs);
@@ -1493,7 +1485,7 @@ const XMLStandard = (function () {
         function ExpressElement(quantityDetail) {
             const attrs = [
                 // 序号
-                { name: 'OrderNumber', dName: '序号', required: true, value: quantityDetail.seq },
+                { name: 'OrderNumber', dName: '序号', required: true, value: quantityDetail.seq + 1 }, // 软件中是从0开始,导出需要从1开始
                 // 工程量计算式
                 { name: 'Express', dName: '工程量计算式', required: true, value: quantityDetail.regex },
                 // 工程量
@@ -1583,14 +1575,14 @@ const XMLStandard = (function () {
             _base.Element.call(this, 'ManageFees', attrs, '子目管理费');
         }
         // 工料机含量明细
-        function LabourMaterialsEquipmentsMachinesElement(glj, quantity) {
+        function LabourMaterialsEquipmentsMachinesElement(glj, quantity, noCost) {
             const attrs = [
                 // 工料机编码
                 { name: 'Number', value: glj.code },
                 // 消耗量
                 { name: 'Quantity', type: _type.DECIMAL, value: quantity },
                 // 不计价材料
-                { name: 'NOCost', typ: _type.BOOL, value: 'false' },
+                { name: 'NOCost', typ: _type.BOOL, value: noCost },
                 // 备注
                 { name: 'Remark', value: '' }
             ];
@@ -1607,7 +1599,7 @@ const XMLStandard = (function () {
                 // 费率
                 { name: 'Rate', type: _type.DECIMAL, value: feeRate },
                 // 金额
-                { name: 'Total', type: _type.DECIMAL, value: calcItem.totalFee },
+                { name: 'Total', type: _type.DECIMAL, value: calcItem.unitFee },
                 // 费用代号
                 { name: 'Code', value: CalculationCodeMap[calcItem.name] },
                 // 备注
@@ -1636,6 +1628,8 @@ const XMLStandard = (function () {
         // 其他项目费标题
         function SundryCostsGroup(node) {
             const bills = node.data;
+            // 材料暂估价取不汇总,其他为汇总
+            const kind = node.getFlag() === fixedFlag.MATERIAL_PROVISIONAL ? SummrayKind.NO : SummrayKind.YES;
             const attrs = [
                 // 名称
                 { name: 'Name', dName: '名称', required: true, value: bills.name },
@@ -1644,7 +1638,7 @@ const XMLStandard = (function () {
                 // 费用代号
                 { name: 'Code', dName: '费用代号', required: true, value: getFeeCode(bills) },
                 // 汇总类型
-                { name: 'Kind', dName: '汇总类型', type: _type.INT, required: true, value: SummrayKind.YES },
+                { name: 'Kind', dName: '汇总类型', type: _type.INT, required: true, value: kind },
                 // 备注
                 { name: 'Remark', value: bills.remark }
             ];
@@ -1653,6 +1647,8 @@ const XMLStandard = (function () {
         // 其他项目费明细
         function SundryCostsItem(node) {
             const bills = node.data;
+            // 材料暂估价取不汇总,其他为汇总
+            const kind = node.getFlag() === fixedFlag.MATERIAL_PROVISIONAL ? SummrayKind.NO : SummrayKind.YES;
             const attrs = [
                 // 名称
                 { name: 'Name', dName: '名称', required: true, value: bills.name },
@@ -1671,7 +1667,7 @@ const XMLStandard = (function () {
                 // 费用代号
                 { name: 'Code', dName: '费用代号', required: true, value: getFeeCode(bills) },
                 // 汇总类型
-                { name: 'Kind', dName: '汇总类型', type: _type.INT, required: true, value: SummrayKind.NO },
+                { name: 'Kind', dName: '汇总类型', type: _type.INT, required: true, value: kind },
                 // 备注
                 { name: 'Remark', value: bills.remark }
             ];
@@ -1711,7 +1707,7 @@ const XMLStandard = (function () {
                 // 金额
                 { name: 'Total', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.totalFee') },
                 // 汇总类型
-                { name: 'Kind', dName: '汇总类型', type: _type.INT, required: true, value: SummrayKind.NO },
+                { name: 'Kind', dName: '汇总类型', type: _type.INT, required: true, value: SummrayKind.YES },
                 // 备注
                 { name: 'Remark', value: bills.remark }
             ];
@@ -1734,7 +1730,7 @@ const XMLStandard = (function () {
                 // 单价 取市场价
                 { name: 'Price', type: _type.DECIMAL, value: price.marketPrice },
                 // 金额 round(总消耗量*市场价,2)
-                { name: 'Total', type: _type.DECIMAL, value: scMathUtil.roundForObj(price.quantity * price.marketPrice, 2) },
+                { name: 'Total', type: _type.DECIMAL, value: scMathUtil.roundForObj(glj.quantity * price.marketPrice, 2) },
                 // 备注
                 { name: 'Remark', value: glj.remark }
             ];
@@ -1778,7 +1774,7 @@ const XMLStandard = (function () {
                 // 金额
                 { name: 'Total', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.totalFee') },
                 // 汇总类型
-                { name: 'Kind', dName: '汇总类型', type: _type.INT, required: true, value: SummrayKind.NO },
+                { name: 'Kind', dName: '汇总类型', type: _type.INT, required: true, value: SummrayKind.YES },
                 // 备注
                 { name: 'Remark', value: bills.remark }
             ];
@@ -1843,8 +1839,8 @@ const XMLStandard = (function () {
             const attrs = [
                 // 名称
                 { name: 'Name', dName: '名称', required: true, value: bills.name },
-                // 项目价值
-                { name: 'Quantity', type: _type.DECIMAL, value: bills.quantity },
+                // 项目价值,计算基数对应的金额
+                { name: 'Quantity', type: _type.DECIMAL, value: bills.tenderCalcBaseValue },
                 // 服务内容
                 { name: 'Content', value: bills.serviceContent },
                 // 计算基数
@@ -1854,7 +1850,7 @@ const XMLStandard = (function () {
                 // 金额
                 { name: 'Total', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.totalFee') },
                 // 汇总类型
-                { name: 'Kind', dName: '汇总类型', type: _type.INT, required: true, value: SummrayKind.NO },
+                { name: 'Kind', dName: '汇总类型', type: _type.INT, required: true, value: SummrayKind.YES },
                 // 备注
                 { name: 'Remark', value: bills.remark }
             ];
@@ -1894,9 +1890,9 @@ const XMLStandard = (function () {
                 // 金额
                 { name: 'Total', type: _type.DECIMAL, value: _util.getFee(bills.fees, 'common.totalFee') },
                 // 依据
-                { name: 'Reason', value: '' },
+                { name: 'Reason', value: bills.claimVisa },
                 // 汇总类型
-                { name: 'Kind', dName: '汇总类型', type: _type.INT, required: true, value: SummrayKind.NO },
+                { name: 'Kind', dName: '汇总类型', type: _type.INT, required: true, value: SummrayKind.YES },
                 // 备注
                 { name: 'Remark', value: bills.remark }
             ];
@@ -1931,8 +1927,36 @@ const XMLStandard = (function () {
             ];
             _base.Element.call(this, eleName, attrs, '税金');
         }
+        // 人材机汇总条目对象
+        function getGLJSummaryItem(glj, price, infoData, coeData) {
+            return {
+                code: glj.code || '',
+                name: glj.name || '',
+                specs: glj.specs || '',
+                unit: glj.unit || '',
+                quantity: glj.quantity || '0',
+                riskCoe: infoData && infoData.riskCoe || '0',
+                standardPrice: infoData && infoData.standardPrice || '0',
+                basePrice: price.basePrice || '0',
+                marketPrice: price.marketPrice || '0',
+                varWeight: coeData && coeData.varWeight || '0',
+                FO: coeData && coeData.FO || '0',
+                FI: coeData && coeData.FI || '0',
+                kind: getGLJKind(glj.type),
+                isConcrete: [205, 206].includes(glj.type),
+                isMainMaterial: !!glj.is_main_material,
+                isProvisionalMaterial: !!glj.is_evaluate,
+                provider: Provider[glj.supply],
+                delivery: glj.delivery || '',
+                location: glj.delivery_address || '',
+                producingArea: glj.originPlace || '',
+                supplier: glj.vender || '',
+                character: glj.qualityGrace || '',
+                remark: glj.remark || ''
+            };
+        }
         // 人材机汇总
-        function LabourMaterialsEquipmentsMachinesSummary(glj, price, infoData, coeData) {
+        function LabourMaterialsEquipmentsMachinesSummary(glj) {
             const attrs = [
                 // 编码
                 { name: 'Number', dName: '编码', required: true, value: glj.code },
@@ -1945,63 +1969,63 @@ const XMLStandard = (function () {
                 // 数量(总消耗量)
                 { name: 'Quantity', type: _type.DECIMAL, value: glj.quantity },
                 // 风险系数 从承包人主要材料设备-造价信息差额调整法读取,取不到则输出"0"
-                { name: 'ProviderExp', type: _type.DECIMAL, value: infoData && infoData.riskCoe || '0' },
+                { name: 'ProviderRate', type: _type.DECIMAL, value: glj.riskCoe },
                 // 基准单价 从承包人主要材料设备-造价信息差额调整法读取,取不到则输出"0"
-                { name: 'ProviderBase', type: _type.DECIMAL, value: infoData && infoData.standardPrice || '0' },
+                { name: 'ProviderBase', type: _type.DECIMAL, value: glj.standardPrice },
                 // 除税定额价(定额价)
-                { name: 'NoTaxOrgPrice', type: _type.DECIMAL, value: price.basePrice },
+                { name: 'NoTaxOrgPrice', type: _type.DECIMAL, value: glj.basePrice },
                 // 除税编制价(市场价)
-                { name: 'NoTaxPrice', type: _type.DECIMAL, value: price.marketPrice },
+                { name: 'NoTaxPrice', type: _type.DECIMAL, value: glj.marketPrice },
                 // 含税定额价 含税与不含税是对应项目的计税方式(一般、简易)
                 // 人材机含税和不含税的数据在项目创建时就确定,并且都赋在一个属性上
                 // 因此导出都取项目中的basePriceh额marketPrice就行
-                { name: 'TaxOrgPrice', type: _type.DECIMAL, value: price.basePrice },
+                { name: 'TaxOrgPrice', type: _type.DECIMAL, value: glj.basePrice },
                 // 含税编制价 
-                { name: 'TaxPrice', type: _type.DECIMAL, value: price.marketPrice },
+                { name: 'TaxPrice', type: _type.DECIMAL, value: glj.marketPrice },
                 // 引进部分
                 { name: 'ForeignCurrency', type: _type.DECIMAL, value: '0' },
                 // 折合人民币
                 { name: 'ConvertedIntoRMB', type: _type.DECIMAL, value: '0' },
                 // 除税定额价合价 取Round(定额价*总消耗量,2)
-                { name: 'NoTaxOrgTotal', type: _type.DECIMAL, value: scMathUtil.roundForObj(price.basePrice * glj.quantity, 2) },
+                { name: 'NoTaxOrgTotal', type: _type.DECIMAL, value: scMathUtil.roundForObj(glj.basePrice * glj.quantity, 2) },
                 // 除税编制价合价(元) 取Round(不含税市场价*总消耗量,2)
-                { name: 'NoTaxTotal', type: _type.DECIMAL, value: scMathUtil.roundForObj(price.marketPrice * glj.quantity, 2) },
+                { name: 'NoTaxTotal', type: _type.DECIMAL, value: scMathUtil.roundForObj(glj.marketPrice * glj.quantity, 2) },
                 // 含税定额价合价
                 { name: 'TaxOrgTotal', type: _type.DECIMAL, value: '0' },
                 // 含税编制价合价 取Round(含税市场价*总消耗量,2) (暂时用市场价)
-                { name: 'TaxTotal', type: _type.DECIMAL, value: scMathUtil.roundForObj(price.marketPrice * glj.quantity, 2) },
+                { name: 'TaxTotal', type: _type.DECIMAL, value: scMathUtil.roundForObj(glj.marketPrice * glj.quantity, 2) },
                 // 变值权重 从承包人主要材料设备-价格指数调整法读取,取不到则输出“0”
-                { name: 'Weight', type: _type.DECIMAL, value: coeData && coeData.varWeight || '0' },
+                { name: 'Weight', type: _type.DECIMAL, value: glj.varWeight },
                 // 基本价格指数 从承包人主要材料设备-价格指数调整法读取,取不到则输出“0”
-                { name: 'BasicPrice', type: _type.DECIMAL, value: coeData && coeData.FO || '0' },
+                { name: 'BasicPrice', type: _type.DECIMAL, value: glj.FO },
                 // 现行价格指数 从承包人主要材料设备-价格指数调整法读取,取不到则输出“0”
-                { name: 'CurrentPrice', type: _type.DECIMAL, value: coeData && coeData.FI || '0' },
+                { name: 'CurrentPrice', type: _type.DECIMAL, value: glj.FI },
                 // 工料机类型
-                { name: 'Kind', type: _type.INT, value: getGLJKind(glj.type) },
+                { name: 'Kind', type: _type.INT, value: glj.kind },
                 // 工料机归属
                 { name: 'Class', value: '' },
                 // 扩展属性
                 { name: 'ExtKind', type: _type.INT, value: 1 },
                 // 商品砼 如果工料机类型是“商品混凝土”、“商品砂浆”、“商品配合比”(软件中没有),则取“true”;否则取“false”
-                { name: 'Concrete', type: _type.BOOL, value: [205, 206].includes(glj.type) },
+                { name: 'Concrete', type: _type.BOOL, value: glj.isConcrete },
                 // 主要材料
-                { name: 'MainMaterial', type: _type.BOOL, value: !!glj.is_main_material },
+                { name: 'MainMaterial', type: _type.BOOL, value: glj.isMainMaterial },
                 // 是否暂估价材料
-                { name: 'ProvisionalMaterial', type: _type.BOOL, value: !!glj.is_evaluate },
+                { name: 'ProvisionalMaterial', type: _type.BOOL, value: glj.isProvisionalMaterial },
                 // 供料方式
-                { name: 'Provider', type: _type.INT, value: Provider[glj.supply] },
+                { name: 'Provider', type: _type.INT, value: glj.provider },
                 // 价格来源
                 { name: 'PriceSource', value: '' },
                 // 交货方式
                 { name: 'Delivery', value: glj.delivery },
                 // 送达地点
-                { name: 'Location', value: glj.delivery_address },
+                { name: 'Location', value: glj.location },
                 // 产地
-                { name: 'ProducingArea', value: glj.originPlace },
+                { name: 'ProducingArea', value: glj.producingArea },
                 // 供应商
-                { name: 'Supplier', value: glj.vender },
+                { name: 'Supplier', value: glj.supplier },
                 // 质量要求
-                { name: 'Character', value: glj.qualityGrace },
+                { name: 'Character', value: glj.character },
                 // 备注
                 { name: 'Remark', value: glj.remark }
             ];
@@ -2090,6 +2114,11 @@ const XMLStandard = (function () {
                     fileName: name
                 });
             });
+            // 建设项目表的人材机汇总、评标材料汇总
+            const constructionGLJSummary = loadConstructionGLJSummary();
+            const constructionBidEvaluations = loadConstructionBidEval();
+            const projectInstallationWorkCost = constructionProjectEle.children.find(ele => ele.name === 'ProjectInstallationWorkCost');
+            projectInstallationWorkCost.children.push(...constructionGLJSummary, ...constructionBidEvaluations);
             return extractData;
         }
 
@@ -2117,6 +2146,7 @@ const XMLStandard = (function () {
             const projectInstall = new ProjectInstallationWorkCost(projectData, summaryInfo);
             // 单项工程
             const sectionalWorks = await loadEngineering(projectData.summaryInfo, projectData.children);
+            // 人材机汇总
             projectInstall.children.push(...sectionalWorks);
             constructionProject.children.push(
                 systemInfo,
@@ -2241,9 +2271,6 @@ const XMLStandard = (function () {
                 const gljSummary = loadGLJSummary();
                 // 评标材料
                 const bidEvaluations = loadBidEvaluation();
-                // 人材机汇总和评标材料需要汇总到建设项目
-                const projectInstallationWorkCost = constructionProjectEle.children.find(ele => ele.name === 'ProjectInstallationWorkCost');
-                projectInstallationWorkCost.children.push(...gljSummary, ...bidEvaluations);
                 unitWork.children.push(
                     attrInfo,
                     addiInfo,
@@ -2271,6 +2298,7 @@ const XMLStandard = (function () {
         function loadAttrInfo(attrList) {
             const attrInfo = new Info('AttrInfo');
             attrInfo.children = loadItems(attrList);
+            return attrInfo;
 
             // 递归导出子项
             function loadItems(items) {
@@ -2352,7 +2380,7 @@ const XMLStandard = (function () {
             const division = new DivisionalAndElementalWorks();
             const subEngineering = tenderDetail.mainTree.roots.find(node => node.getFlag() === fixedFlag.SUB_ENGINERRING);
             // 合计费用
-            const summaryCost = new SummaryOfBasicCost(tenderDetail.mainTree.items, subEngineering.data);
+            const summaryCost = new SummaryOfBasicCost(tenderDetail.mainTree.items, subEngineering);
             // 分部、分项
             const fbfx = loadFBFX(subEngineering.children, BillsKind.FBFX);
             division.children.push(summaryCost, ...fbfx);
@@ -2364,7 +2392,7 @@ const XMLStandard = (function () {
             const preliminaries = new Preliminaries();
             const measure = tenderDetail.mainTree.roots.find(node => node.getFlag() === fixedFlag.MEASURE);
             // 合计费用
-            const summaryCost = new SummaryOfBasicCost(tenderDetail.mainTree.items, measure.data);
+            const summaryCost = new SummaryOfBasicCost(tenderDetail.mainTree.items, measure);
             // 分部、分项
             const fbfx = loadFBFX(measure.children, BillsKind.MEASURE);
             preliminaries.children.push(summaryCost, ...fbfx);
@@ -2378,9 +2406,9 @@ const XMLStandard = (function () {
                 // 有子清单的是分部
                 if (node.source.children.length) {
                     ele = new DivisionalWorks(node.data);
-                    const summaryCost = new SummaryOfBasicCost(tenderDetail.mainTree.items, node.data);
+                    const summaryCost = new SummaryOfBasicCost(tenderDetail.mainTree.items, node);
                     // 递归获取子元素
-                    ele.children = [summaryCost, ...loadFBFX(node.children)];
+                    ele.children = [summaryCost, ...loadFBFX(node.children, kind)];
                 } else { // 无子清单的是分项
                     ele = loadBills(node, kind);
                 }
@@ -2393,7 +2421,7 @@ const XMLStandard = (function () {
             const specialty = Specialty[tenderDetail.projectInfo.property.engineeringName]; // 工程专业
             const workElement = new WorkElement(node, kind, specialty);
             // 合计费用
-            const summaryCost = new SummaryOfBasicCost(tenderDetail.mainTree.items, node.data);
+            const summaryCost = new SummaryOfBasicCost(tenderDetail.mainTree.items, node);
             workElement.children.push(summaryCost);
             // 工程量计算表
             const expressElement = loadQuantityExpressions(tenderDetail.quantity_detail.datas, true, node.data.ID);
@@ -2447,15 +2475,16 @@ const XMLStandard = (function () {
             let rationGLJs = tenderDetail.ration_glj.datas.filter(rGLJ => rGLJ.rationID === node.data.ID);
             rationGLJs = gljUtil.sortRationGLJ(rationGLJs); // 定额人材机排序
             const rationGLJElements = rationGLJs
-                /* .filter(rGLJ => {
+                .filter(rGLJ => {
                     // 总消耗量为0的,不输出
-                    const totalQuantity = gljUtil.getTotalQuantity(rGLJ, node.data, Decimal.GLJ, Decimal.QUANTITY);
+                    const totalQuantity = gljUtil.getTotalQuantity(rGLJ, node.data, Decimal.QUANTITY, Decimal.GLJ);
                     const parsedTotalQuantity = parseFloat(totalQuantity);
                     return parsedTotalQuantity;
-                }) */
+                })
                 .map(rGLJ => {
-                    //const totalQuantity = gljUtil.getTotalQuantity(rGLJ, node.data, Decimal.GLJ, Decimal.QUANTITY);
-                    return new LabourMaterialsEquipmentsMachinesElement(rGLJ, rGLJ.quantity)
+                    const totalQuantity = gljUtil.getTotalQuantity(rGLJ, node.data, Decimal.QUANTITY, Decimal.GLJ);
+                    const noCost = !+totalQuantity; // 总消耗量为0即为不计价材料
+                    return new LabourMaterialsEquipmentsMachinesElement(rGLJ, rGLJ.quantity, noCost);
                 });
             norm.children.push(...rationGLJElements);
             // 子目单价计算(定额计算程序)
@@ -2577,6 +2606,60 @@ const XMLStandard = (function () {
             return taxEle;
         }
 
+        // 建设项目人材机汇总的映射表
+        // 映射除了消耗量的字段,字段与字段间用特殊符号隔开,并作为映射的key,消耗量作为value
+        // 当除了消耗量和用消耗量关联计算的数据以外的字段都相等(包括组成物数据)时,认为是相同的人材机,不汇总消耗量,否则汇总消耗量
+        const allGLJMap = {};
+        const splitStr = '{@}'; // 字段间分割符
+        const splitKeys = [
+            'code', 'name', 'specs', 'unit', 'riskCoe', 'standardPrice',
+            'basePrice', 'marketPrice', 'varWeight', 'FO', 'FI', 'kind', 'isConcrete',
+            'isMainMaterial', 'isProvisionalMaterial', 'provider', 'delivery', 'location',
+            'producingArea', 'supplier', 'character', 'remark'
+        ];
+        // 合并人材机汇总数据(合并消耗量)
+        function mergeGLJSummary(gljSummaryItem) {
+            const connectKey = splitKeys.reduce((acc, key, index) => {
+                return `${acc}${index === 0 ? '' : splitStr}${gljSummaryItem[key]}`;
+            }, '');
+            gljSummaryItem.quantity = +gljSummaryItem.quantity; // 防止汇总的时候变成字符串拼接
+            if (!allGLJMap[connectKey]) {
+                // 不需要合并组成物,以第一条人材机的组成物为准
+                allGLJMap[connectKey] = { serialNo: Object.keys(allGLJMap).length, quantity: gljSummaryItem.quantity, ratios: gljSummaryItem.ratios };
+            } else {
+                allGLJMap[connectKey].quantity = scMathUtil.roundForObj(allGLJMap[connectKey].quantity + gljSummaryItem.quantity, Decimal.GLJ);
+            }
+        }
+        // 加载建设项目人材机汇总数据
+        function loadConstructionGLJSummary() {
+            const gljList = [];
+            Object
+                .entries(allGLJMap)
+                .forEach(([connectKey, { serialNo, quantity, ratios }]) => {
+                    const glj = connectKey
+                        .split(splitStr)
+                        .reduce((obj, value, index) => {
+                            const attr = splitKeys[index];
+                            obj[attr] = value;
+                            return obj;
+                        }, {});
+                    glj.quantity = quantity;
+                    glj.serialNo = serialNo;
+                    glj.ratios = ratios;
+                    gljList.push(glj);
+                });
+            return gljList
+                .sort((a, b) => a.serialNo - b.serialNo)
+                .map(gljSummaryItem => {
+                    const gljEle = new LabourMaterialsEquipmentsMachinesSummary(gljSummaryItem);
+                    gljEle.children = gljSummaryItem.ratios.map(ratio => {
+                        const noCost = !+ratio.consumption;
+                        return new LabourMaterialsEquipmentsMachinesElement(ratio, ratio.consumption, noCost);
+                    });
+                    return gljEle;
+                });
+        }
+
         // 加载人材机汇总
         function loadGLJSummary() {
             // 所有人材机
@@ -2589,21 +2672,70 @@ const XMLStandard = (function () {
             const engineeringCostNode = tenderDetail.Bills.tree.roots.find(node => node.getFlag() === fixedFlag.ENGINEERINGCOST);
             const ecTotalFee = _util.getFee(engineeringCostNode.data.fees, 'common.totalFee');
             const coeDatas = materialAdjustObj.getPriceCoeDatas(gljList, contractorList, ecTotalFee, decimalObj);
-            return gljList.map(glj => {
-                // 人材机
-                const price = gljUtil.getGLJPrice(glj, tenderDetail.projectGLJ.datas, // 价格信息
-                    tenderDetail.projectInfo.property.calcOptions, tenderDetail.labourCoe.datas, tenderDetail.projectInfo.property.decimal, false, _, scMathUtil);
-                // 人材机对应的造价信息差额数据
-                const infoData = infoDatas.find(item => item.projectGLJID === glj.id);
-                // 人材机对应的价格指数数据
-                const coeData = coeDatas.find(item => item.projectGLJID === glj.id);
-                const gljEle = new LabourMaterialsEquipmentsMachinesSummary(glj, price, infoData, coeData);
-                // 人材机组成物
-                const connectKey = gljUtil.getIndex(glj, gljKeyArray);
-                const ratios = tenderDetail.projectGLJ.datas.mixRatioMap[connectKey] || [];
-                gljEle.children = ratios.map(ratio => new LabourMaterialsEquipmentsMachinesElement(ratio, ratio.consumption));
-                return gljEle;
-            });
+            return gljList
+                .filter(glj => !!+glj.quantity) // 总消耗量为0不导出
+                .map(glj => {
+                    // 人材机
+                    const price = gljUtil.getGLJPrice(glj, tenderDetail.projectGLJ.datas, // 价格信息
+                        tenderDetail.projectInfo.property.calcOptions, tenderDetail.labourCoe.datas, tenderDetail.projectInfo.property.decimal, false, _, scMathUtil);
+                    // 人材机对应的造价信息差额数据
+                    const infoData = infoDatas.find(item => item.projectGLJID === glj.id);
+                    // 人材机对应的价格指数数据
+                    const coeData = coeDatas.find(item => item.projectGLJID === glj.id);
+                    const gljSummaryItem = getGLJSummaryItem(glj, price, infoData, coeData);
+                    const gljEle = new LabourMaterialsEquipmentsMachinesSummary(gljSummaryItem);
+                    // 人材机组成物
+                    const connectKey = gljUtil.getIndex(glj, gljKeyArray);
+                    const ratios = tenderDetail.projectGLJ.datas.mixRatioMap[connectKey] || [];
+                    gljSummaryItem.ratios = ratios;
+                    gljEle.children = ratios.map(ratio => {
+                        const noCost = !+ratio.consumption;
+                        return new LabourMaterialsEquipmentsMachinesElement(ratio, ratio.consumption, noCost);
+                    });
+                    mergeGLJSummary(gljSummaryItem); // 合并人材机消耗量,为输出建设项目表下的人材机汇总做准备
+                    return gljEle;
+                });
+        }
+
+        // 建设项目评标材料的映射表
+        // 映射除了消耗量的字段,字段与字段间用特殊符号隔开,并作为映射的key,消耗量作为value
+        const allBidEvalMap = {};
+        const bidEvalSplitKeys = [
+            'seq', 'code', 'name', 'specs', 'unit', 'marketPrice', 'delivery',
+            'delivery_address', 'originPlace', 'vender', 'qualityGrace', 'remark'
+        ];
+        // 合并数据(合并消耗量)
+        function mergeBidEval(glj) {
+            const connectKey = bidEvalSplitKeys.reduce((acc, key, index) => {
+                return `${acc}${index === 0 ? '' : splitStr}${glj[key]}`;
+            }, '');
+            glj.quantity = +glj.quantity; // 防止汇总的时候变成字符串拼接
+            if (!allBidEvalMap[connectKey]) {
+                allBidEvalMap[connectKey] = { serialNo: Object.keys(allBidEvalMap).length, quantity: glj.quantity };
+            } else {
+                allBidEvalMap[connectKey].quantity = scMathUtil.roundForObj(allBidEvalMap[connectKey].quantity + glj.quantity, Decimal.GLJ);
+            }
+        }
+        // 加载建设项目评标材料数据
+        function loadConstructionBidEval() {
+            const gljList = [];
+            Object
+                .entries(allBidEvalMap)
+                .forEach(([connectKey, { serialNo, quantity }]) => {
+                    const glj = connectKey
+                        .split(splitStr)
+                        .reduce((obj, value, index) => {
+                            const attr = bidEvalSplitKeys[index];
+                            obj[attr] = value;
+                            return obj;
+                        }, {});
+                    glj.quantity = quantity;
+                    glj.serialNo = serialNo;
+                    gljList.push(glj);
+                });
+            return gljList
+                .sort((a, b) => a.serialNo - b.serialNo)
+                .map(glj => new BidEvaluationMainMaterial(glj));
         }
 
         // 加载评标材料
@@ -2621,6 +2753,21 @@ const XMLStandard = (function () {
                     glj.vender = projectGLJ.vender;
                     glj.qualityGrace = projectGLJ.qualityGrace;
                 }
+                const toMergeGLJ = _.cloneDeep(glj);
+                // 需要提前处理空值的情况,将合并数据需要判断的字段值进行空值统一处理,防止空值值不同(null, undefined, '')造成连接key判断错误
+                toMergeGLJ.seq = toMergeGLJ.seq || '';
+                toMergeGLJ.code = toMergeGLJ.code || '';
+                toMergeGLJ.name = toMergeGLJ.name || '';
+                toMergeGLJ.specs = toMergeGLJ.specs || '';
+                toMergeGLJ.unit = toMergeGLJ.unit || '';
+                toMergeGLJ.marketPrice = toMergeGLJ.marketPrice || '0';
+                toMergeGLJ.delivery = toMergeGLJ.delivery || '';
+                toMergeGLJ.delivery_address = toMergeGLJ.delivery_address || '';
+                toMergeGLJ.originPlace = toMergeGLJ.originPlace || '';
+                toMergeGLJ.vender = toMergeGLJ.vender || '';
+                toMergeGLJ.qualityGrace = toMergeGLJ.qualityGrace || '';
+                toMergeGLJ.remark = toMergeGLJ.remark || '';
+                mergeBidEval(toMergeGLJ);
                 return new BidEvaluationMainMaterial(glj);
             });
         }

+ 27 - 17
web/over_write/js/guangdong_2018_import.js

@@ -70,6 +70,11 @@ const importXML = (() => {
     // 工程量表达式相关
     const GCLMXHj = 'GCLMXHJ';
     const QDL = 'QDL';
+    // 工程量表达式累加映射
+    const summationMap = {
+        '1': 1, // 累加
+        '2': 0 // 不累加
+    };
 
     /* 
      * 从导入的xml文件中提取有用的数据
@@ -218,10 +223,10 @@ const importXML = (() => {
         }));
         clearEmptyItems(featureData);
         return featureData;
-        
+
         function clearEmptyItems(items) {
             items.forEach(item => {
-                if(!item.items) {
+                if (!item.items) {
                     return;
                 }
                 if (!item.items.length) {
@@ -237,7 +242,7 @@ const importXML = (() => {
         const summarySrc = getValue(tenderSrc, ['UnitWorksSummary']);
         const fields = [['UnitWorksSummaryGroup'], ['UnitWorksSummaryItem']];
         return extractItemsRecur(summarySrc, fields, (src, curField) => {
-            const item = { 
+            const item = {
                 code: getValue(src, ['_Number']),
                 name: getValue(src, ['_Name']),
                 quantity: getValue(src, ['_Quantity']),
@@ -410,7 +415,7 @@ const importXML = (() => {
             seq: getValue(itemSrc, ['_OrderNumber']),
             regex: getValue(itemSrc, ['_Express']),
             result: getValue(itemSrc, ['_Quantity']),
-            isSummation: +getValue(itemSrc, ['_Kind'])
+            isSummation: summationMap[getValue(itemSrc, ['_Kind'])]
         }));
     }
     // 提取定额
@@ -457,14 +462,14 @@ const importXML = (() => {
             const calculationOfItems = arrayValue(rationSrc, ['UnitPriceCalculationOfItem']);
             const fees = [];
             const codeFiedNameMap = {
-                'DEZJF': 'direct',
+                'ZJF': 'direct',
                 'RGF': 'labour',
                 'CLF': 'material',
                 'JXF': 'machine',
                 'ZCF': 'mainMaterial',
                 'SBF': 'equipment',
                 'LR': 'profit',
-                'DJ': 'common',
+                //'DJ': 'common',定额本身已经有单价这个字段,不需要从这读取
 
             };
             calculationOfItems.forEach(item => {
@@ -549,24 +554,29 @@ const importXML = (() => {
             visa: extractData(sundry, 'SiteInstructionCost', [['SiteInstructionCostGroup'], ['SiteInstructionCostItem']]),
         };
         // 提取标题和明细
-        function extractGroupOrItem(itemSrc, isGroup = true, extendAttrs = null) {
-            const source = isGroup
-                ? {
+        function extractGroupOrItem(itemSrc, isGroup = true, extendAttrs = null, field) {
+            let source;
+            if (isGroup) {
+                source = {
                     name: getValue(itemSrc, ['_Name']),
                     fees: importFileKind === FileKind.tender ? [{ fieldName: 'common', totalFee: getValue(itemSrc, ['_Total']) }] : [],
                     feeCode: getValue(itemSrc, ['_Code']),
                     remark: getValue(itemSrc, ['_Remark'])
-                }
-                : {
+                };
+            } else {
+                source = {
                     name: getValue(itemSrc, ['_Name']),
                     unit: getValue(itemSrc, ['_Unit']),
-                    quantity: getValue(itemSrc, ['_Quantity']),
                     calcBase: getValue(itemSrc, ['_QtyFormula']),
                     feeRate: getValue(itemSrc, ['_Rate']),
                     fees: importFileKind === FileKind.tender ? [{ fieldName: 'common', unitFee: getValue(itemSrc, ['_Price']), totalFee: getValue(itemSrc, ['_Total']) }] : [],
                     feeCode: getValue(itemSrc, ['_Code']),
                     remark: getValue(itemSrc, ['_Remark'])
                 };
+                if (field !== 'MainContractorAttendanceItem') { // MainContractorAttendanceItem的quantity比较特殊,是清单基数计算后的值而不是工程量,不需要导入
+                    source.quantity = getValue(itemSrc, ['_Quantity']);
+                }
+            }
             return extendAttrs ? Object.assign(source, extendAttrs) : source;
         }
         // 提取数据
@@ -589,8 +599,8 @@ const importXML = (() => {
                         }, {})
                         : null;
                     return curField[0] === fields[0][0]
-                        ? extractGroupOrItem(itemSrc, true, groupExtend)
-                        : extractGroupOrItem(itemSrc, false, itemExtend);
+                        ? extractGroupOrItem(itemSrc, true, groupExtend, curField[0])
+                        : extractGroupOrItem(itemSrc, false, itemExtend, curField[0]);
                 })
             };
         }
@@ -715,7 +725,7 @@ const importXML = (() => {
         // 提取造价信息差额调整法
         function extractDifferentiaGLJ(gljSrc) {
             // 风险系数或者基准单价有值,才算一条差额数据
-            const riskCoe = getValue(gljSrc, ['_ProviderExp']);
+            const riskCoe = getValue(gljSrc, ['_ProviderRate']);
             const standardPrice = getValue(gljSrc, ['_ProviderBase']);
             if ((riskCoe && riskCoe !== '0') || (standardPrice && standardPrice !== '0')) {
                 return {
@@ -1208,7 +1218,7 @@ const importXML = (() => {
                 item.ID = uuid.v1();
                 item[typeKey] = refID; // billID、rationID
                 item.projectID = tenderID;
-            }); 
+            });
         }
     }
     // 转换人材机汇总相关数据(人材机汇总、承包人材料、评标材料、暂估价材料、组成物、单价文件)
@@ -1474,7 +1484,7 @@ const importXML = (() => {
             ration,
             rationGLJ,
             rationCoe,
-            quantityDetails: [...billsQuantityDetails, ... rationQuantityDetails],
+            quantityDetails: [...billsQuantityDetails, ...rationQuantityDetails],
             ...relatedljGLJData
         };
         clean(detailData);