Procházet zdrojové kódy

feat:安徽马鞍山导出接口相关

vian před 5 roky
rodič
revize
4a47ce24dc

+ 9 - 1
modules/import/controllers/import_controller.js

@@ -5,6 +5,7 @@
  * Created by jimiz on 2017/4/9.
  */
 let logger = require("../../../logs/log_helper").logger;
+const Project = require('../../main/models/project');
 let pm_facade = require('../../pm/facade/pm_facade');
 let controller = {
     importProject:async function (req){
@@ -34,7 +35,14 @@ let controller = {
         let data = JSON.parse(req.body.dataString);
         result.data = await pm_facade.accessToCopyProject(req.body.userID,req.body.compilationID,data);
         return result
-    }
+    },
+    async getDataForInterface (req) {
+        const result = {
+            error: 0
+        };
+        result.data = await Project.getDataSync(req.body.project_id);
+        return result;
+    },
 };
 
 

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

@@ -11,6 +11,7 @@ module.exports = function (app) {
     importRouter.post('/copyProject',importController.action);
     importRouter.post('/copyConstructionProject',importController.action);
     importRouter.post('/prepareInitialData',importController.action);
+    importRouter.post('/getDataForInterface',importController.action);
     importRouter.get('/test',function (req,res) {
         res.json("hello word");
     })

+ 15 - 1
modules/main/controllers/project_controller.js

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

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

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

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

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

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

@@ -40,6 +40,7 @@ let callback = function (req, res, err, message, data) {
 
 
 module.exports = {
+    redirectToImportServer,
     checkRight: function (req, res) {
         if (typeof req.body.data === 'object') {
             req.body.data = JSON.stringify(req.body.data);

+ 7 - 0
public/common_util.js

@@ -37,6 +37,12 @@
         return a == b;
     }
 
+    // 是否是汉字(基本汉字)
+    function isHan(str) {
+        const reg = /[\u4e00-\u9fa5]/;
+        return reg.test(str);
+    }
+
     // 将树数据排序好
     function getSortedTreeData(rootID, items) {
         return sortSameDedth(rootID, items).reverse();
@@ -128,6 +134,7 @@
         isEmptyVal,
         hasValue,
         similarEqual,
+        isHan,
         getSortedTreeData,
         handleFullscreen,
         standardNumber,

+ 3 - 1
public/web/id_tree.js

@@ -243,7 +243,9 @@ var idTree = {
         Node.prototype.serialNo = function () {
             return this.tree.items.indexOf(this);
         };
-
+        Node.prototype.row = function () {
+            return this.serialNo() + 1;
+        }
         Node.prototype.addChild = function (node) {
             var preSibling = this.children.length === 0 ? null : this.children[this.children.length - 1];
             node.parent = this;

+ 1 - 1
web/building_saas/fee_rates/fee_rate.html

@@ -5,7 +5,7 @@
         <div class="form-inline py-1">
             <label  class="mx-2" >使用费率文件:<span id="feeRateFileName">费率1</span>(<label class="a_color" id="pop-lv">与<span id="projectCount">3</span> 个单位工程同步</label>)
                 <a class="btn btn-sm ml-1" href="#" data-toggle="modal" data-target="#change-lv" id="changFeeRateFile"><i class="fa fa-exchange"></i> 选择其他</a>
-                <a class="btn btn-sm ml-1" href="#" data-toggle="modal" id="saveAs" data-target="#copy-lv"><i class="fa fa-files-o"></i> 另存单独用</a></label>
+                <a class="btn btn-sm ml-1" href="#" data-toggle="modal" id="save-as" data-target="#copy-lv"><i class="fa fa-files-o"></i> 另存单独用</a></label>
         </div>
     </div>
     <div class="toolsbar_feeRate px-1">

+ 1 - 0
web/building_saas/main/html/main.html

@@ -2023,6 +2023,7 @@
     <script src="/lib/spreadjs/views/plugins/gc.spread.views.gridlayout.10.0.0.min.js" type="text/javascript"></script>
     <script src="/lib/js-xlsx/xlsx.core.min.js"></script>
     <script src="/lib/lz-string/lz-string.min.js"></script>
+    <script src="/lib/fileSaver/FileSaver.min.js"></script>
     <script type="text/javascript" src="/lib/jspdf/jspdf.min.js"></script>
     <!-- inject:js -->
     <!--<script type="text/javascript" src="/test/tmp_data/test_ration_calc/ration_calc_base.js"></script>-->

+ 3 - 1
web/building_saas/main/js/models/cache_tree.js

@@ -190,7 +190,9 @@ var cacheTree = {
         Node.prototype.serialNo = function () {
             return this.tree.items.indexOf(this);
         }
-
+        Node.prototype.row = function () {
+            return this.serialNo() + 1;
+        }
         Node.prototype.addChild = function (node) {
             var preSibling = this.children.length === 0 ? null : this.children[this.children.length - 1];
             node.parent = this;

+ 48 - 3
web/building_saas/main/js/models/project.js

@@ -30,7 +30,44 @@ var PROJECT = {
             });
 
         };
-        tools.doAfterLoad = function(result, callback){
+        // isInit: 是否是初始化,导出接口用到getData,但是非初始化,多次调用getData不会覆盖原有缓存数据
+        tools.doAfterLoad = function(result, isInit, callback){
+            var counter;
+            //必须要先load ProjectInfo的信息
+            let projectInfoModule = result.find(data => data.moduleName === ModuleNames.projectInfo);
+            if (projectInfoModule) {
+                me._project.projectInfo = projectInfoModule.data;
+            }
+            result.forEach(function(item){
+                if (item.moduleName !== ModuleNames.projectInfo) {
+                    if (me.modules[item.moduleName]){
+                        me.modules[item.moduleName].loadData(item.data, isInit);
+                    } else if (item.moduleName === me.projCounter) {
+                        counter = item.data;
+                    } else if (item.moduleName === me.projSetting) {
+                        me._project.projSetting = item.data;
+                        me._project.projSetting.moduleName = me.projSetting;
+                    } else if(item.moduleName === ModuleNames.projectGLJ){
+                        me._project.projectGLJ.loadToCache(item.data);
+                    }
+                }
+            });
+            for (module in counter) {
+                if (me.modules[module]) {
+                    me.modules[module].setMaxID(counter[module]);
+                }
+            }
+            if (isInit) {
+                projectInfoObj.showProjectInfo(me._project.projectInfo);
+            }
+            me._project.property = me._project.projectInfo.property;
+            me._project.loadMainTree();
+            //me.test(result[0].data[0]);
+            if (callback) {
+                callback(0);
+            }
+        };
+        /* tools.doAfterLoad = function(result, callback){
             var counter;
             //必须要先load ProjectInfo的信息
             let projectInfoModule = result.find(data => data.moduleName === ModuleNames.projectInfo);
@@ -63,7 +100,7 @@ var PROJECT = {
             if (callback) {
                 callback(0);
             }
-        };
+        }; */
         tools.eachItem=function(item){
             if (me.modules[item.moduleName]){
                 me.modules[item.moduleName].doAfterUpdate(item.err, item.data);
@@ -239,7 +276,8 @@ var PROJECT = {
                 timeout: 50000,
                 success: function (result) {
                     if (!result.error) {
-                        tools.doAfterLoad(result.data, callback);
+                        const isInit = true;
+                        tools.doAfterLoad(result.data, isInit, callback);
                         // for test calc
                         //tools.doAfterLoad([{moduleName: 'bills', data: BillsData}, {'moduleName': 'ration', data: DrawingData}], callback);
                     } else {
@@ -252,6 +290,13 @@ var PROJECT = {
                 }
             });
         };
+        project.prototype.loadDataSync = async function () {
+            const data = await ajaxPost('/project/getDataForInterface', {user_id: tools._userID, project_id: tools._ID});
+            if (data) {
+                const isInit = false;
+                tools.doAfterLoad(data, isInit);
+            }
+        };
 
         project.prototype.beginUpdate = function(operation){
             if (tools.updateLock === 0){

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

@@ -878,7 +878,7 @@ var projectObj = {
         this.project.loadDatas(function (err) {
             projectInfoObj.refreshTotalPriceSpan(); // 工具栏中的总造价信息
             let mTime = +new Date();
-            projectInfoObj.showProjectInfo(that.project.projectInfo);
+            //projectInfoObj.showProjectInfo(that.project.projectInfo);
             //快速列设置
             if(!colSettingObj.getVisible('itemCharacterText')){
                 switchTznrHtml(true);

+ 309 - 11
web/building_saas/standard_interface/export/anhui_maanshan.js

@@ -10,27 +10,42 @@ INTERFACE_EXPORT = (() => {
 
     /**
      * 
-     * @param {String} userID - 用户ID
      * @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(userID, areaKey, exportKind, projectData, tenderDetailMap) {
+    async function entry(areaKey, exportKind, projectData, tenderDetailMap) {
         const {
-            CONFIG: { TYPE, EXPORT_KIND },
+            CONFIG: { TYPE },
             UTIL: {
                 getValueByKey,
+                getHan,
+                getFee,
             },
             Element,
-        } = XML_EXPORT_BASE;
+        } = INTERFACE_EXPORT_BASE;
+
+        const {
+            EXPORT_KIND: { BID_INVITATION, BID_SUBMISSION, CONTROL },
+            fixedFlag,
+        } = window.commonConstants
+
+        const { isEmptyVal, isDef } = window.commonUtil;
 
         const czzt = {
-            [EXPORT_KIND.BID_INVITATION]: '招标',
-            [EXPORT_KIND.BID_SUBMISSION]: '投标',
-            [EXPORT_KIND.CONTROL]: '招标控制',
+            [BID_INVITATION]: '招标',
+            [BID_SUBMISSION]: '投标',
+            [CONTROL]: '招标控制',
         };
+
+        const isBidInvitation = exportKind === BID_INVITATION; // 是否是招标
+        const isBidSubmission = exportKind === BID_SUBMISSION; // 是否是投标
+        const isControl = exportKind === CONTROL; // 是否是控制价
+
+        // 节点定义--------------------------------
+
         // 建设项目基本信息
         function JingJiBiao(projectName, information) {
             const attrs = [
@@ -46,6 +61,9 @@ INTERFACE_EXPORT = (() => {
             ];
             Element.call(this, 'JingJiBiao', attrs);
         }
+
+        const subArea = areaKey.split('@')[1];
+
         // 招标信息
         function ZhaoBiaoXx(information) {
             const attrs = [
@@ -58,10 +76,23 @@ INTERFACE_EXPORT = (() => {
                 { name: 'BzTime', value: getValueByKey(information, ''), type: TYPE.DATE }, // 编制时间
                 { name: 'FhTime', value: getValueByKey(information, ''), type: TYPE.DATE }, // 复核时间
             ];
+            // 额外字段
+            const extraMap = {
+                '淮北': [
+                    { name: 'ZbrNssbh', value: getValueByKey(information, '') }, // 招标人纳税识别号
+                    { name: 'ZxrNssbh', value: getValueByKey(information, '') }, // 造价咨询人纳税识别号
+                    { name: 'ZbrDbSfzh', value: getValueByKey(information, '') }, // 招标人法定代表人或其授权人身份证号
+                    { name: 'ZxrNssbh', value: getValueByKey(information, '') }, // 造价咨询人法定代表或其授权人纳税识别号
+                ]
+            };
+            if (extraMap[subArea]) {
+                attrs.push(...extraMap[subArea]);
+            }
             Element.call(this, 'ZhaoBiaoXx', attrs);
         }
-        // 招标信息
-        function ZhaoBiaoXx(information) {
+
+        // 招标控制价信息
+        function ZhaoBiaoKzXx(information) {
             const attrs = [
                 { name: 'Zbr', value: getValueByKey(information, '') }, // 招标人
                 { name: 'Zxr', value: getValueByKey(information, '') }, // 造价咨询人
@@ -71,13 +102,280 @@ INTERFACE_EXPORT = (() => {
                 { name: 'Fhr', value: getValueByKey(information, '') }, // 复核人
                 { name: 'BzTime', value: getValueByKey(information, ''), type: TYPE.DATE }, // 编制时间
                 { name: 'FhTime', value: getValueByKey(information, ''), type: TYPE.DATE }, // 复核时间
+                { name: 'Zbkzj', value: getValueByKey(information, ''), type: TYPE.DECIMAL }, // 控制价总价(元),取“投标报价”的金额。
             ];
-            Element.call(this, 'ZhaoBiaoXx', attrs);
+            // 额外字段
+            const extraMap = {
+                '淮北': [
+                    { name: 'ZbrNssbh', value: getValueByKey(information, '') }, // 招标人纳税识别号
+                    { name: 'ZxrNssbh', value: getValueByKey(information, '') }, // 造价咨询人纳税识别号
+                    { name: 'ZbrDbSfzh', value: getValueByKey(information, '') }, // 招标人法定代表人或其授权人身份证号
+                    { name: 'ZxrNssbh', value: getValueByKey(information, '') }, // 造价咨询人法定代表或其授权人纳税识别号
+                ]
+            };
+            if (extraMap[subArea]) {
+                attrs.push(...extraMap[subArea]);
+            }
+            Element.call(this, 'ZhaoBiaoKzXx', attrs);
+        }
+
+        // 招标控制价信息
+        function TouBiaoXx(information) {
+            const attrs = [
+                { name: 'Zbr', value: getValueByKey(information, '') }, // 招标人
+                { name: 'Tbr', value: getValueByKey(information, '') }, // 投标人
+                { name: 'TbrDb', value: getValueByKey(information, '') }, // 投标人法定代表或其授权
+                { name: 'Bzr', value: getValueByKey(information, '') }, // 编制人
+                { name: 'BzTime', value: getValueByKey(information, ''), type: TYPE.DATE }, // 编制时间
+                { name: 'Tbzj', value: getValueByKey(information, ''), type: TYPE.DECIMAL }, // 控制价总价(元),取“投标报价”的金额。
+            ];
+            // 额外字段
+            const extraMap = {
+                '淮北': [
+                    { name: 'ZbrNssbh', value: getValueByKey(information, '') }, // 招标人纳税识别号
+                    { name: 'TbrNssbh', value: getValueByKey(information, '') }, // 投标人纳税识别号
+                    { name: 'TbrDbsfzh', value: getValueByKey(information, '') }, // 投标人法定代表或其授权人身份证号
+                ]
+            };
+            if (extraMap[subArea]) {
+                attrs.push(...extraMap[subArea]);
+            }
+            Element.call(this, 'TouBiaoXx', attrs);
+        }
+
+        // 单项工程信息,因项目管理中无“单项工程”这一层,从单位工程的工程特征信息中拼凑出来
+        function Dxgcxx(code, name) {
+            const attrs = [
+                { name: 'Dxgcbh', value: code }, // 单项工程编号
+                { name: 'Dxgcmc', value: name }, // 单项工程名称
+            ];
+            Element.call(this, 'Dxgcxx', attrs);
+        }
+
+        // 单位工程信息
+        function Dwgcxx(tenderName, feature) {
+            const attrs = [
+                { name: 'Dwgcbh', value: getValueByKey(feature, '') }, // 单位工程编号
+                { name: 'Dwgcmc', value: tenderName }, // 单位工程名称
+            ];
+            Element.call(this, 'Dwgcxx', attrs);
+        }
+
+        // 取费信息(费率信息)
+        function Qfxx() {
+            Element.call(this, 'Qfxx');
+        }
+
+        // 计价费率表
+        function JjFlb() {
+            Element.call(this, 'JjFlb');
+        }
+
+        // 费率明细名称-编码映射表
+        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,
+            构造物I: 5,
+            '构造物I(不计冬)': 16,
+            构造物II: 6,
+            '构造物III(桥梁)': 9,
+            '构造物III(除桥以外不计雨)': 8,
+            技术复杂大桥: 10,
+            '钢材及钢结构(桥梁)': 12,
+            '钢材及钢结构(除桥以外不计夜)': 13,
+            '费率为0': 17,
+            '路面(不计雨)': 4,
+            '构造物I(不计雨)': 16,
+            '构造物III(除桥以外)': 8,
+            '钢材及钢结构(除桥以外)': 13,
+            设备: 15,
+            量价: 14,
+
+        };
+        // 计价费率表明细,造价书费率页面左侧最底层数据
+        function JjFlbMx(rootItem, item) {
+            const rate = isEmptyVal(item.rate) ? '100' : item.rate; // 为空时输出=100,为0时输出=0
+            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 }, // 类别
+            ];
+            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 }, // 编码
+                { 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, 'labour.tenderTotalFee'), type: TYPE.DECIMAL }, // 人工费
+            ];
+            Element.call(this, 'QdMx', attrs);
+        }
+
+        // 组装数据 --------------------------------------
+
+        // 组装费率数据
+        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);
+            return qdxm;
+        }
+
+        // 组装单位工程数据
+        function setupTender(tenderData, feature) {
+            const detail = tenderDetailMap[tenderData.ID];
+            const dwgcxx = new Dwgcxx(tenderData.name, feature);
+            dwgcxx.children.push(
+                setupFeeRate(detail.FeeRate),
+                setupBills(detail.mainTree)
+            );
+            return dwgcxx;
+        }
+
+        // 组装建设项目数据
+        function setupConstruction(constructionData) {
+            const information = constructionData.property && constructionData.property.basicInformation || [];
+            const jingJiBiao = new JingJiBiao(constructionData.name, information);
+            // 将单位工程工程特征中,单项工程编号、名称相同的,插入到建设项目和分段(单位工程)的中间层。
+            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 suffix = INTERFACE_CONFIG[areaKey]['fileSuffix'][exportKind];
+            return [{
+                data: jingJiBiao,
+                exportKind,
+                fileName: `${constructionData.name}${suffix}`
+            }];
+        }
+
+        return setupConstruction(projectData);
+
+
     }
 
     return {
         entry,
     };
-})();
+})();

+ 28 - 14
web/building_saas/standard_interface/export/base.js

@@ -3,10 +3,10 @@
  * @date 2019/6/20
  * @version
  */
-const XML_EXPORT_BASE = (() => {
+const INTERFACE_EXPORT_BASE = (() => {
     'use strict';
 
-    const { isDef, hasValue } = window.commonUtil;
+    const { hasValue, isHan } = window.commonUtil;
 
     // 属性类型
     const TYPE = {
@@ -39,20 +39,17 @@ const XML_EXPORT_BASE = (() => {
         GRANULARITY,
         EXPORT_KIND 
     } = window.commonConstants;
-    const EXPORT_KIND_NAME = {
+    /* const EXPORT_KIND_NAME = {
         1: '招标',
         2: '投标',
         3: '控制价'
-    };
+    }; */
     // 配置项
     const CONFIG = Object.freeze({
         TYPE,
         WHITE_SPACE,
         ADJUST_TYPE,
         TIMEOUT_TIME,
-        GRANULARITY,
-        EXPORT_KIND,
-        EXPORT_KIND_NAME
     });
 
     // 缓存项 不需要的时候需要清空
@@ -97,11 +94,11 @@ const XML_EXPORT_BASE = (() => {
      *         {Array}attrs 节点属性数据
      * @return {void}
      * */
-    function Element(name, attrs) {
+    function Element(name, attrs = []) {
         this.name = name;
         this.attrs = attrs;
-        handleXMLEntity(this.attrs);
         check(this.attrs);
+        handleXMLEntity(this.attrs);
         this.children = [];
     }
 
@@ -169,7 +166,7 @@ const XML_EXPORT_BASE = (() => {
             }
             // 类型对应得值不正确时,赋类型对应默认值
             if (!data.type) {
-                return;
+                continue;
             }
             const dateReg = /([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8])))/;
             if (data.type === TYPE.DATE && !dateReg.test(data.value)) {
@@ -572,8 +569,23 @@ const XML_EXPORT_BASE = (() => {
         });
     }
 
+    // 将文本的中文提取出来
+    function getHan(str) {
+        if (!str) {
+            return '';
+        }
+        return str
+            .split('')
+            .reduce((acc, cur) => {
+                if (isHan(cur)) {
+                    acc.push(cur);
+                }
+                return acc;
+            }, [])
+            .join('');
+    }
+
     const UTIL = Object.freeze({
-        isDef,
         hasValue,
         setTimeoutSync,
         getFee,
@@ -597,6 +609,7 @@ const XML_EXPORT_BASE = (() => {
         setAttr,
         getParsedData,
         setupCode,
+        getHan,
     });
 
     // 开始标签
@@ -690,10 +703,10 @@ const XML_EXPORT_BASE = (() => {
                 await setTimeoutSync(() => { }, TIMEOUT_TIME); // 需要请求项目详细数据的时候,间隔一段时间再初始单位工程数据,减少服务器压力
             }
             // 获取单位工程详细数据
-            tenderDetail = await getTenderDetail(tenderItem.ID, userID);
+            await getTenderDetail(tenderItem.ID, userID);
         }
         // 提取相关项目的详细导出数据
-        return await entryFunc(userID, areaKey, exportKind, projectData, tenderDetailMap);
+        return await entryFunc(areaKey, exportKind, projectData, tenderDetailMap);
     }
 
     /**
@@ -703,7 +716,7 @@ const XML_EXPORT_BASE = (() => {
      * @param {Array} extractData - 提取的数据
      * @param {Function} saveAsFunc - 各自费用定额的导出方法,适应不同接口需要不同的最终文件形式
      */
-    async function exportFile(extractData, saveAsFunc = defaultSaveAs) {
+    async function exportFile(extractData, saveAsFunc) {
         // 获取文件数据
         const fileData = extractData.map(extractObj => {
             // 转换成xml字符串
@@ -741,6 +754,7 @@ const XML_EXPORT_BASE = (() => {
         UTIL,
         Element,
         extractExportData,
+        defaultSaveAs,
         exportFile,
     };
 })();

+ 7 - 7
web/building_saas/standard_interface/export/view.js

@@ -9,10 +9,10 @@
 const EXPORT_VIEW = (() => {
     'use strict';
 
-    const _base = XML_EXPORT_BASE;
+    const _base = INTERFACE_EXPORT_BASE;
     const _cache = _base.CACHE;
     // 导出数据缓存,为了自检完后,再导出的时候不需要重新运行相关提取程序。(暂时取消了自检,但还是留着这个缓存机制)
-    const _exportCache = [];
+    let _exportCache = [];
     // 操作状态
     const STATE = {
         checking: false, // 自检
@@ -54,10 +54,10 @@ const EXPORT_VIEW = (() => {
                         const exportData = await _base.extractExportData(INTERFACE_EXPORT.entry, requestForSummaryInfo, boqType, areaKey, projectID, userID);
                         _exportCache.push(...exportData);
                     }
-                    if (_exportCache && _exportCache.length) {
-                        // 导出文件
-                        await _base.exportFile(_exportCache, INTERFACE_EXPORT.saveAsFile || null);
-                    }
+                }
+                if (_exportCache && _exportCache.length) {
+                    // 导出文件
+                    await _base.exportFile(_exportCache, INTERFACE_EXPORT.saveAsFile || INTERFACE_EXPORT_BASE.defaultSaveAs);
                 }
             } catch (err) {
                 console.log(err);
@@ -80,7 +80,7 @@ const EXPORT_VIEW = (() => {
             resetState();
         });
     }
-    return { exportListener }
+    return { exportListener, resetState }
 })();
 
 $(document).ready(() => {

+ 4 - 0
web/building_saas/standard_interface/index.js

@@ -51,10 +51,14 @@ const STD_INTERFACE = (() => {
         $subAreas.html(subAreasHtml);
         // 父级地区变更,子地区选项更新
         $parentAreas.change(function () {
+            EXPORT_VIEW.resetState(); // 清空接口缓存
             const curArea = $(this).val();
             const subAreasHtml = parentMap[curArea].reduce((acc, area) => acc += `<option value="${area}">${area}</option>`, '');
             $subAreas.html(subAreasHtml);
         });
+        $subAreas.change(function() {
+            EXPORT_VIEW.resetState();
+        })
     }
 
     /**