Browse Source

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

Conflicts:
	web/building_saas/main/js/models/calc_base.js
zhangweicheng 5 years ago
parent
commit
72a3388137

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

@@ -617,7 +617,7 @@ module.exports = {
         try {
             const { count, projectID } = JSON.parse(req.body.data);
             // 分享建设项目,仅提供给专业版用户
-            const proShareProjType = [projType.folder, projType.project];
+            /* const proShareProjType = [projType.folder, projType.project];
             const project = await projectModel.findOne({ ID: projectID }, 'projType');
             if (project && proShareProjType.indexOf(project.projType) >= 0) {
                 const isFree = userModelObj.isFreeFromSession(req.session.compilationVersion);
@@ -625,7 +625,7 @@ module.exports = {
                     callback(req, res, 0, 'success', { isFree });
                     return;
                 }
-            }
+            } */
             const userID = req.session.sessionUser.id;
             // 最近分享
             const recentUsers = await pm_facade.getRecentShareList(userID, count);

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

@@ -789,7 +789,7 @@ var sheetCommonObj = {
         }
         return new getTipsCombo();
     },
-    getTreeNodeCellType: function (datas, row, parentMap,treeCol) {// 2018-09-26  不用spreadjs默认的树结构,自定义控件
+    getTreeNodeCellType: function (datas, row, parentMap,treeCol, paintFunc) {// 2018-09-26  不用spreadjs默认的树结构,自定义控件
         var ns = GC.Spread.Sheets;
         let rectW = 10;
         let rectH = 10;
@@ -802,6 +802,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开始,取当前节点是第几级的

+ 5 - 1
web/building_saas/main/js/models/calc_base.js

@@ -725,7 +725,7 @@ let baseFigureTemplate = {
     },
     //{专项暂定合计}
     // 汇总专项暂定列有值的清单的金额
-    'ZXZDHJ': function (tender) {
+        /* 'ZXZDHJ': function (tender) {
       let rst = 0,
         feeField = 'common',
         subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
@@ -741,6 +741,10 @@ let baseFigureTemplate = {
         rst += data.feesIndex[feeField][subFeeField];
       }
       return rst.toDecimal(decimalObj.bills.totalPrice);
+        }, */
+        // 第100章至700章清单行的暂估合价
+        'ZXZDHJ': function (tender) {
+            return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'estimate');
     },
     //{100章以外清单合计}
     // 取清单固定清单[第100章至700章清单]的金额,但扣除清单100章下的金额。

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

@@ -369,7 +369,7 @@ let MainTreeCol = {
         specialProvisional: function (node) {
             if(!MainTreeCol.readOnly.specialProvisional(node)){
                 let dynamicCombo = sheetCommonObj.getDynamicCombo();
-                dynamicCombo.items(["材料","工程设备","专业工程",""]);
+                dynamicCombo.items(["专业工程",""]);
                 return dynamicCombo;
             }
 

+ 111 - 63
web/building_saas/main/js/views/project_property_basicInfo.js

@@ -19,10 +19,10 @@ let basicInfoView = {
             allowUserDragFill: false,
             scrollbarMaxAlign : true
         },
-        dateRows: [5, 18],
-        numRows: [7],
+        dateRows: [],
+        numRows: [],
         locked: {
-            rows: [0, 19, 23, 27],
+            rows: [],
             cols: [0]
         }
     },
@@ -70,6 +70,8 @@ let basicInfoView = {
             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);
@@ -91,6 +93,7 @@ let basicInfoView = {
         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);
     },
@@ -102,15 +105,28 @@ let basicInfoView = {
         let fuc = function () {
             sheet.setRowCount(datas.length);
             me.initTree(sheet, true, datas);
-            me.setDatePicker(sheet, me.setting.dateRows);
             sheet.setFormatter(-1, 1, '@');
-            for(let col = 0, cLen = cols.length; col < cLen; col++){
-                sheet.getRange(-1, col, -1, 1).hAlign(GC.Spread.Sheets.HorizontalAlign[cols[col]['hAlign']]);
-                sheet.getRange(-1, col, -1, 1).vAlign(GC.Spread.Sheets.VerticalAlign[cols[col]['vAlign']]);
-                for(let row = 0, rLen = datas.length; row < rLen; row++){
+            //兼容旧数据,旧数据可能没有设置cellType
+            let compatNumKeys = ['grossArea'],
+                compatDateKeys = ['constructionDate', 'establishDate'];
+            for(let row = 0;row < datas.length ; row ++){
+                if(datas[row].cellType == 'number' || compatNumKeys.includes(datas[row].key)){
+                    me.setting.numRows.push(row);
+                } else if (datas[row].cellType === 'date' || compatDateKeys.includes(datas[row].key)) {
+                    me.setting.dateRows.push(row);
+                } else if(datas[row].cellType === 'comboBox'){
+                    let options = datas[row].options ? datas[row].options.split("@") : [];
+                    projFeatureView.setCombo(sheet, row, options);
+                }
+                let readOnly = typeof datas[row].readOnly === 'string' ? JSON.parse(datas[row].readOnly) : datas[row].readOnly;
+                if (readOnly || datas[row].items) {
+                    me.setting.locked.rows.push(row);
+                }
+                for(let col = 0;col < cols.length;col++){
                     sheet.setValue(row, col, datas[row][cols[col]['dataCode']]);
                 }
             }
+            me.setDatePicker(sheet, me.setting.dateRows);
         };
         this.renderSheetFuc(sheet, fuc);
     },
@@ -120,8 +136,7 @@ let basicInfoView = {
         if(args.col === 0){
             args.cancel = true;
         }
-        //工程专业
-        if(args.col === 1 && me.setting.locked.rows.indexOf(args.row) !== -1){
+        if(me.setting.locked.rows.indexOf(args.row) !== -1){
             args.cancel = true;
         }
     },
@@ -130,8 +145,12 @@ let basicInfoView = {
         let me = basicInfoView;
         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;
             //date
-            if(me.setting.dateRows.indexOf(args.row) !== -1){
+            if (required && !v) {
+                v = 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 = me.filtDate(v);
@@ -141,8 +160,7 @@ let basicInfoView = {
                         return;
                     }
                 }
-            }
-            else if(me.setting.numRows.indexOf(args.row) !== -1){//控制数值
+            } 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 : '';
@@ -153,6 +171,10 @@ let basicInfoView = {
         }
     },
 
+    onEnterCell: function (sender, args) {
+        args.sheet.repaint();
+    },
+
     onClipboardPasting: function (sender, args) {
         let me = basicInfoView;
         if(args.cellRange.col === 0){
@@ -166,23 +188,25 @@ let basicInfoView = {
         let recRows = [];
         for(let i = 0, len = items.length; i < len; i++){
             let row = i + args.cellRange.row;
+            let comboItems = projFeatureView.getComboItemsByRow.call(me, 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(me.setting.dateRows.indexOf(row) !== -1){
+            } else if(comboItems && !comboItems.includes(items[i].value)){//粘贴下拉框数据过滤
+                recRows.push(row);
+            } else if(me.setting.dateRows.indexOf(row) !== -1){
                 items[i].value = me.filtDate(items[i].value);
                 if(!me.isDef(items[i].value)){
                     recRows.push(row);
-                }
-                else {
+                } else {
                     me.datas[row].value = items[i].value;
                 }
-            }
-            else if(me.setting.numRows.indexOf(row) !== -1 && !me.isNum(items[i].value)){
+            } else if(me.setting.numRows.indexOf(row) !== -1 && !me.isNum(items[i].value)){
                 recRows.push(row);
-            }
-            else {
-                me.datas[row].value = items[i].value;
+            } else if (required && !items[i].value) {
+                recRows.push(row);
+            } else
+                {me.datas[row].value = items[i].value;
             }
         }
         if(recRows.length > 0){
@@ -191,7 +215,7 @@ let basicInfoView = {
                     let staticV = me.datas[recRows[i]].value || '';
                     args.sheet.setValue(recRows[i], args.cellRange.col, staticV);
                 }
-            })
+            });
         }
     },
 
@@ -219,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) {
@@ -288,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, 0, paint))
         }
     },
 
@@ -406,7 +450,7 @@ let basicInfoView = {
             if(recode&&recode.hasOwnProperty('items')){
                 //方框外1像素内都有效
                 const boxLengh = 10;
-                if (hitinfo.x >= centerX - halfBoxLength - 2 && hitinfo.x <= centerX + halfBoxLength + 1 &&
+                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
@@ -473,8 +517,7 @@ let basicInfoView = {
 };
 
 $(document).ready(function () {
-    //暂时隐藏
-   $('#poj-set').on('shown.bs.modal', function (e) {
+    $('#poj-set').on('shown.bs.modal', function (e) {
         //init Spread
         basicInfoView.initDatas(basicInfoView.orgDatas);
         basicInfoView.buildSheet();
@@ -494,10 +537,15 @@ $(document).ready(function () {
     });
 
     $('#tab_poj-settings-basicInfo').on('shown.bs.tab', function () {
-        basicInfoView.workBook.refresh();
+        sheetCommonObj.refreshWorkbookDelDefer(basicInfoView.workBook, 100);
     });
     $('#openProjSet').click(function () {
         $('[data-toggle="tooltip"]').tooltip('hide');
         $('#poj-set').modal('show');
-    });
+    })
+   /* $('#property_ok').bind('click', function () {
+        if(basicInfoView.toUpdate(basicInfoView.orgDatas, basicInfoView.datas)){
+            basicInfoView.a_updateInfo(basicInfoView.toSaveDatas(basicInfoView.datas));
+        }
+    });*/
 });

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

@@ -60,6 +60,14 @@ let tender_obj={
                 newNode.source = mainNode.source;
                 newNode.sourceType = mainNode.sourceType;
                 newNode.mainNode = mainNode;
+
+                // 只显示到叶子清单层
+                if (calcTools.isLeafBill(newNode))
+                    newNode.expanded = false;
+
+                if (calcTools.isRationCategory(newNode))
+                    newNode.visible = false;
+
                 if (mainNode.children.length > 0) {
                     for (let c of mainNode.children) {
                         createTenderNode(c, newNode, null);

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

@@ -797,10 +797,10 @@ const pmShare = (function () {
                       return !(selected && selected.data.allowCopy && selected.data.projType === projectType.project);
                   },
                   callback: function (key, opt) {
-                      if($(".p-title").text().includes('学习')){
+                      /* if($(".p-title").text().includes('学习')){
                         hintBox.versionBox('此功能仅在专业版中提供,学习版可选择单个分段进行拷贝。');
                         return;
-                      }
+                      } */
                       copyContructionProject(tree.selected);
                   }
                 },

+ 8 - 0
web/building_saas/standard_interface/config.js

@@ -55,6 +55,14 @@ const INTERFACE_CONFIG = (() => {
         [CONTROL]: '.XCGLKZJ',
       },
     },
+    '安徽@池州': {
+      scriptName: 'anhui_chizhou.js',
+      fileSuffix: {
+          [BID_INVITATION]: '.CZGLZB',
+          [BID_SUBMISSION]: '.CZLTB',
+          [CONTROL]: '.CZGLZBKZJ',
+      }
+    },
     '广东@中山': {
       scriptName: 'guangdong_zhongshan.js',
       fileSuffix: {

+ 749 - 0
web/building_saas/standard_interface/export/anhui_chizhou.js

@@ -0,0 +1,749 @@
+
+INTERFACE_EXPORT = (() => {
+    'use strict';
+
+    /**
+     * 
+     * @param {String} areaKey - 地区标识,如:'安徽@马鞍山',有些地区的接口只是取值上有不同,共有一个接口脚本, 需要通过地区标识确定一些特殊处理
+     * @param {Number} exportKind - 导出类型,招标、投标、控制价
+     * @param {Object} projectData - 项目表数据:{ 建设项目Data, children: [单位工程...] }
+     * @param {Object} tenderDetailMap - 单位工程ID与getData接口数据(projectObj.project的结构)的映射。
+     * @return {Promise<Array>} - 返回的数据结构必须按照规定:[{ data, exportKind, fileName }],参考web\building_saas\standard_interface\index.js中的注释说明
+     */
+    async function entry(areaKey, exportKind, projectData, tenderDetailMap) {
+        const {
+            CONFIG: { 
+                TYPE, 
+                WHITE_SPACE
+            },
+            UTIL: {
+                getValueByKey,
+                getHan,
+                getFee,
+                generateHardwareId,
+            },
+            Element,
+        } = INTERFACE_EXPORT_BASE;
+
+        const {
+            EXPORT_KIND: { BID_INVITATION, BID_SUBMISSION, CONTROL },
+            fixedFlag,
+            RationType,
+        } = window.commonConstants
+
+        const GljType = gljUtil.gljType;
+
+        const { isEmptyVal, isDef } = window.commonUtil;
+
+        const isBidInvitation = exportKind === BID_INVITATION; // 是否是招标
+        const isBidSubmission = exportKind === BID_SUBMISSION; // 是否是投标
+        const isControl = exportKind === CONTROL; // 是否是控制价
+
+        // 节点定义--------------------------------
+
+        // 建设项目基本信息
+        function JingJiBiao(projectName, information) {
+            const taxModeMap = {
+                '一般计税': '1',
+                '简易计税': '2',
+            };
+            const czzt = {
+                [BID_INVITATION]: '招标',
+                [BID_SUBMISSION]: '投标',
+                [CONTROL]: '招标控制',
+            };
+            const attrs = [
+                { name: 'Xmbh', value: getValueByKey(information, 'projNum') , minLen: 1, whiteSpace: WHITE_SPACE.COLLAPSE }, // 项目编号,招标xsd中有规定minLen whiteSpace,这里投标招标统一处理
+                { name: 'Xmmc', value: projectName }, // 项目名称
+                { name: 'Bzlx', value: '清单' }, // 项目编制类型
+                { name: 'Jjyj', value: '【18清单】2018部颁清单计价依据' }, // 计价依据
+                { name: 'Xmqzzh', value: getValueByKey(information, 'startAndChainages') }, // 项目起止桩号
+                { name: 'Jsdw', value: getValueByKey(information, 'constructingUnits') }, // 建设单位
+                { name: 'Czzt', value: czzt[exportKind] }, // 操作状态:招标、投标、招标控制,即导出接口时,所选的文件类型
+                { name: 'Jsfs', value: taxModeMap[getValueByKey(information, 'taxMode')] || '1', type: TYPE.INT }, // 计税方式,默认1。1=一般计税 2=简易计税
+                { name: 'Version', value: '1.0' },
+            ];
+            Element.call(this, 'JingJiBiao', attrs);
+        }
+
+        const subArea = areaKey.split('@')[1];
+
+        // 招标信息
+        function ZhaoBiaoXx(information) {
+            const attrs = [
+                { name: 'Zbr', value: getValueByKey(information, 'tendereeName') }, // 招标人
+                { name: 'Zxr', value: getValueByKey(information, 'costConsultant') }, // 造价咨询人
+                { name: 'ZbrDb', value: getValueByKey(information, 'tenderAuthorizer') }, // 招标人法定代表人或其授权人
+                { name: 'ZxrDb', value: getValueByKey(information, 'consultantAuthorizer') }, // 造价咨询人法定代表人或其授权人
+                { name: 'Bzr', value: getValueByKey(information, 'tenderCompiler') }, // 编制人
+                { name: 'Fhr', value: getValueByKey(information, 'tenderExaminer') }, // 复核人
+                { name: 'BzTime', value: getValueByKey(information, 'compilationTime'), type: TYPE.DATE }, // 编制时间
+                { name: 'FhTime', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE }, // 复核时间
+            ];
+            // 额外字段
+            const extraMap = {
+                '淮北': [
+                    { name: 'ZbrNssbh', value: getValueByKey(information, 'tendereeTaxpayerIdentificationNo') }, // 招标人纳税识别号
+                    { name: 'ZxrNssbh', value: getValueByKey(information, 'costConsultantTaxpayerIdentificationNo') }, // 造价咨询人纳税识别号
+                    { name: 'ZbrDbSfzh', value: getValueByKey(information, 'tenderAuthorizerIDNo') }, // 招标人法定代表人或其授权人身份证号
+                    { name: 'ZxrNssbh', value: getValueByKey(information, 'consultantAuthorizerTaxpayerIdentificationNo') }, // 造价咨询人法定代表或其授权人纳税识别号
+                ]
+            };
+            if (extraMap[subArea]) {
+                attrs.push(...extraMap[subArea]);
+            }
+            Element.call(this, 'ZhaoBiaoXx', attrs);
+        }
+
+        // 招标控制价信息
+        function ZhaoBiaoKzXx(information, totalCost) {
+            const attrs = [
+                { name: 'Zbr', value: getValueByKey(information, 'tendereeName') }, // 招标人
+                { name: 'Zxr', value: getValueByKey(information, 'costConsultant') }, // 造价咨询人
+                { name: 'ZbrDb', value: getValueByKey(information, 'tenderAuthorizer') }, // 招标人法定代表人或其授权人
+                { name: 'ZxrDb', value: getValueByKey(information, 'consultantAuthorizer') }, // 造价咨询人法定代表人或其授权人
+                { name: 'Bzr', value: getValueByKey(information, 'tenderCompiler') }, // 编制人
+                { name: 'Fhr', value: getValueByKey(information, 'tenderExaminer') }, // 复核人
+                { name: 'BzTime', value: getValueByKey(information, 'compilationTime'), type: TYPE.DATE }, // 编制时间
+                { name: 'FhTime', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE }, // 复核时间
+                { name: 'Zbkzj', value: totalCost, type: TYPE.DECIMAL }, // 控制价总价(元),取“投标报价”的金额。
+            ];
+            // 额外字段
+            const extraMap = {
+                '淮北': [
+                    { name: 'ZbrNssbh', value: getValueByKey(information, 'tendereeTaxpayerIdentificationNo') }, // 招标人纳税识别号
+                    { name: 'ZxrNssbh', value: getValueByKey(information, 'costConsultantTaxpayerIdentificationNo') }, // 造价咨询人纳税识别号
+                    { name: 'ZbrDbSfzh', value: getValueByKey(information, 'tenderAuthorizerIDNo') }, // 招标人法定代表人或其授权人身份证号
+                    { name: 'ZxrNssbh', value: getValueByKey(information, 'consultantAuthorizerTaxpayerIdentificationNo') }, // 造价咨询人法定代表或其授权人纳税识别号
+                ]
+            };
+            if (extraMap[subArea]) {
+                attrs.push(...extraMap[subArea]);
+            }
+            Element.call(this, 'ZhaoBiaoKzXx', attrs);
+        }
+
+        // 招标控制价信息
+        function TouBiaoXx(information, totalCost) {
+            const attrs = [
+                { name: 'Zbr', value: getValueByKey(information, 'tendereeName') }, // 招标人
+                { name: 'Tbr', value: getValueByKey(information, 'bidder') }, // 投标人
+                { name: 'TbrDb', value: getValueByKey(information, 'bidderAuthorizer') }, // 投标人法定代表或其授权
+                { name: 'Bzr', value: getValueByKey(information, 'tenderCompiler') }, // 编制人
+                { name: 'BzTime', value: getValueByKey(information, 'compilationTime'), type: TYPE.DATE }, // 编制时间
+                { name: 'Tbzj', value: totalCost, type: TYPE.DECIMAL }, // 控制价总价(元),取“投标报价”的金额。
+            ];
+            // 额外字段
+            const extraMap = {
+                '淮北': [
+                    { name: 'ZbrNssbh', value: getValueByKey(information, 'tendereeTaxpayerIdentificationNo') }, // 招标人纳税识别号
+                    { name: 'TbrNssbh', value: getValueByKey(information, 'bidderTaxpayerIdentificationNo') }, // 投标人纳税识别号
+                    { name: 'TbrDbsfzh', value: getValueByKey(information, 'tenderAuthorizerIDNo') }, // 投标人法定代表或其授权人身份证号
+                ]
+            };
+            if (extraMap[subArea]) {
+                attrs.push(...extraMap[subArea]);
+            }
+            Element.call(this, 'TouBiaoXx', attrs);
+        }
+
+        // 单项工程信息,因项目管理中无“单项工程”这一层,从单位工程的工程特征信息中拼凑出来
+        function Dxgcxx(code, name) {
+            const attrs = [
+                { name: 'Dxgcbh', value: code, minLen: 1, whiteSpace: WHITE_SPACE.COLLAPSE }, // 单项工程编号
+                { name: 'Dxgcmc', value: name }, // 单项工程名称
+            ];
+            Element.call(this, 'Dxgcxx', attrs);
+        }
+
+        // 单位工程信息
+        function Dwgcxx(tenderName, feature) {
+            const attrs = [
+                { name: 'Dwgcbh', value: getValueByKey(feature, 'unitProjNo'), minLen: 1, whiteSpace: WHITE_SPACE.COLLAPSE }, // 单位工程编号
+                { name: 'Dwgcmc', value: tenderName }, // 单位工程名称
+            ];
+            Element.call(this, 'Dwgcxx', attrs);
+        }
+
+        // 取费信息(费率信息)
+        function Qfxx() {
+            Element.call(this, 'Qfxx');
+        }
+
+        // 计价费率表
+        function JjFlb() {
+            Element.call(this, 'JjFlb');
+        }
+
+        // 计价费率表明细,造价书费率页面左侧最底层数据
+        function JjFlbMx(rootItem, item) {
+            // 费率明细名称-编码映射表
+            const FeeRateCodeMap = {
+                '冬季施工增加费': 'DJF',
+                '雨季施工增加费': 'YJF',
+                '夜间施工增加费': 'YEF',
+                '工地转移费': 'ZYF',
+                '高原地区施工增加费': 'GYF',
+                '风沙地区施工增加费': 'FSF',
+                '沿海地区施工增加费': 'YHF',
+                '行车干扰施工增加费': 'XCF',
+                '施工辅助费': 'SFF',
+                '养老保险费': 'YLF',
+                '失业保险费': 'SYF',
+                '医疗保险费': 'YBF',
+                '住房公积金': 'ZFF',
+                '工伤保险费': 'GSF',
+                '基本费用': 'JBF',
+                '主副食运费补贴': 'YFF',
+                '职工探亲补贴': 'TQF',
+                '职工取暖补贴': 'QNF',
+                '财务费用': 'CWF',
+                '利润': 'LR',
+                '税金': 'SJ',
+            };
+            // 费率工程名称-取费类别映射表
+            const FeeRateTypeMap = {
+                '土方': 1,
+                '石方': 3,
+                '运输': 2,
+                '路面': 4,
+                '隧道': 11,
+                '构造物Ⅰ': 5,
+                '构造物Ⅰ(不计冬)': 16,
+                '构造物Ⅱ': 6,
+                '构造物Ⅲ(桥梁)': 9,
+                '构造物Ⅲ(除桥以外不计雨夜)': 8,
+                '技术复杂大桥': 10,
+                '钢材及钢结构(桥梁)': 12,
+                '钢材及钢结构(除桥以外不计夜)': 13,
+                '费率为0': 17,
+                '路面(不计雨)': 4,
+                '构造物Ⅰ(不计雨)': 16,
+                '构造物Ⅲ(除桥以外)': 8,
+                '钢材及钢结构(除桥以外)': 13,
+                '设备': 15,
+                '量价': 14,
+            };
+            const rate = isEmptyVal(item.rate) ? '100' : item.rate; // 为空时输出=100,为0时输出=0
+            /* if (!FeeRateTypeMap[rootItem.name]) {
+                debugger;
+            } */
+            const attrs = [
+                { name: 'Bm', value: FeeRateCodeMap[item.name] }, // 编码
+                { name: 'Name', value: item.name }, // 名称
+                { name: 'Fl', value: rate, type: TYPE.DECIMAL }, // 费率
+                { name: 'Qflb', value: FeeRateTypeMap[rootItem.name], type: TYPE.INT }, // 取费类别
+            ];
+            Element.call(this, 'JjFlbMx', attrs);
+        }
+
+        // 计价费率项
+        function JjFlx() {
+            Element.call(this, 'JjFlx');
+        }
+
+        // 计价费率项明细,造价书费率页面右侧最顶层数据
+        function JjFlxMx(item) {
+            // 编码取名称拼音首字母
+            const allHanName = getHan(item.name || '');
+            const code = pinyinUtil.getFirstLetter(allHanName).toLowerCase();
+            let value;
+            if (isDef(item.value)) {
+                value = item.value;
+            } else {
+                const selected = item.optionList.find(item => item.selected);
+                value = selected.name;
+            }
+            const attrs = [
+                { name: 'Bm', value: code }, // 编码
+                { name: 'Mc', value: item.name }, // 名称
+                { name: 'ShuZhi', value: value }, // 数值
+            ];
+            Element.call(this, 'JjFlxMx', attrs);
+        }
+
+        // 清单项目
+        function QdXm() {
+            Element.call(this, 'QdXm');
+        }
+        // 标题类别:1=100~700清单合计,2=暂估价合计,3=清单不包含暂估价合计,4=计日工,5=暂列金额(不含计日工总额)),6=投标报价,0=其他
+        const BillsTitleType = {
+            [fixedFlag.ONE_SEVEN_BILLS]: '1',
+            [fixedFlag.PROVISIONAL_TOTAL]: '2',
+            [fixedFlag.BILLS_TOTAL_WT_PROV]: '3',
+            [fixedFlag.DAYWORK_LABOR]: '4',
+            [fixedFlag.PROVISIONAL]: '5',
+            [fixedFlag.TOTAL_COST]: '6',
+        };
+        // 清单标题 造价书的第一层数据。
+        function QdBt(node) {
+            const row = node.row();
+            const fee = isBidInvitation ? '0' : getFee(node.data.fees, 'common.tenderTotalFee')
+            const attrs = [
+                { name: 'Xh', value: row, type: TYPE.INT }, // 序号
+                { name: 'Bm', value: node.data.code }, // 编码
+                { name: 'Name', value: node.data.name }, // 名称
+                { name: 'Je', value: fee, type: TYPE.DECIMAL }, // 金额
+                { name: 'Code', value: `F${row}` }, // 行引用
+                { name: 'Jsgs', value: node.data.calcBase }, // 计算基数
+                { name: 'Lb', value: BillsTitleType[node.getFlag()], type: TYPE.INT }, // 类别
+                { name: 'Bz', value: node.data.remark }, // 备注
+            ];
+            Element.call(this, 'QdBt', attrs);
+        }
+
+        // 清单明细 (只有100-700章清单标题输出)
+        function QdMx(node) {
+            const row = node.row();
+            const attrs = [
+                { name: 'Xh', value: row, type: TYPE.INT }, // 序号
+                { name: 'Qdbm', value: node.data.code, minLen: 1 }, // 编码
+                { name: 'Name', value: node.data.name }, // 名称
+                { name: 'Dw', value: node.data.unit }, // 单位
+                { name: 'Sl', value: node.data.quantity, type: TYPE.DECIMAL }, // 工程量
+                { name: 'Sl2', value: '0', type: TYPE.DECIMAL }, // 工程量2
+                { name: 'Rgf', value: isBidInvitation ? '0' : getFee(node.data.fees, 'marketLabour.tenderTotalFee'), type: TYPE.DECIMAL }, // 人工费
+                { name: 'Clf', value: isBidInvitation ? '0' : getFee(node.data.fees, 'marketMaterial.tenderTotalFee'), type: TYPE.DECIMAL }, // 材料费
+                { name: 'Jxf', value: isBidInvitation ? '0' : getFee(node.data.fees, 'marketMachine.tenderTotalFee'), type: TYPE.DECIMAL }, // 机械费
+                { name: 'Sbf', value: isBidInvitation ? '0' : getFee(node.data.fees, 'marketEquipment.tenderTotalFee'), type: TYPE.DECIMAL }, // 设备费
+                { name: 'Csf', value: isBidInvitation ? '0' : getFee(node.data.fees, 'measure.tenderTotalFee'), type: TYPE.DECIMAL }, // 措施费
+                { name: 'Glf', value: isBidInvitation ? '0' : getFee(node.data.fees, 'manage.tenderTotalFee'), type: TYPE.DECIMAL }, // 企业管理费
+                { name: 'Gf', value: isBidInvitation ? '0' : getFee(node.data.fees, 'force.tenderTotalFee'), type: TYPE.DECIMAL }, // 规费
+                { name: 'Lr', value: isBidInvitation ? '0' : getFee(node.data.fees, 'profit.tenderTotalFee'), type: TYPE.DECIMAL }, // 利润
+                { name: 'Sj', value: isBidInvitation ? '0' : getFee(node.data.fees, 'tax.tenderTotalFee'), type: TYPE.DECIMAL }, // 税金
+                { name: 'Zhdj', value: isBidInvitation ? '0' : getFee(node.data.fees, 'common.tenderUnitFee'), type: TYPE.DECIMAL }, // 单价
+                { name: 'Zhhj', value: isBidInvitation ? '0' : getFee(node.data.fees, 'common.tenderTotalFee'), type: TYPE.DECIMAL }, // 合价
+                { name: 'Zgj', value: isBidInvitation ? '0' : getFee(node.data.fees, 'estimate.tenderTotalFee'), type: TYPE.DECIMAL }, // 暂估价
+                { name: 'Iszg', value: node.data.specialProvisional === '专业工程', type: TYPE.BOOL }, // 是否暂定 如果专项暂定列选择了“专业工程”,则输出true,否则是false。
+                { name: 'Djfx', value: !!node.data.unitPriceAnalysis, type: TYPE.BOOL }, // 单价分析
+                { name: 'Jsgs', value: node.data.calcBase }, // 计算基数
+                { name: 'Bl', value: '' }, // 变量
+                { name: 'Bz', value: node.data.remark }, // 备注
+            ];
+            Element.call(this, 'QdMx', attrs);
+        }
+
+        // 定额组价
+        function Qdxdezj() {
+            Element.call(this, 'Qdxdezj');
+        }
+
+        // 定额租价明细 招标文件不输出
+        function QdxdezjMx(ration) {
+            const typeMap = {
+                [RationType.RATION]: '1',
+                [RationType.VOLUME_PRICE + GljType.LABOUR]: '2',
+                [RationType.VOLUME_PRICE + GljType.GENERAL_MATERIAL]: '3',
+                [RationType.VOLUME_PRICE + GljType.GENERAL_MACHINE]: '4',
+                [RationType.VOLUME_PRICE + GljType.EQUIPMENT]: '2',
+            };
+            const typeKey = (ration.type || '') + (ration.subType || '');
+            const type = typeMap[typeKey];
+            const attrs = [
+                { name: 'Debm', value: ration.code }, // 编码
+                { name: 'Mc', value: ration.name }, // 名称
+                { name: 'Dw', value: ration.unit }, // 单位
+                { name: 'Sl', value: ration.tenderQuantity }, // 工程量
+                { name: 'Dj', value: getFee(ration.fees, 'common.tenderUnitFee'), type: TYPE.DECIMAL }, // 单价
+                { name: 'Hj', value: getFee(ration.fees, 'common.tenderTotalFee'), type: TYPE.DECIMAL }, // 合价
+                { name: 'Rgf', value: getFee(ration.fees, 'marketLabour.tenderTotalFee'), type: TYPE.DECIMAL }, // 人工费
+                { name: 'Clf', value: getFee(ration.fees, 'marketMaterial.tenderTotalFee'), type: TYPE.DECIMAL }, // 材料费
+                { name: 'Jxf', value: getFee(ration.fees, 'marketMachine.tenderTotalFee'), type: TYPE.DECIMAL }, // 机械费
+                { name: 'Sbf', value: getFee(ration.fees, 'marketEquipment.tenderTotalFee'), type: TYPE.DECIMAL }, // 设备费
+                { name: 'Csf', value: getFee(ration.fees, 'measure.tenderTotalFee'), type: TYPE.DECIMAL }, // 措施费
+                { name: 'Glf', value: getFee(ration.fees, 'manage.tenderTotalFee'), type: TYPE.DECIMAL }, // 企业管理费
+                { name: 'Gf', value: getFee(ration.fees, 'force.tenderTotalFee'), type: TYPE.DECIMAL }, // 规费
+                { name: 'Lr', value: getFee(ration.fees, 'profit.tenderTotalFee'), type: TYPE.DECIMAL }, // 利润
+                { name: 'Sj', value: getFee(ration.fees, 'tax.tenderTotalFee'), type: TYPE.DECIMAL }, // 税金
+                { name: 'Delb', value: type, type: TYPE.INT }, // 取定额/量价/设备的类别。(1=普通定额,2=人工,3=材料,4=机械,5=设备)
+                { name: 'Iszd', value: 'false', type: TYPE.BOOL }, // 暂时,全部取fals
+            ];
+            Element.call(this, 'QdxdezjMx', attrs);
+        }
+
+        // 定额人材机含量
+        function Qdxdercjhl() {
+            Element.call(this, 'Qdxdercjhl');
+        }
+
+        // 定额人材机含量明细
+        function QdxdercjhlMx(rcjID, quantity) {
+            const attrs = [
+                { name: 'RcjId', value: rcjID }, // 人材机资源ID
+                { name: 'Sl', value: quantity, type: TYPE.DECIMAL }, // 消耗量
+            ]
+            Element.call(this, 'QdxdercjhlMx', attrs);
+        }
+
+        // 清单人材机含量
+        function Qdxrcjhl() {
+            Element.call(this, 'Qdxrcjhl');
+        }
+
+        // 清单人材机含量明细
+        function QdxrcjhlMx(item) {
+            const attrs = [
+                { name: 'RcjId', value: item.rcjID }, // 人材机资源ID
+                { name: 'Rcjhl', value: item.contain, type: TYPE.DECIMAL }, // 含量:取叶子清单下所有的人材机的含量,=各定额下的人材机总消耗量之和,再除以清单工程量
+                { name: 'Rcjhj', value: item.totalPrice, type: TYPE.DECIMAL }, // 合价:人材机含量*人材机预算价
+                { name: 'Zgjbz', value: item.isEvaluate, type: TYPE.BOOL }, // 是否暂估,根据资源ID,读取工料机汇总界面对应的“是否暂估”
+                { name: 'Zcbz', value: 'false', type: TYPE.BOOL }, // 主材标记,公路上无此概念,默认输出=fales
+                { name: 'Zyclbz', value: item.isMainMaterial, type: TYPE.BOOL }, // 根据资源ID,读取工料机汇总界面对应的“主要材料”
+            ]
+            Element.call(this, 'QdxrcjhlMx', attrs);
+        }
+
+        // 计日工
+        function Jrg() {
+            Element.call(this, 'Jrg');
+        }
+
+        // 计日工标题
+        function JrgBt(node) {
+            const typeMap = {
+                [fixedFlag.LABOUR_SERVICE]: '1',
+                [fixedFlag.MATERIAL]: '2',
+                [fixedFlag.CONSTRUCTION_MACHINE]: '3',
+            };
+            const attrs = [
+                { name: 'Name', value: node.data.name }, // 名称
+                { name: 'Je', value: isBidInvitation ? '0' : getFee(node.data.fees, 'common.tenderTotalFee'), type: TYPE.DECIMAL }, // 金额
+                { name: 'Lb', value: typeMap[node.getFlag()], type: TYPE.INT }, // 类别:1=劳务;2=材料;3=施工机械
+                { name: 'Bz', value: node.data.remark }, // 备注
+            ];
+            Element.call(this, 'JrgBt', attrs);
+        }
+
+        // 计日工明细
+        function JrgMx(node) {
+            const attrs = [
+                { name: 'Bh', value: node.data.code }, // 编码
+                { name: 'Name', value: node.data.name }, // 名称
+                { name: 'Dw', value: node.data.unit }, // 单位
+                { name: 'Sl', value: node.data.quantity }, // 工程量
+                { name: 'Dj', value: isBidInvitation ? '0' : getFee(node.data.fees, 'common.tenderTotalFee'), type: TYPE.DECIMAL }, // 单价
+                { name: 'Hj', value: isBidInvitation ? '0' : getFee(node.data.fees, 'common.tenderUnitFee'), type: TYPE.DECIMAL }, // 合价
+            ];
+            Element.call(this, 'JrgMx', attrs);
+        }
+
+        // 暂估材料表、评标材料表
+        function gljRefRoot(eleName) {
+            Element.call(this, eleName);
+        }
+
+        // 暂估材料表、评标材料表明细
+        function gljRefElement(eleName, glj) {
+            const attrs = [
+                { name: 'Xh', value: glj.seq }, // 序号
+                { name: 'RcjId', value: projectGLJIDToRcjID[glj.projectGLJID] }, // 资源ID
+                { name: 'Bm', value: glj.code }, // 编码
+                { name: 'Mc', value: glj.name }, // 名称
+                { name: 'Ggxh', value: glj.specs }, // 规格型号
+                { name: 'Dw', value: glj.unit }, // 单位
+                { name: 'Sl', value: BID_INVITATION ? '0' : glj.quantity, type: TYPE.DECIMAL }, // 工程量
+                { name: 'Dj', value: glj.marketPrice, type: TYPE.DECIMAL }, // 暂定价、单价
+                { name: 'Hj', value: BID_INVITATION ? '0' : glj.totalPrice, type: TYPE.DECIMAL }, // 合价
+                { name: 'Bz', value: glj.remark }, // 备注
+            ];
+            Element.call(this, eleName, attrs);
+        }
+
+        // 人材机汇总
+        function Rcjhz() {
+            Element.call(this, 'Rcjhz');
+        }
+
+        // 人材机汇总明细
+        function RcjhzMx(glj) {
+            const rootTypeReg = /\d/;
+            let rootType = rootTypeReg.exec(glj.type)[0];
+            if (!['1', '2', '3'].includes(rootType)) {
+                rootType = '2'; 
+            }
+            const attrs = [
+                { name: 'RcjId', value: projectGLJIDToRcjID[glj.id] }, // 资源ID
+                { name: 'RcjBm', value: glj.code }, // 编码
+                { name: 'Name', value: glj.name }, // 名称
+                { name: 'Ggxh', value: glj.specs }, // 规格型号
+                { name: 'Dw', value: glj.unit }, // 单位
+                { name: 'Dj', value: glj.priceInfo.tenderPrice, type: TYPE.DECIMAL }, // 预算价,调后
+                { name: 'Sl', value: glj.tenderQuantity, type: TYPE.DECIMAL }, // 总消耗量
+                { name: 'Hj', value: glj.totalPrice, type: TYPE.DECIMAL }, // 合价,人材料总消耗量*预算价
+                { name: 'Cd', value: '' }, // 产地
+                { name: 'Gycs', value: '' }, // 厂商
+                { name: 'Rcjlb', value: rootType, type: TYPE.INT }, // 人材机类型 1=人工;2=材料;3=机械
+                { name: 'Jgbz', value: 'false', type: TYPE.BOOL }, // 供材方
+                { name: 'Zyclbz', value: !!glj.is_main_material, type: TYPE.BOOL }, // 主要材料
+                { name: 'Zgjbz', value: !!glj.is_evaluate, type: TYPE.BOOL }, // 是否暂估
+                { name: 'Zcbz', value: 'false', type: TYPE.BOOL }, // 主材标记
+            ];
+            Element.call(this, 'RcjhzMx', attrs);
+        }
+
+        // 配比组成
+        function PbZc() {
+            Element.call(this, 'PbZc');
+        }
+
+        // 配比明细
+        function PbZcMx(ratio) {
+            const attrs = [
+                { name: 'RcjId', value: projectGLJIDToRcjID[ratio.projectGLJID] }, // 组成物对应人材机汇总的资源ID
+                { name: 'Sl', value: ratio.consumption, type: TYPE.DECIMAL }, // 组成物消耗量
+            ];
+            Element.call(this, 'PbZcMx', attrs);
+        }
+        
+        // 校验信息
+        function Jyxx(cpuID, diskID, macID, toMD5Value) {
+            const attrs = [
+                { name: 'SoftName', value: '纵横创新软件有限公司' }, // 公司名
+                { name: 'SoftNum', value: md5(toMD5Value) }, // 校验数据
+                { name: 'MacAdress', value: macID }, // mac地址
+                { name: 'DogNum', value: userID }, // 加密锁号,取用户ID
+                { name: 'ComputerName', value: USER_NAME }, // 计算机名,取用户名称
+                { name: 'HDDSerial', value: diskID }, // 硬盘序列号
+                { name: 'CPUSerial', value: cpuID }, // CPU序列号
+            ];
+            Element.call(this, 'Jyxx', attrs);
+        }
+
+        // 组装数据 --------------------------------------
+
+        // 组装建设项目数据
+        function setupConstruction(constructionData) {
+            const information = constructionData.property && constructionData.property.basicInformation || [];
+            const summaryInfo = constructionData.summaryInfo[constructionData.ID];
+            const jingJiBiao = new JingJiBiao(constructionData.name, information);
+            if (isBidInvitation) {
+                jingJiBiao.children.push(new ZhaoBiaoXx(information));
+            } else if (isControl) {
+                jingJiBiao.children.push(new ZhaoBiaoKzXx(information, summaryInfo.totalCost));
+            } else {
+                jingJiBiao.children.push(new TouBiaoXx(information, summaryInfo.totalCost));
+            }
+            // 将单位工程工程特征中,单项工程编号、名称相同的,插入到建设项目和分段(单位工程)的中间层。
+            const midLayerMap = {}; // 单项工程key(code@name)与单项工程节点映射
+            for (const tenderData of constructionData.children) {
+                const feature = tenderData.property && tenderData.property.projectFeature || [];
+                const midLayerCode = getValueByKey(feature, 'singleProjNo');
+                const midLayerName = getValueByKey(feature, 'singleProjName');
+                const midLayerKey = `${midLayerCode}@${midLayerName}`;
+                if (!midLayerMap[midLayerKey]) {
+                    jingJiBiao.children.push(midLayerMap[midLayerKey] = new Dxgcxx(midLayerCode, midLayerName));
+                }
+                midLayerMap[midLayerKey].children.push(setupTender(tenderData, feature));
+            }
+            // 校验信息
+            const [cpuID, diskID, macID] = generateHardwareId().split(';');
+            const suffix = INTERFACE_CONFIG[areaKey]['fileSuffix'][exportKind];
+            // 需要md5的值:MD5(“WHGLZTB”+项目名称+机器码+加密锁锁号)机器码取mac地址,加密锁锁号取用户账号id
+            const toMD5Suffix = suffix.split('.')[1];
+            const toMD5Value = `${toMD5Suffix}${constructionData.name}${macID}${userID}`;
+            jingJiBiao.children.push(new Jyxx(cpuID, diskID, macID, suffix, toMD5Value));
+            return [{
+                data: jingJiBiao,
+                exportKind,
+                fileName: `${constructionData.name}${suffix}`
+            }];
+        }
+
+        // getData接口数据
+        let curDetail;
+
+        // 需要先设置项目人材机的关联ID(从1开始),因为定额人材机等一些节点需要用到这个关联ID
+        const projectGLJIDToRcjID = {}; // 项目人材机ID与新生成的整形ID映射
+        const projectGLJMap = {}; // 项目人材机与项目人材机数据映射
+
+        // 组装单位工程数据
+        function setupTender(tenderData, feature) {
+            curDetail = tenderDetailMap[tenderData.ID];
+            curDetail.projectGLJ.datas.gljList.forEach((glj, index) => {
+                projectGLJIDToRcjID[glj.id] = index + 1;
+                projectGLJMap[glj.id] = glj;
+            });
+            const dwgcxx = new Dwgcxx(tenderData.name, feature);
+            dwgcxx.children.push(
+                setupFeeRate(curDetail.FeeRate),
+                setupBills(curDetail.mainTree),
+                ...setupGLJList(curDetail)
+            );
+            return dwgcxx;
+        }
+
+        // 组装费率数据
+        function setupFeeRate(feeRateDetail) {
+            const qfxx = new Qfxx();
+            const jjflb = new JjFlb();
+            // 费率界面左侧底层数据
+            let curRootItem;
+            feeRateDetail.datas.rates.forEach(item => {
+                if (!item.ParentID) {
+                    curRootItem = item;
+                } else if (!item.sum) { // 最底层
+                    jjflb.children.push(new JjFlbMx(curRootItem, item));
+                }
+            });
+            // 费率界面右侧顶层数据
+            const jjflx = new JjFlx();
+            const flxmxData = feeRateDetail
+                .getAllSubRates()
+                .filter(item => !item.isSub)
+                .map(item => new JjFlxMx(item));
+            jjflx.children.push(...flxmxData);
+
+            qfxx.children.push(jjflb, jjflx);
+            return qfxx;
+        }
+
+        // 组装清单数据
+        function setupBills(mainTree) {
+            const qdxm = new QdXm();
+            const qdbtData = mainTree.roots.map(node => new QdBt(node));
+            qdxm.children.push(...qdbtData);
+
+            mainTree.roots.forEach(node => {
+                const flag = node.getFlag();
+                const qdbt = new QdBt(node);
+                if (flag === fixedFlag.ONE_SEVEN_BILLS) { // 100章到700章清单需要输出详细数据
+                    qdbt.children.push(...setupSubBills(node.children));
+                } else if (flag === fixedFlag.DAYWORK_LABOR) {
+                    qdbt.children.push(setupDaywork(node.children));
+                }
+                qdxm.children.push(qdbt);
+            });
+
+            function setupSubBills(nodes) {
+                const rst = [];
+                nodes.forEach(node => {
+                    const qdmx = new QdMx(node);
+                    rst.push(qdmx);
+                    const subIsRations = node.children.length && !node.source.children.length;
+                    if (subIsRations) {
+                        qdmx.children.push(...setupRations(node.children));
+                        qdmx.children.push(...setupBillsContain(node.data));
+                    } else {
+                        qdmx.children.push(...setupSubBills(node.children));
+                    }
+                });
+                return rst;
+            }
+
+            function setupDaywork(nodes) {
+                const dayworkRoot = new Jrg();
+                const dayworkBT = nodes.map(node => {
+                    const bt = new JrgBt(node);
+                    const dayworkMX = node.children.map(child => new JrgMx(child));
+                    bt.children.push(...dayworkMX);
+                    return bt;
+                });
+                dayworkRoot.children.push(...dayworkBT);
+                return dayworkRoot;
+            }
+
+            return qdxm;
+        }
+
+        // 组装定额、定额人材机数据
+        function setupRations(rationNodes) {
+            // 招标文件不输出
+            if (isBidInvitation) {
+                return [];
+            }
+            const rationRoot = new Qdxdezj();
+            const rationEles = rationNodes.map(node => {
+                const rationEle = new QdxdezjMx(node.data);
+                /* // 定额人材机
+                let rationGLJList = curDetail.ration_glj.datas.filter(glj => glj.rationID === node.data.ID);
+                rationGLJList = gljUtil.sortRationGLJ(rationGLJList); */
+                const rationGLJRoot = new Qdxdercjhl();
+                const rationGLJEles = node.data.rationGLJList.map(glj => new QdxdercjhlMx(projectGLJIDToRcjID[glj.projectGLJID], glj.tenderQuantity));
+                rationGLJRoot.children.push(...rationGLJEles);
+                rationEle.children.push(rationGLJRoot);
+                return rationEle;
+            });
+            rationRoot.children.push(...rationEles);
+            return [rationRoot];
+        }
+
+        // 组装清单人材机含量数据
+        function setupBillsContain(bills) {
+            if (isBidInvitation) {
+                return [];
+            }
+            // 读取清单下的人材机,先将各定额下的人材机汇总,相同的合并(相同的项目人材机ID)
+            const gljList = curDetail.ration_glj.datas.filter(glj => glj.billsItemID === bills.ID);
+            if (!gljList) {
+                return [];
+            }
+            const map = {};
+            gljList.forEach(glj => {
+                if (!map[glj.projectGLJID]) {
+                    const projectGLJ = projectGLJMap[glj.projectGLJID];
+                    map[glj.projectGLJID] = {
+                        rcjID: projectGLJIDToRcjID[glj.projectGLJID],
+                        totalQuantity: glj.tenderQuantity,
+                        price: glj.tenderPrice,
+                        isEvaluate: !!projectGLJ.is_evaluate,
+                        isMainMaterial: !!projectGLJ.is_main_material
+                    };
+                } else {
+                    map[glj.projectGLJID].totalQuantity = scMathUtil.roundTo(map[glj.projectGLJID].totalQuantity + glj.tenderQuantity, -6);
+                }
+            });
+            const containItems = Object
+                .values(map)
+                .map(glj => {
+                    const contain = glj.totalQuantity / (bills.quantity || 1);
+                    return {
+                        rcjID: glj.rcjID,
+                        contain: scMathUtil.roundTo(contain, -6), // 固定取6位
+                        totalPrice: scMathUtil.roundTo(contain * glj.price, -2), // 固定取2位
+                        isEvaluate: glj.isEvaluate,
+                        isMainMaterial: glj.isMainMaterial
+                    };
+                });
+            const root = new Qdxrcjhl();
+            root.children.push(...containItems.map(item => new QdxrcjhlMx(item)));
+            return [root]
+        }
+
+        // 组装人材机汇总相关表数据
+        function setupGLJList(tenderDetail) {
+            // 暂估材料
+            const zgcl = new gljRefRoot('ZgCl');
+            zgcl.children = tenderDetail.evaluateMaterialData.map(glj => new gljRefElement('ZgClMx', glj));
+
+            // 评标材料
+            const jpcl = new gljRefRoot('JpCl');
+            jpcl.children = tenderDetail.bidMaterialData.map(glj => new gljRefElement('JpClMx', glj));
+            if (isBidInvitation) {
+                return [zgcl, jpcl];
+            }
+            // 人材机汇总 (招标不导出)
+            const rcjhz = new Rcjhz();
+            rcjhz.children = tenderDetail.projectGLJ.datas.gljList.map(pGLJ => {
+                const gljElement = new RcjhzMx(pGLJ);
+                // 组成物
+                const connectKey = gljUtil.getIndex(pGLJ, gljKeyArray);
+                const ratioData = tenderDetail.projectGLJ.datas.mixRatioMap[connectKey];
+                if (ratioData && ratioData.length) {
+                    const pbzc = new PbZc();
+                    pbzc.children = ratioData.map(ratio => new PbZcMx(ratio));
+                    gljElement.children.push(pbzc);
+                }
+                return gljElement;
+            });
+            return [zgcl, jpcl, rcjhz];
+        }
+
+        return setupConstruction(projectData);
+
+
+    }
+
+    return {
+        entry
+    };
+})();

+ 2 - 5
web/building_saas/standard_interface/export/anhui_maanshan.js

@@ -231,9 +231,6 @@ INTERFACE_EXPORT = (() => {
                 '量价': 14,
             };
             const rate = isEmptyVal(item.rate) ? '100' : item.rate; // 为空时输出=100,为0时输出=0
-            /* if (!FeeRateTypeMap[rootItem.name]) {
-                debugger;
-            } */
             const attrs = [
                 { name: 'Bm', value: FeeRateCodeMap[item.name] }, // 编码
                 { name: 'Name', value: item.name }, // 名称
@@ -475,7 +472,7 @@ INTERFACE_EXPORT = (() => {
                 { name: 'Dw', value: glj.unit }, // 单位
                 { name: 'Dj', value: glj.priceInfo.tenderPrice, type: TYPE.DECIMAL }, // 预算价,调后
                 { name: 'Sl', value: glj.tenderQuantity, type: TYPE.DECIMAL }, // 总消耗量
-                { name: 'Hj', value: glj.totalPrice, type: TYPE.DECIMAL }, // 合价,人材料总消耗量*预算价
+                { name: 'Hj', value: glj.priceInfo.totalPrice, type: TYPE.DECIMAL }, // 合价,人材料总消耗量*预算价
                 { name: 'Cd', value: '' }, // 产地
                 { name: 'Gycs', value: '' }, // 厂商
                 { name: 'Rcjlb', value: rootType, type: TYPE.INT }, // 人材机类型 1=人工;2=材料;3=机械
@@ -690,7 +687,7 @@ INTERFACE_EXPORT = (() => {
                     map[glj.projectGLJID] = {
                         rcjID: projectGLJIDToRcjID[glj.projectGLJID],
                         totalQuantity: glj.tenderQuantity,
-                        price: glj.tenderPrice,
+                        price: projectGLJ.priceInfo.tenderPrice,
                         isEvaluate: !!projectGLJ.is_evaluate,
                         isMainMaterial: !!projectGLJ.is_main_material
                     };

+ 6 - 4
web/building_saas/standard_interface/export/base.js

@@ -780,16 +780,17 @@ const INTERFACE_EXPORT_BASE = (() => {
         const projectGLJList = tenderDetail.projectGLJ.datas.gljList;
         // 计算人材机总消耗量,否则projectGLJ.datas.gljList里的数据不会有消耗量数据
         gljUtil.calcProjectGLJQuantity(tenderDetail.projectGLJ.datas, tenderDetail.ration_glj.datas, tenderDetail.Ration.datas, tenderDetail.Bills.datas, tenderDetail.property.decimal.glj.quantity, _, scMathUtil);
+        const connectKeyMap = {};
+        const projectGLJIDMap = {};
         projectGLJList.forEach(glj => {
+            connectKeyMap[gljUtil.getIndex(glj, gljKeyArray)] = glj.id; // 为了方便后续导出处理,给组成物数据设置上对应项目人材机ID
+            projectGLJIDMap[glj.id] = glj;
             // 项目人材机设置价格信息,否则projectGLJ.datas.gljList里的数据不会有相关价格信息
             // 价格信息存在新的priceInfo字段,以免对一些方法造成影响
             glj.priceInfo = gljUtil.getGLJPrice(glj, tenderDetail.projectGLJ.datas, tenderDetail.property.calcOptions, tenderDetail.labourCoe.datas, decimalInfo, false, _, scMathUtil, {}, tenderDetail.projectGLJ.getTenderPriceCoe(glj, tenderDetail.property));
             // 计算合价:人材料总消耗量*预算价
             glj.priceInfo.totalPrice = scMathUtil.roundForObj(glj.priceInfo.tenderPrice * glj.tenderQuantity, 2);
         });
-        // 为了方便后续导出处理,给组成物数据设置上对应项目人材机ID
-        const connectKeyMap = {};
-        projectGLJList.forEach(pGLJ => connectKeyMap[gljUtil.getIndex(pGLJ, gljKeyArray)] = pGLJ.id);
         const ratiosArr = Object.values(tenderDetail.projectGLJ.datas.mixRatioMap);
         ratiosArr.forEach(ratios => {
             ratios.forEach(ratio => {
@@ -803,7 +804,8 @@ const INTERFACE_EXPORT_BASE = (() => {
         // 处理定额人材机,将定额人材机挂到定额的rationGLJList字段中,同时将定额人材机进行排序、计算调价消耗量
         tenderDetail.Ration.datas.forEach(ration => {
             ration.rationGLJList = tenderDetail.ration_glj.datas.filter(glj => {
-                gljUtil.getTotalQuantity(glj, ration, decimalInfo.ration.quantity, decimalInfo.glj.quantity);
+                const pGLJ = projectGLJIDMap[glj.projectGLJID];
+                glj.tenderQuantity = gljUtil.getRationGLJTenderQuantity(glj, ration, decimalInfo.glj.quantity, scMathUtil, pGLJ);
                 return glj.rationID === ration.ID;
             });
             ration.rationGLJList = gljUtil.sortRationGLJ(ration.rationGLJList);

+ 2 - 16
web/over_write/js/anhui_2019.js

@@ -341,23 +341,9 @@ if (typeof baseFigureTemplate !== 'undefined') {
             return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'common');
         },
         //{专项暂定合计}
-        // 汇总专项暂定列有值的清单的金额
+        // 第100章至700章清单行的暂估合价
         'ZXZDHJ': function (tender) {
-            let rst = 0,
-                feeField = 'common',
-                subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            let billsData = calcBase.project.Bills.datas,
-                filterData = billsData.filter(function (data) {
-                    return data.specialProvisional;
-                });
-            for (let data of filterData) {
-                if (cbTools.isUnDef(data.feesIndex) || _.isEmpty(data.feesIndex) ||
-                    cbTools.isUnDef(data.feesIndex[feeField]) || cbTools.isUnDef(data.feesIndex[feeField][subFeeField])) {
-                    continue;
-                }
-                rst += data.feesIndex[feeField][subFeeField];
-            }
-            return rst.toDecimal(decimalObj.bills.totalPrice);
+            return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'estimate');
         },
         //{100章以外清单合计}
         // 取清单固定清单[第100章至700章清单]的金额,但扣除清单100章下的金额。

+ 2 - 16
web/over_write/js/hunan_2020.js

@@ -358,23 +358,9 @@ if (typeof baseFigureTemplate !== 'undefined') {
             return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'common');
         },
         //{专项暂定合计}
-        // 汇总专项暂定列有值的清单的金额
+        // 第100章至700章清单行的暂估合价
         'ZXZDHJ': function (tender) {
-            let rst = 0,
-                feeField = 'common',
-                subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            let billsData = calcBase.project.Bills.datas,
-                filterData = billsData.filter(function (data) {
-                    return data.specialProvisional;
-                });
-            for (let data of filterData) {
-                if (cbTools.isUnDef(data.feesIndex) || _.isEmpty(data.feesIndex) ||
-                    cbTools.isUnDef(data.feesIndex[feeField]) || cbTools.isUnDef(data.feesIndex[feeField][subFeeField])) {
-                    continue;
-                }
-                rst += data.feesIndex[feeField][subFeeField];
-            }
-            return rst.toDecimal(decimalObj.bills.totalPrice);
+            return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'estimate');
         },
         //{100章以外清单合计}
         // 取清单固定清单[第100章至700章清单]的金额,但扣除清单100章下的金额。

+ 2 - 16
web/over_write/js/neimeng_2019.js

@@ -296,23 +296,9 @@ if (typeof baseFigureTemplate !== 'undefined') {
             return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'common');
         },
         //{专项暂定合计}
-        // 汇总专项暂定列有值的清单的金额
+        // 第100章至700章清单行的暂估合价
         'ZXZDHJ': function (tender) {
-            let rst = 0,
-                feeField = 'common',
-                subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            let billsData = calcBase.project.Bills.datas,
-                filterData = billsData.filter(function (data) {
-                    return data.specialProvisional;
-                });
-            for (let data of filterData) {
-                if (cbTools.isUnDef(data.feesIndex) || _.isEmpty(data.feesIndex) ||
-                    cbTools.isUnDef(data.feesIndex[feeField]) || cbTools.isUnDef(data.feesIndex[feeField][subFeeField])) {
-                    continue;
-                }
-                rst += data.feesIndex[feeField][subFeeField];
-            }
-            return rst.toDecimal(decimalObj.bills.totalPrice);
+            return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'estimate');
         },
         //{100章以外清单合计}
         // 取清单固定清单[第100章至700章清单]的金额,但扣除清单100章下的金额。

+ 2 - 16
web/over_write/js/quanguo_2018.js

@@ -431,23 +431,9 @@ if (typeof baseFigureTemplate !== 'undefined') {
       return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'common');
     },
     //{专项暂定合计}
-    // 汇总专项暂定列有值的清单的金额
+    // 第100章至700章清单行的暂估合价
     'ZXZDHJ': function (tender) {
-      let rst = 0,
-        feeField = 'common',
-        subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-      let billsData = calcBase.project.Bills.datas,
-        filterData = billsData.filter(function (data) {
-          return data.specialProvisional;
-        });
-      for (let data of filterData) {
-        if (cbTools.isUnDef(data.feesIndex) || _.isEmpty(data.feesIndex) ||
-          cbTools.isUnDef(data.feesIndex[feeField]) || cbTools.isUnDef(data.feesIndex[feeField][subFeeField])) {
-          continue;
-        }
-        rst += data.feesIndex[feeField][subFeeField];
-      }
-      return rst.toDecimal(decimalObj.bills.totalPrice);
+      return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'estimate');
     },
     //{100章以外清单合计}
     // 取清单固定清单[第100章至700章清单]的金额,但扣除清单100章下的金额。

+ 50 - 15
web/over_write/js/shandong_2016.js

@@ -229,22 +229,9 @@ if (typeof baseFigureTemplate !== "undefined") {
       return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, "common");
     },
     //{专项暂定合计}
-    // 汇总专项暂定列有值的清单的金额
+    // 第100章至700章清单行的暂估合价
     ZXZDHJ: function (tender) {
-      let rst = 0,
-        feeField = "common",
-        subFeeField = tender ? "tenderTotalFee" : "totalFee";
-      let billsData = calcBase.project.Bills.datas,
-        filterData = billsData.filter(function (data) {
-          return data.specialProvisional;
-        });
-      for (let data of filterData) {
-        if (cbTools.isUnDef(data.feesIndex) || _.isEmpty(data.feesIndex) || cbTools.isUnDef(data.feesIndex[feeField]) || cbTools.isUnDef(data.feesIndex[feeField][subFeeField])) {
-          continue;
-        }
-        rst += data.feesIndex[feeField][subFeeField];
-      }
-      return rst.toDecimal(decimalObj.bills.totalPrice);
+      return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'estimate');
     },
     //{100章以外清单合计}
     // 取清单固定清单[第100章至700章清单]的金额,但扣除清单100章下的金额。
@@ -346,6 +333,54 @@ if (typeof feeRateObject !== "undefined") {
   };
 }
 
+if (typeof gljOprObj !== 'undefined') {
+    // 添加、替换工料机界面工料机排序
+    gljOprObj.sortSelectViewGLJ = function (data) {
+        // 工料机编码:按“-”前的数值排序
+        const reg = /[^-]+/;
+        data.sort((a, b) => {
+            const orgCodeA = a.code;
+            const orgCodeB = b.code;
+            const regCodeA = orgCodeA.match(reg);
+            const regCodeB = orgCodeB.match(reg);
+            const compareCodeA = regCodeA
+                ? +regCodeA[0]
+                    ? +regCodeA[0]
+                    : regCodeA[0]
+                : orgCodeA;
+            const compareCodeB = regCodeB
+                ? +regCodeB[0]
+                    ? +regCodeB[0]
+                    : regCodeB
+                : orgCodeB;
+            return isNaN(compareCodeA) && isNaN(compareCodeB)
+                ? compareCodeA.localeCompare(compareCodeB)
+                : compareCodeA - compareCodeB;
+        });
+        // 工料机类型排序:人工、机上人工、混凝土、砂浆、配合比、普通材料、商品混凝土、商品砂浆、外购砼构件、绿化苗木、机械台班、机械组成物、设备。
+        const TypeMap = {
+            1: 1,
+            303: 2,
+            202: 3,
+            203: 4,
+            204: 5,
+            201: 6,
+            205: 7,
+            206: 8,
+            208: 9,
+            209: 10,
+            301: 11,
+            302: 12,
+            5: 13,
+        };
+        data.sort((a, b) => {
+            const typeA = TypeMap[a.gljType];
+            const typeB = TypeMap[b.gljType];
+            return typeA - typeB;
+        });
+    }
+}
+
 if (typeof module !== "undefined" && !module.nodeType) {
   module.exports = {
     progression,

+ 2 - 16
web/over_write/js/zhejiang_2005.js

@@ -173,23 +173,9 @@ if (typeof baseFigureTemplate !== 'undefined') {
             return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'common');
         },
         //{专项暂定合计}
-        // 汇总专项暂定列有值的清单的金额
+        // 第100章至700章清单行的暂估合价
         'ZXZDHJ': function (tender) {
-            let rst = 0,
-                feeField = 'common',
-                subFeeField = tender ? 'tenderTotalFee' : 'totalFee';
-            let billsData = calcBase.project.Bills.datas,
-                filterData = billsData.filter(function (data) {
-                    return data.specialProvisional;
-                });
-            for (let data of filterData) {
-                if (cbTools.isUnDef(data.feesIndex) || _.isEmpty(data.feesIndex) ||
-                    cbTools.isUnDef(data.feesIndex[feeField]) || cbTools.isUnDef(data.feesIndex[feeField][subFeeField])) {
-                    continue;
-                }
-                rst += data.feesIndex[feeField][subFeeField];
-            }
-            return rst.toDecimal(decimalObj.bills.totalPrice);
+            return cbTools.getBaseFee(calcBase.fixedFlag.ONE_SEVEN_BILLS, tender, 'estimate');
         },
         //{100章以外清单合计}
         // 取清单固定清单[第100章至700章清单]的金额,但扣除清单100章下的金额。