Jelajahi Sumber

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

TonyKang 5 tahun lalu
induk
melakukan
5590297cd0
28 mengubah file dengan 1040 tambahan dan 738 penghapusan
  1. 5 0
      config/gulpConfig.js
  2. 11 11
      gulpfile.js
  3. 1 0
      lib/x2js/xml2json.min.js
  4. 9 0
      modules/pm/controllers/pm_controller.js
  5. 29 0
      modules/pm/facade/pm_facade.js
  6. 2 1
      modules/pm/models/project_property_template.js
  7. 1 0
      modules/pm/routes/pm_route.js
  8. 4 4
      public/web/gljUtil.js
  9. 63 0
      public/web/upload_cdn.js
  10. 4 4
      web/building_saas/main/html/main.html
  11. 2 2
      web/building_saas/main/js/models/cache_tree.js
  12. 18 6
      web/building_saas/main/js/models/project.js
  13. 2 2
      web/building_saas/main/js/models/project_glj.js
  14. 18 13
      web/building_saas/main/js/views/material_calc_view.js
  15. 2 1
      web/building_saas/main/js/views/project_property_decimal_view.js
  16. 49 0
      web/building_saas/pm/html/project-management.html
  17. 12 0
      web/building_saas/pm/js/pm_newMain.js
  18. 1 1
      web/building_saas/pm/js/pm_tree.js
  19. 7 0
      web/building_saas/standard_interface/config.js
  20. 114 672
      web/building_saas/standard_interface/export/anhui_chizhou.js
  21. 10 3
      web/building_saas/standard_interface/export/anhui_maanshan.js
  22. 109 11
      web/building_saas/standard_interface/export/guangdong_zhongshan.js
  23. 1 1
      web/building_saas/standard_interface/export/view.js
  24. 196 0
      web/building_saas/standard_interface/import/anhui_maanshan.js
  25. 274 0
      web/building_saas/standard_interface/import/base.js
  26. 84 0
      web/building_saas/standard_interface/import/view.js
  27. 9 5
      web/building_saas/standard_interface/index.js
  28. 3 1
      web/over_write/js/quanguo_2018.js

+ 5 - 0
config/gulpConfig.js

@@ -45,6 +45,7 @@ module.exports = {
         'public/web/syntax-detection.js',
         'public/web/uuid.js',
         'public/web/date_util.js',
+        'public/web/upload_cdn.js',
         'public/web/id_tree.js',
         'public/web/tree_sheet/tree_sheet_helper.js',
         'public/web/sheet/sheet_data_helper.js',
@@ -57,6 +58,10 @@ module.exports = {
         'public/web/socket/connection.js',
         'web/common/components/share/index.js',
         'web/building_saas/pm/js/**/*.js',
+        'web/building_saas/standard_interface/config.js',
+        'web/building_saas/standard_interface/index.js',
+        'web/building_saas/standard_interface/import/base.js',
+        'web/building_saas/standard_interface/import/view.js',
         'lib/ztree/*.js',
         'lib/jquery-contextmenu/jquery.contextMenu.min.js'
     ],

+ 11 - 11
gulpfile.js

@@ -99,8 +99,8 @@ let compleGljOptions = {
     htmlDest: 'web/building_saas/complementary_glj_lib/html',
     htmlName: 'tools-gongliaoji.html',
     injectList: [
-        'web/dest/scripts/compleGlj.all.min' + version + '.js',
-        'web/dest/css/compleGlj.all.min' + version + '.css',
+        'web/dest/scripts/compleGlj.all.min.' + version + '.js',
+        'web/dest/css/compleGlj.all.min.' + version + '.css',
         'web/dest/css/common.all.min.' + version + '.css'
     ]
 };
@@ -115,8 +115,8 @@ let compleRation_rationOptions = {
     htmlDest: 'web/building_saas/complementary_ration_lib/html',
     htmlName: 'dinge.html',
     injectList: [
-        'web/dest/scripts/compleRation_ration.all.min' + version + '.js',
-        'web/dest/css/compleRation_ration.all.min' + version + '.css',
+        'web/dest/scripts/compleRation_ration.all.min.' + version + '.js',
+        'web/dest/css/compleRation_ration.all.min.' + version + '.css',
         'web/dest/scripts/common.all.min.'+version+'.js',
         'web/dest/css/common.all.min.' + version + '.css'
     ]
@@ -132,8 +132,8 @@ let compleRation_gljOptions = {
     htmlDest: 'web/building_saas/complementary_ration_lib/html',
     htmlName: 'gongliao.html',
     injectList: [
-        'web/dest/scripts/compleRation_glj.all.min' + version + '.js',
-        'web/dest/scripts/compleRation_glj.all.min' + version + '.css',
+        'web/dest/scripts/compleRation_glj.all.min.' + version + '.js',
+        'web/dest/scripts/compleRation_glj.all.min.' + version + '.css',
         'web/dest/scripts/common.all.min.'+version+'.js',
         'web/dest/css/common.all.min.' + version + '.css'
     ]
@@ -149,8 +149,8 @@ let compleRation_coeOptions = {
     htmlDest: 'web/building_saas/complementary_ration_lib/html',
     htmlName: 'fuzhu.html',
     injectList: [
-        'web/dest/scripts/compleRation_coe.all.min' + version + '.js',
-        'web/dest/scripts/compleRation_coe.all.min' + version + '.css',
+        'web/dest/scripts/compleRation_coe.all.min.' + version + '.js',
+        'web/dest/scripts/compleRation_coe.all.min.' + version + '.css',
         'web/dest/scripts/common.all.min.'+version+'.js',
         'web/dest/css/common.all.min.' + version + '.css'
     ]
@@ -166,8 +166,8 @@ let compleRation_instOptions = {
     htmlDest: 'web/building_saas/complementary_ration_lib/html',
     htmlName: 'anzhuang.html',
     injectList: [
-        'web/dest/scripts/compleRation_inst.all.min' + version + '.js',
-        'web/dest/scripts/compleRation_inst.all.min' + version + '.css',
+        'web/dest/scripts/compleRation_inst.all.min.' + version + '.js',
+        'web/dest/scripts/compleRation_inst.all.min.' + version + '.css',
         'web/dest/scripts/common.all.min.'+version+'.js',
         'web/dest/css/common.all.min.' + version + '.css'
     ]
@@ -387,4 +387,4 @@ gulp.task('unit_price',gulp.series('unitPrice_inject', function (){
   return htmlmin(unitPriceOptions);
 }));
 
-gulp.task('build',gulp.series('common','header','login','pm','main', 'compleGlj', 'compleRation_ration', 'compleRation_glj', 'compleRation_coe', 'compleRation_inst','unit_price'));
+gulp.task('build', gulp.series('common', 'header', 'login', 'pm', 'main', 'compleGlj', 'compleRation_ration', 'compleRation_glj', 'compleRation_coe', 'compleRation_inst', 'unit_price'));

File diff ditekan karena terlalu besar
+ 1 - 0
lib/x2js/xml2json.min.js


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

@@ -928,6 +928,15 @@ module.exports = {
             callback(req, res, 1, err, null);
         }
     },
+    getImportTemplateData: async function (req, res) {
+        try {
+            const data = JSON.parse(req.body.data);
+            const templateData = await pm_facade.getImportTemplateData(req.session.sessionCompilation._id, data.valuationID, data.feeRateStandard);
+            callback(req, res, 0, 'success', templateData);
+        } catch (err) {
+            callback(req, res, 1, err, null);
+        }
+    }
 };
 
 

+ 29 - 0
modules/pm/facade/pm_facade.js

@@ -10,6 +10,7 @@
 //先导出后require可以解决循环引用问题
 module.exports={
     getBasicInfo,
+    getImportTemplateData,
     getProjectByGranularity,
     prepareShareList,
     getShareList,
@@ -115,6 +116,8 @@ let index = require("../../system_setting/model/index");
 const compilationModel = mongoose.model('compilation');
 const engineeringModel = mongoose.model('engineering_lib');
 const basicInfoModel = mongoose.model('std_basic_info_lib');
+const stdBillsTemplateModel = mongoose.model('std_bills_template_items');
+const stdFeeRateModel = mongoose.model('std_fee_rate_libs');
 let qiniu = require("qiniu");
 let fs = require("fs");
 let path = require("path");
@@ -2316,4 +2319,30 @@ async function getBasicInfo(compilationID, fileKind = null) {
         infoLib.info = needfulData;
     }
     return infoLib;
+}
+
+// 获取导入接口功能的模板数据,用于将导入数据与模板数据进行合并生成新的项目
+async function getImportTemplateData(compilationID, valuationID, feeRateStandard) {
+    const engineeringLib = await engineeringModel.findOne({ feeName: '公路工程', valuationID }).lean();
+    if (!engineeringLib) {
+        return null;
+    }
+    const infoLib = await getBasicInfo(compilationID, BOQType.BID_SUBMISSION);
+    const featureLibID = engineeringLib.feature_lib[0].id;
+    const featureLib = await featureLibModel.findOne({ ID: featureLibID }).lean();
+    const billsTemplateLibID = engineeringLib.tax_group[0].template_lib.id;
+    const billsTemplate = await stdBillsTemplateModel.find({ libID: billsTemplateLibID }).lean();
+    let feeRateTemplate;
+    if (feeRateStandard) {
+        feeRateTemplate = await stdFeeRateModel.findOne({ libName: feeRateStandard }).lean();
+    } else {
+        const feeRateLibID = engineeringLib.tax_group[0].fee_lib.id;
+        feeRateTemplate = await stdFeeRateModel.findOne({ ID: feeRateLibID }).lean();
+    }
+    return {
+        basicInfo: infoLib && infoLib.info || [],
+        feature: featureLib && featureLib.feature || [],
+        bills: billsTemplate,
+        feeRate: feeRateTemplate && feeRateTemplate.rates || []
+    };
 }

+ 2 - 1
modules/pm/models/project_property_template.js

@@ -11,7 +11,8 @@ const defaultDecimal = {
     quantity_detail: 4,
     material:5,//三材系数
     process: 6,
-    marketPriceProcess:2
+    marketPriceProcess: 2,
+    temProcess:6
 };
 const displaySetting = {
     autoHeight:true,

+ 1 - 0
modules/pm/routes/pm_route.js

@@ -66,6 +66,7 @@ module.exports = function (app) {
     pmRouter.post('/copyConstructionProject',systemMiddleware.tenderNumberChecking,pmController.copyConstructionProject);
     pmRouter.post('/importProcessChecking', pmController.importProcessChecking);
     pmRouter.post('/getProjectByGranularity', pmController.getProjectByGranularity);
+    pmRouter.post('/getImportTemplateData', pmController.getImportTemplateData);
 
 
     app.use('/pm/api', pmRouter);

+ 4 - 4
public/web/gljUtil.js

@@ -330,9 +330,10 @@ let gljUtil = {
             tem_marketPrice = ext[tem.id].marketPrice;
           };
 
-          let temP = scMathUtil.roundForObj(
-            scMathUtil.roundForObj(tem_marketPrice * priceCoe, price_decimal) *
-            scMathUtil.roundForObj(ratio.consumption, quantity_decimal), process_decimal);
+
+          let temP = scMathUtil.roundForObj(tem_marketPrice * priceCoe, price_decimal) * scMathUtil.roundForObj(ratio.consumption, quantity_decimal);
+          if(decimalObj.temProcess) temP = scMathUtil.roundForObj(temP, decimalObj.temProcess);//取两次防止中间过程使用两位导致 4舍5入后少0.01的情况
+          temP = scMathUtil.roundForObj(temP,process_decimal);
           p = scMathUtil.roundForObj(temP + p, process_decimal);
         }
       }
@@ -656,7 +657,6 @@ let gljUtil = {
     CONSTRUCTION_TECH: 3
   },
 
-
   gljType: {
     LABOUR: 1, // 人工
     // ==============材料类型 ↓=================

+ 63 - 0
public/web/upload_cdn.js

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

+ 4 - 4
web/building_saas/main/html/main.html

@@ -2157,10 +2157,10 @@
     <script type="text/javascript" src="/web/building_saas/main/js/views/divide_view.js"></script>
     <script type="text/javascript" src="/public/web/storageUtil.js"></script>
     <script type="text/javascript" src="/web/building_saas/report/js/rpt_jspdf.js"></script>
-    <script src="/web/building_saas/standard_interface/config.js"></script>;
-    <script src="/web/building_saas/standard_interface/index.js"></script>;
-    <script src="/web/building_saas/standard_interface/export/base.js"></script>;
-    <script src="/web/building_saas/standard_interface/export/view.js"></script>;
+    <script src="/web/building_saas/standard_interface/config.js"></script>
+    <script src="/web/building_saas/standard_interface/index.js"></script>
+    <script src="/web/building_saas/standard_interface/export/base.js"></script>
+    <script src="/web/building_saas/standard_interface/export/view.js"></script>
 
     <!-- endinject -->
 

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

@@ -480,11 +480,11 @@ var cacheTree = {
             this.sortTreeItems();
             return rst;
         };
-        Tree.prototype.delete = function (node) {
+        Tree.prototype.delete = function (node,resetPre=true) {
             var success = false;
             success=this.cascadeRemove(node);
             this.sortTreeItems();
-            this.preSelected = null;
+            if(resetPre) this.preSelected = null;
             return success;
         };
         Tree.prototype.cascadeRemove = function (node) {

+ 18 - 6
web/building_saas/main/js/models/project.js

@@ -523,12 +523,20 @@ var PROJECT = {
 
 
           function deleteTreeNodes(deleteNodes) {
-              let controller = projectObj.mainController, project = projectObj.project;
-              let Bill = project.Bills, Ration = project.Ration;
-              for(let rd of deleteNodes){
-                  controller.sheet.deleteRows(rd.serialNo(),1);
+            let controller = projectObj.mainController, project = projectObj.project;
+            let Bill = project.Bills, Ration = project.Ration;
+            let selected = controller.tree.selected;
+            let selected_serialNo = selected.serialNo();
+              for (let rd of deleteNodes) {
+                controller.sheet.deleteRows(rd.serialNo(), 1);
+                if (selected.data.ID == rd.data.ID) {
                   controller.tree.delete(rd);
-                  if(rd.sourceType == Bill.getSourceType()) Bill.tree.delete(rd.source);
+                  controller.setTreeSelected(controller.tree.items[selected_serialNo] ? controller.tree.items[selected_serialNo] : controller.tree.items[controller.tree.items.length - 1]);
+                } else {
+                  controller.tree.delete(rd, false);
+                }
+
+                if (rd.sourceType == Bill.getSourceType()) Bill.tree.delete(rd.source);
               }
           }
           function addNewNodes(newDatas,refreshNode) {
@@ -553,7 +561,11 @@ var PROJECT = {
                   newAddNode.push(newNode);
                   refreshNode.push(newNode);
               }
-              TREE_SHEET_HELPER.refreshTreeNodeData(controller.setting, controller.sheet, newAddNode, false);
+            if (newAddNode.length == 0) return;
+            let sels = controller.sheet.getSelections();
+            controller.setTreeSelected(newAddNode[newAddNode.length -1]);
+            controller.sheet.setSelection(newAddNode[newAddNode.length - 1].serialNo(), sels[0].col, 1, 1);
+            TREE_SHEET_HELPER.refreshTreeNodeData(controller.setting, controller.sheet, newAddNode, false);
           }
         };
         project.prototype.setValue=function (obj,key,value) {

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

@@ -372,7 +372,7 @@ ProjectGLJ.prototype.calcAllMaterial = function (unitPriecs,needReCalc) {//当
             let fOrPDataMap = {};
             let tIndex = gljUtil.getIndex(g);
             //运费和原价中的工料机只要有一个包含该材料,就要重算
-            if(freightListMap[tIndex]){ //运费重算
+          if (freightListMap[tIndex]) { //运费重算
                 let freightUpdateMap = {};
                 for(let f of freightListMap[tIndex]){
                     if(f.conveyance != "自办运输") continue;//不是自办运输的不用管
@@ -388,7 +388,7 @@ ProjectGLJ.prototype.calcAllMaterial = function (unitPriecs,needReCalc) {//当
                 if(!_.isEmpty(freightUpdateMap)) fOrPDataMap["freight"] = {update:freightUpdateMap};
             }
             //原价重算
-            if(originalListMap[tIndex]){ //运费重算
+            if(originalListMap[tIndex]){ 
                 let originalUpdateMap = {};
                 for(let o of originalListMap[tIndex]){
                     let calo = ifNeedReCalc(o,priceMap,needReCalc);

+ 18 - 13
web/building_saas/main/js/views/material_calc_view.js

@@ -56,7 +56,7 @@ materialCalcObj = {
         },
          getText:{
           forExp:function (item) {
-             return item.exp&&item.exp!=""?item.exp:materialCalcObj.getFreightEXP(item);
+             return gljUtil.isDef(item.exp)?item.exp:materialCalcObj.getFreightEXP(item);
           }
       }
 
@@ -568,9 +568,10 @@ materialCalcObj = {
                 if (parseInt(row) >= me.freightDatas.length) {//新增
                     let tempFreight = null;
                     if (dataCode == "start") {
-                        tempFreight = me.getSavedFreight(value, material);
+                      tempFreight = me.getSavedFreight(value, material);
                     }
                     if (tempFreight) {
+                        if (tempFreight.conveyance == "自办运输") tempFreight.exp = "";
                         newData = tempFreight;
                     } else {
                         if(_.isEmpty(newData)) newData = me.getNewFreightData(material);
@@ -817,7 +818,7 @@ materialCalcObj = {
             for(let key in record){
                 if(key == "_id") continue;
                 f[key] = record[key];
-                if(key =="ration" || key == "ration_gljs")f[key] = [];//下面的定额和工料机不用保存
+                if(key =="rations" || key == "ration_gljs")f[key] = [];//下面的定额和工料机不用保存
             }
             user_freight.freight = f;
             projectObj.project.projectGLJ.updateUserFreight({freights:[user_freight],action:"add"});
@@ -825,22 +826,26 @@ materialCalcObj = {
     },
 
     updateUserFreight:function (record,userFreight) {
-        let t  = {};
-        for(let t_key in userFreight){
-            if(t_key == "ID") continue;
-            t[t_key] = userFreight[t_key];
+        let t = {};
+        for (let t_key in userFreight) {
+          if (t_key == "ID") continue;
+          t[t_key] = userFreight[t_key];
         }
 
         t.rootProjectID = projectObj.project.projectInfo.property.rootProjectID;
         let f = {};
-        for(let key in record){
-            if(key == "_id") continue;
-            f[key] = record[key];
-            if(key =="ration" || key == "ration_gljs")f[key] = [];//下面的定额和工料机不用保存
+        for (let key in record) {
+          if (key == "_id") continue;
+          f[key] = record[key];
+          if (key == "rations" || key == "ration_gljs") f[key] = []; //下面的定额和工料机不用保存
         }
+        if (record.conveyance == "自办运输") f.exp = '';
         t.freight = f;
-        projectObj.project.projectGLJ.updateUserFreight({ID:userFreight.ID,doc:t,action:"update"});
-
+        projectObj.project.projectGLJ.updateUserFreight({
+          ID: userFreight.ID,
+          doc: t,
+          action: "update"
+        });
     },
 
 

+ 2 - 1
web/building_saas/main/js/views/project_property_decimal_view.js

@@ -13,7 +13,8 @@ let defaultDecimal = {
         feeRate: {editable: true, data: 3},
         quantity_detail: {editable: false, data: 4},
         process: {editable: false, data: 6},
-        marketPriceProcess:{editable: false, data: 2}
+        marketPriceProcess: { editable: false, data: 2 },
+        temProcess:{editable: false, data: 6}
     }
 };
 

+ 49 - 0
web/building_saas/pm/html/project-management.html

@@ -705,8 +705,52 @@
     </div>
 </div>
 
+<!--弹出 数据接口导出-->
+<div class="modal fade" id="interface-import-modal" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">电子招投标数据接口
+                </h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <div class="form-group row">
+                    <label for="staticEmail" class="col-auto col-form-label col-form-label-sm">选择文件</label>
+                    <div class="col">
+                        <div class="custom-file custom-file-sm">
+                            <input type="file" class="custom-file-input" id="interface-import-file" lang="zh">
+                            <label class="custom-file-label" for="customFile" id="interface-import-label" style="white-space: nowrap; overflow: hidden;">请选择导入文件</label>
+                        </div>
+                    </div>
+                </div>
+                <div class="form-group row">
+                    <label class="mb-0 pr-0 col-2 col-form-label col-form-label-sm">地区选择</label>
+                    <div class="col-6 row" style="margin-left: -11px;">
+                        <div class="col-6 pr-0">
+                            <select class="form-control  form-control-sm" id="import-parent-area">
+                            </select>
+                        </div>
+                        <div class="col-6 pr-0">
+                            <select class="form-control  form-control-sm" id="import-sub-area">
+                            </select>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <a id="interface-import-confirm" href="javascript:void(0);" class="btn btn-primary">确定导入</a>
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
+
 <!-- JS. -->
 <script src = "/lib/spreadjs/sheets/gc.spread.sheets.all.11.1.2.min.js"></script>
+<script src="/lib/x2js/xml2json.min.js"></script>
 <script src = "/lib/fileSaver/FileSaver.min.js"></script>
 <script src="/lib/qiniu/qiniu.min.js"></script>
 <script>GC.Spread.Sheets.LicenseKey = '<%- LicenseKey %>';</script>
@@ -715,6 +759,7 @@
 <script src="/web/building_saas/js/global.js"></script>
 <script src="/public/web/uuid.js"></script>
 <script src="/public/web/date_util.js"></script>
+<script src="/public/web/upload_cdn.js"></script>
 <script src="/web/building_saas/pm/js/pm_tree.js"></script>
 <script src="/public/web/id_tree.js"></script>
 <script src="/public/web/tree_sheet/tree_sheet_helper.js"></script>
@@ -731,6 +776,10 @@
 <script src="/web/building_saas/pm/js/pm_newMain.js"></script>
 <script src="/web/building_saas/pm/js/pm_gc.js"></script>
 <script src="/web/building_saas/pm/js/pm_share.js"></script>
+<script src="/web/building_saas/standard_interface/config.js"></script>
+<script src="/web/building_saas/standard_interface/index.js"></script>
+<script src="/web/building_saas/standard_interface/import/base.js"></script>
+<script src="/web/building_saas/standard_interface/import/view.js"></script>
 <!-- zTree -->
 <script type="text/javascript" src="/lib/ztree/jquery.ztree.core.js"></script>
 <script type="text/javascript" src="/lib/ztree/jquery.ztree.excheck.js"></script>

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

@@ -333,6 +333,17 @@ const projTreeObj = {
                 }
             }
         },
+        importInterface: {
+            name: '导入接口文件',
+            icon: 'fa-cloud-upload',
+            visible: function () {
+                const names = ['全国公路(2018)'];
+                return compilationData && names.includes(compilationData.name);
+            },
+            callback: function () {
+                $('#interface-import-modal').modal('show');
+            }
+        },
         manageFiles: {
             name: "管理相关文件",
             icon: 'fa-list',
@@ -441,6 +452,7 @@ const projTreeObj = {
                 "saveAs": me.contextMenuItems.saveAs,
                 "spr2": '--------',
                 "share": me.contextMenuItems.share,
+                "importInterface": me.contextMenuItems.importInterface,
                 "spr3": '--------',
                 "manageFiles": me.contextMenuItems.manageFiles,
                 "refreshSummary": me.contextMenuItems.refreshSummary,

+ 1 - 1
web/building_saas/pm/js/pm_tree.js

@@ -333,7 +333,7 @@ const pmTree = {
           node.children = parentMap[node.data.ID] || [];
           node.parent = IDMap[node.data.ParentID] || this._root;
         });
-        const roots = parentMap['-1'];
+        const roots = parentMap['-1'] || [];
         // 将各节点的children进行排序,并设置nextSibling
         this._root.children = sortChildren(roots);
         // 生成tree.items

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

@@ -17,6 +17,7 @@ const INTERFACE_CONFIG = (() => {
   return {
     '安徽@马鞍山': {
       scriptName: 'anhui_maanshan.js',
+      feeRateStandard: '安徽估概预算-皖交建管函[2019]210号',
       fileSuffix: {
         [BID_INVITATION]: '.MASGLZB',
         [BID_SUBMISSION]: '.MASGLTB',
@@ -25,6 +26,7 @@ const INTERFACE_CONFIG = (() => {
     },
     '安徽@淮北': {
       scriptName: 'anhui_maanshan.js',
+      feeRateStandard: '安徽估概预算-皖交建管函[2019]210号',
       fileSuffix: {
         [BID_INVITATION]: '.HBGLZB',
         [BID_SUBMISSION]: '.HBGLTB',
@@ -33,6 +35,7 @@ const INTERFACE_CONFIG = (() => {
     },
     '安徽@铜陵': {
       scriptName: 'anhui_maanshan.js',
+      feeRateStandard: '安徽估概预算-皖交建管函[2019]210号',
       fileSuffix: {
         [BID_INVITATION]: '.TLGLZB',
         [BID_SUBMISSION]: '.TLGLTB',
@@ -41,6 +44,7 @@ const INTERFACE_CONFIG = (() => {
     },
     '安徽@芜湖': {
       scriptName: 'anhui_maanshan.js',
+      feeRateStandard: '安徽估概预算-皖交建管函[2019]210号',
       fileSuffix: {
         [BID_INVITATION]: '.WHGLZB',
         [BID_SUBMISSION]: '.WHGLTB',
@@ -49,6 +53,7 @@ const INTERFACE_CONFIG = (() => {
     },
     '安徽@黄山': {
       scriptName: 'anhui_maanshan.js',
+      feeRateStandard: '安徽估概预算-皖交建管函[2019]210号',
       fileSuffix: {
         [BID_INVITATION]: '.HSGLZB',
         [BID_SUBMISSION]: '.HSGLTB',
@@ -57,6 +62,7 @@ const INTERFACE_CONFIG = (() => {
     },
     '安徽@宣城': {
       scriptName: 'anhui_maanshan.js',
+      feeRateStandard: '安徽估概预算-皖交建管函[2019]210号',
       fileSuffix: {
         [BID_INVITATION]: '.XCGLZB',
         [BID_SUBMISSION]: '.XCGLTB',
@@ -65,6 +71,7 @@ const INTERFACE_CONFIG = (() => {
     },
     '安徽@池州': {
       scriptName: 'anhui_chizhou.js',
+      feeRateStandard: '安徽估概预算-皖交建管函[2019]210号',
       fileSuffix: {
         [BID_INVITATION]: '.CZGLZB',
         [BID_SUBMISSION]: '.CZLTB',

+ 114 - 672
web/building_saas/standard_interface/export/anhui_chizhou.js

@@ -39,721 +39,163 @@ INTERFACE_EXPORT = (() => {
         const isBidSubmission = exportKind === BID_SUBMISSION; // 是否是投标
         const isControl = exportKind === CONTROL; // 是否是控制价
 
-        // 节点定义--------------------------------
-        const subArea = areaKey.split('@')[1];
-
-        function ChiZGLGC() {
-            Element.call(this, '池州公路工程');
+// 节点定义------------------------------------------------------------------------------------------------------------
+        function CreateNode(nodeName, attrArr = []) {
+            Element.call(this, nodeName, attrArr);
         }
 
         function GongCXX(information) {
             const attrs = [
-                { name: '项目编号', value: getValueByKey(information, 'tendereeName') }, // 招标人
-                { name: '项目名称', value: getValueByKey(information, 'costConsultant') }, // 造价咨询人
-                { name: '建设单位', value: getValueByKey(information, 'tenderAuthorizer') }, // 招标人法定代表人或其授权人
-                { name: '起始桩号', value: getValueByKey(information, 'consultantAuthorizer') }, // 造价咨询人法定代表人或其授权人
-                { name: '终点桩号', value: getValueByKey(information, 'tenderCompiler') }, // 编制人
-                { name: '建设地址', value: getValueByKey(information, 'tenderExaminer') }, // 复核人
-                { name: '项目概况', value: getValueByKey(information, 'compilationTime'), type: TYPE.DATE }, // 编制时间
-                { name: '建设性质', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE }, // 复核时间
-                { name: '专业划分', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE }, // 复核时间
-                { name: '道路里程', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE }, // 复核时间
-                { name: '设计单位', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE }, // 复核时间
-                { name: '计税方式', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE }, // 复核时间
-                { name: '文件类型', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE }, // 复核时间
-                { name: '标准版本号', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE }, // 复核时间
-                { name: 'GUID', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE }, // 复核时间
+                { name: '项目编号', value: getValueByKey(information, 'projNum') },
+                { name: '项目名称', value: getValueByKey(information, '') },
+                { name: '建设单位', value: getValueByKey(information, 'constructingUnits') },
+                { name: '起始桩号', value: getValueByKey(information, 'startAndChainages') },
+                { name: '终点桩号', value: getValueByKey(information, 'startAndChainages') },
+                { name: '建设地址', value: getValueByKey(information, '') },
+                { name: '项目概况', value: getValueByKey(information, '') },
+                { name: '建设性质', value: getValueByKey(information, '') },
+                { name: '专业划分', value: getValueByKey(information, '') },
+                { name: '道路里程', value: getValueByKey(information, '') },
+                { name: '设计单位', value: getValueByKey(information, '') },
+                { name: '计税方式', value: getValueByKey(information, 'taxMode') },
+                { name: '文件类型', value: getValueByKey(information, 'projType') },
+                { name: '标准版本号', value: getValueByKey(information, '') },
+                { name: 'GUID', value: getValueByKey(information, '') }
             ];
             Element.call(this, '工程信息', attrs);
         }
-
-        function ZhaoTBXX(){
-            Element.call(this, '招投标信息');
-        }
-
-        function GongLGCSJ(){
-            Element.call(this, '公路工程数据');
-        }
-
         function ZhaoBiaoXx(information) {
             const attrs = [
-                { name: '招标人', 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: '招标人', value: getValueByKey(information, 'tendereeName') },
+                { name: '招标人纳税识别号', value: getValueByKey(information, 'tendereeTaxpayerIdentificationNo') },
+                { name: '招标法定代表人或其授权人', value: getValueByKey(information, 'tenderAuthorizer') },
+                { name: '招标法人或其授权人身份证号', value: getValueByKey(information, 'tenderAuthorizerIDNo') },
+                { name: '造价咨询人', value: getValueByKey(information, 'costConsultant') },
+                { name: '造价咨询人纳税识别号', value: getValueByKey(information, 'costConsultantTaxpayerIdentificationNo') },
+                { name: '造价咨询人法定代表人或其授权人', value: getValueByKey(information, 'consultantAuthorizer') },
+                { name: '造价咨询法人或其授权人身份证号', value: getValueByKey(information, 'consultantAuthorizerTaxpayerIdentificationNo') },
+                { name: '编制人', value: getValueByKey(information, 'tenderCompiler') },
+                { name: '编制人资格证号', value: getValueByKey(information, '') },
+                { name: '编制日期', value: getValueByKey(information, 'compilationTime'), type: TYPE.DATE},
+                { name: '复核人', value: getValueByKey(information, 'tenderExaminer') },
+                { name: '复核人资格证号', value: getValueByKey(information, '') },
+                { name: '复核日期', 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, '招标信息', 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 }, // 控制价总价(元),取“投标报价”的金额。
+                { name: '招标人', value: getValueByKey(information, 'tendereeName') },
+                { name: '招标人纳税识别号', value: getValueByKey(information, 'tendereeTaxpayerIdentificationNo') },
+                { name: '招标法定代表人或其授权人', value: getValueByKey(information, 'tenderAuthorizer') },
+                { name: '招标法人或其授权人身份证号', value: getValueByKey(information, 'tenderAuthorizerIDNo') },
+                { name: '造价咨询人', value: getValueByKey(information, 'costConsultant') },
+                { name: '造价咨询人纳税识别号', value: getValueByKey(information, 'costConsultantTaxpayerIdentificationNo') },
+                { name: '造价咨询人法定代表人或其授权人', value: getValueByKey(information, 'consultantAuthorizer') },
+                { name: '造价咨询法人或其授权人身份证号', value: getValueByKey(information, 'consultantAuthorizerTaxpayerIdentificationNo') },
+                { name: '编制人', value: getValueByKey(information, 'tenderCompiler') },
+                { name: '编制人资格证号', value: getValueByKey(information, '') },
+                { name: '编制日期', value: getValueByKey(information, 'compilationTime'), type: TYPE.DATE},
+                { name: '复核人', value: getValueByKey(information, 'tenderExaminer') },
+                { name: '复核人资格证号', value: getValueByKey(information, '') },
+                { name: '复核日期', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE },
+                { name: '审核人', value: getValueByKey(information, 'tenderExaminer') },
+                { name: '审核人资格证号', value: getValueByKey(information, '') },
+                { name: '审核日期', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE },
+                { name: '控制价总价', value: totalCost, type: TYPE.DECIMAL },
+                { name: '工期', value: getValueByKey(information, '') },
+                { name: '质量要求', value: getValueByKey(information, '') }
             ];
-            // 额外字段
-            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, '招标控制价', 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 }, // 控制价总价(元),取“投标报价”的金额。
+                { name: '投标人', value: getValueByKey(information, 'bidderName') },
+                { name: '投标人纳税识别号', value: getValueByKey(information, '') },
+                { name: '投标人法定代表或其授权人', value: getValueByKey(information, 'bidderAuthorizer') },
+                { name: '投标人法人或其授权人身份证号', value: getValueByKey(information, '') },
+                { name: '编制人', value: getValueByKey(information, 'tenderCompiler') },
+                { name: '编制人资格证号', value: getValueByKey(information, '') },
+                { name: '编制日期', value: getValueByKey(information, 'compilationTime'), type: TYPE.DATE},
+                { name: '复核人', value: getValueByKey(information, 'tenderExaminer') },
+                { name: '复核人资格证号', value: getValueByKey(information, '') },
+                { name: '复核日期', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE },
+                { name: '审核人', value: getValueByKey(information, 'tenderExaminer') },
+                { name: '审核人资格证号', value: getValueByKey(information, '') },
+                { name: '审核日期', value: getValueByKey(information, 'reviewTime'), type: TYPE.DATE },
+                { name: '投标总价', value: totalCost, type: TYPE.DECIMAL },
+                { name: '工期', value: getValueByKey(information, '') },
+                { name: '投标担保金额', value: getValueByKey(information, '') },
+                { name: '质量承诺', value: getValueByKey(information, '') },
+                { name: '投标担保方式', value: getValueByKey(information, '') },
+                { name: '造价软件品牌', value: getValueByKey(information, '') },
+                { name: '造价软件版本', value: getValueByKey(information, '') },
+                { name: '造价软件加密锁编号', value: getValueByKey(information, '') },
+                { name: '计算机硬件信息', value: getValueByKey(information, '') },
+                { name: '', value: getValueByKey(information, '') }
             ];
-            // 额外字段
-            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, '投标信息', 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 }, // 单位工程名称
+        // 生成标段工程
+        function CreateGLBDGC(tenderData, gongLGCSJ, gongLGCHZ, Num){
+            let attrs0 = [
+                { name: '序号', value: Num },
+                { name: '标段名称', value: tenderData.name },
+                { name: '金额', value: projectData.summaryInfo[tenderData.ID].totalCost }
             ];
-            Element.call(this, 'Dwgcxx', attrs);
-        }
 
-        // 取费信息(费率信息)
-        function Qfxx() {
-            Element.call(this, 'Qfxx');
-        }
+            const attrs1 = [{ name: '唯一标识-Guid', value: tenderData._id }];           // 公路标段工程
+            const gongLBDGC = new CreateNode('公路标段工程', attrs0.concat(attrs1));
+            gongLGCSJ.children.push(gongLBDGC);
+            const gongCLQDB = new CreateNode('工程量清单表');
+            const jiRGXXB = new CreateNode('计日工信息表');
+            const ZaoJHZB = new CreateNode('造价汇总表');
+            const RenCJHZ = new CreateNode('人材机汇总');
+            gongLBDGC.children = [gongCLQDB, jiRGXXB, ZaoJHZB, RenCJHZ];
 
-        // 计价费率表
-        function JjFlb() {
-            Element.call(this, 'JjFlb');
+            const attrs2 = [{ name: '备注', value: '' }];
+            const gongLGCHZMX = new CreateNode('公路工程汇总明细', attrs0.concat(attrs2));
+            gongLGCHZ.children.push(gongLGCHZMX);
         }
 
-        // 计价费率表明细,造价书费率页面左侧最底层数据
-        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 setupConstruction() {
+            const information = projectData.property && projectData.property.basicInformation || [];
+            const summaryInfo = projectData.summaryInfo[projectData.ID];
 
-        // 计价费率项
-        function JjFlx() {
-            Element.call(this, 'JjFlx');
-        }
+            const chiZGLGC = new CreateNode('池州公路工程');
+            const gongCXX = new GongCXX(information);   // 工程信息
+            const zhaoTBXX = new CreateNode('招投标信息');
+            const gongLGCSJ = new CreateNode('公路工程数据');
+            chiZGLGC.children = [gongCXX, zhaoTBXX, gongLGCSJ];
 
-        // 计价费率项明细,造价书费率页面右侧最顶层数据
-        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 chiZGLGC = new ChiZGLGC(constructionData.name, information);
-            chiZGLGC.children.push(new GongCXX(information));
-
-            const zhaoTBXX = new ZhaoTBXX();
-            chiZGLGC.children.push(zhaoTBXX);
-            if (isBidInvitation) {
+            if (isBidInvitation)
                 zhaoTBXX.children.push(new ZhaoBiaoXx(information));
-            } else if (isControl) {
+            else if (isControl)
                 zhaoTBXX.children.push(new ZhaoBiaoKzXx(information, summaryInfo.totalCost));
-            } else {
+            else
                 zhaoTBXX.children.push(new TouBiaoXx(information, summaryInfo.totalCost));
-            }
 
-            const gongLGCSJ = new GongLGCSJ(constructionData.name, information);
-            chiZGLGC.children.push(gongLGCSJ);
+            const gongLGCHZ = new CreateNode('公路工程汇总');
+            gongLGCSJ.children.push(gongLGCHZ);
 
-            // 将单位工程工程特征中,单项工程编号、名称相同的,插入到建设项目和分段(单位工程)的中间层。
-            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]) {
-                //     chiZGLGC.children.push(midLayerMap[midLayerKey] = new Dxgcxx(midLayerCode, midLayerName));
-                // }
-                // midLayerMap[midLayerKey].children.push(setupTender(tenderData, feature));
+            let n = 0;
+            for (const tenderData of projectData.children) {
+                n++;
+                CreateGLBDGC(tenderData, gongLGCSJ, gongLGCHZ, n);
             }
-            // 校验信息
-            const [cpuID, diskID, macID] = generateHardwareId().split(';');
+            gongLGCSJ.children.push(gongLGCSJ.children.shift());
+
             const suffix = INTERFACE_CONFIG[areaKey]['fileSuffix'][exportKind];
-            // 需要md5的值:MD5(“WHGLZTB”+项目名称+机器码+加密锁锁号)机器码取mac地址,加密锁锁号取用户账号id
-            const toMD5Suffix = suffix.split('.')[1];
-            const toMD5Value = `${toMD5Suffix}${constructionData.name}${macID}${userID}`;
-            // chiZGLGC.children.push(new Jyxx(cpuID, diskID, macID, suffix, toMD5Value));
             return [{
                 data: chiZGLGC,
                 exportKind,
-                fileName: `${constructionData.name}${suffix}`
+                fileName: `${projectData.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 setupConstruction();
     }
 
     return {

+ 10 - 3
web/building_saas/standard_interface/export/anhui_maanshan.js

@@ -273,7 +273,7 @@ INTERFACE_EXPORT = (() => {
         value = item.value;
       } else {
         const selected = item.optionList.find(item => item.selected);
-        value = selected.name;
+        value = selected && selected.name || '';
       }
       const attrs = [
         { name: 'Bm', value: code }, // 编码
@@ -586,7 +586,7 @@ INTERFACE_EXPORT = (() => {
       });
       const dwgcxx = new Dwgcxx(tenderData.name, feature);
       dwgcxx.children.push(
-        setupFeeRate(curDetail.FeeRate),
+        setupFeeRate(curDetail.FeeRate, feature),
         setupBills(curDetail.mainTree),
         ...setupGLJList(curDetail)
       );
@@ -594,7 +594,7 @@ INTERFACE_EXPORT = (() => {
     }
 
     // 组装费率数据
-    function setupFeeRate(feeRateDetail) {
+    function setupFeeRate(feeRateDetail, feature) {
       const qfxx = new Qfxx();
       const jjflb = new JjFlb();
       // 费率界面左侧底层数据
@@ -607,7 +607,14 @@ INTERFACE_EXPORT = (() => {
         }
       });
       // 费率界面右侧顶层数据
+      // 除右侧数据外,还应拼凑工程所在地、费率标准的数据。
       const jjflx = new JjFlx();
+      // 工程所在地取建设项目-基本信息中的“工程所在地”;
+      const locationItem = { name: '工程所在地', value: getValueByKey(feature, 'projLocation') };
+      jjflx.children.push(new JjFlxMx(locationItem));
+      // 费率标准取,费率-重选标准中当前用的费率标准名称。
+      const feeRateStandardItem = { name: '费率标准', value: feeRateDetail.datas.libName };
+      jjflx.children.push(new JjFlxMx(feeRateStandardItem));
       const flxmxData = feeRateDetail
         .getAllSubRates()
         .filter(item => !item.isSub)

+ 109 - 11
web/building_saas/standard_interface/export/guangdong_zhongshan.js

@@ -17,12 +17,12 @@ INTERFACE_EXPORT = (() => {
     行车干扰施工增加费: "XCGRGCSGZJFFL",
     行车干扰: "XCGRGCSGZJFFL",
     工地转移费: "GDZYFFL",
-    工地转移: "GDZYFFL",
+    '工地转移(km)': "GDZYFFL",
     施工辅助费: "SGFZFFL",
     施工辅助: "SGFZFFL",
     基本费用: "JBFYFL",
     主副食运费补贴: "ZFSYFBTFL",
-    综合里程: "ZFSYFBTFL",
+    '综合里程(km)': "ZFSYFBTFL",
     职工探亲路费: "ZGTQLFFL",
     职工探亲: "ZGTQLFFL",
     职工取暖补贴: "ZGQNBTFL",
@@ -76,6 +76,19 @@ INTERFACE_EXPORT = (() => {
     'rationCommon': 'DEJAF'
   };
 
+  const feeLibNoMap = {
+    'eba0e7c0-cfe6-11ea-ba15-739be477d1fe': 'GYSFL-000000-2018-86',
+    '451d3180-e02e-11ea-8056-1514dfc2f39e': 'GYSFL-340000-2019-210'
+  };
+
+  const FixedCostMap = {
+    //折旧费、检修费、维护费、安拆辅助费
+    '折旧费': 1,
+    '检修费': 1,
+    '维护费': 1,
+    '安拆辅助费':1
+  }
+
   function getBasePrice(projectGLJID, tenderProject) {
     let glj = _.find(tenderProject.projectGLJ.datas.gljList, {
       id: projectGLJID
@@ -220,7 +233,7 @@ INTERFACE_EXPORT = (() => {
       function NormLib() {
         const attrs = [{
             name: "NormLibNo",
-            value: "0",
+            value: "YSDE-000000-2018-86",
           },
           {
             name: "NormLibName",
@@ -248,7 +261,7 @@ INTERFACE_EXPORT = (() => {
         },
         {
           name: "RateLibNo",
-          value: feeRateInfo.libID,
+          value: feeLibNoMap[feeRateInfo.libID],
         },
       ];
       Element.call(this, "Rate", attrs);
@@ -333,6 +346,7 @@ INTERFACE_EXPORT = (() => {
 
     function Pract(tpdata, seriNo) {
       let pgljData = tpdata.projectGLJ.datas;
+      let decimalObj = tpdata.property.decimal;
       const attrs = [{
           name: "PractNo",
           value: seriNo,
@@ -586,7 +600,7 @@ INTERFACE_EXPORT = (() => {
         }
       }
 
-      function Mech(g) {
+      function Mech(g,gljKeyMap) {
         const attrs = [{
             name: "Code",
             value: g.code,
@@ -617,22 +631,104 @@ INTERFACE_EXPORT = (() => {
           },
         ];
         Element.call(this, "Mech", attrs);
+        let FixedCostItems = [];
+        let fcsum = 0;
+        let VariableCostItems = [];
+        let vcsum = 0;
+        if (g.ratio_data && g.ratio_data.length > 0) { 
+          for (let ratio of g.ratio_data) { 
+            let rIndex = gljUtil.getIndex(ratio);
+            let rglj = gljKeyMap[rIndex];
+            let ts = scMathUtil.roundForObj(parseFloat(ratio.consumption) * rglj.priceInfo.tenderPrice, decimalObj.glj.unitPrice);
+            if (FixedCostMap[ratio.name]) {
+              FixedCostItems.push(new FixedCostItem(ratio));
+              fcsum = scMathUtil.roundForObj(fcsum + ts, decimalObj.glj.unitPrice);
+            } else { 
+              VariableCostItems.push(new VariableCostItem(ratio));
+              vcsum = scMathUtil.roundForObj(vcsum + ts, decimalObj.glj.unitPrice);
+            } 
+          }
+        }
+
+
+        if (FixedCostItems.length > 0) {
+          let fc = new FixedCost(fcsum);
+          fc.children = FixedCostItems;
+          this.children.push(fc);
+        }
+          
+        if (VariableCostItems.length > 0) {
+          let vc = new VariableCost(vcsum);
+          vc.children = VariableCostItems;
+          this.children.push(vc);
+        }
+
+        function VariableCost(sum) { 
+          const attrs = [{
+            name: "VariableCostSum",
+            value: sum,
+          }];
+          Element.call(this, "VariableCost", attrs);
+        }
+        
+        function VariableCostItem(item) { 
+          const attrs = [{
+            name: "VariableCostNo",
+            value: item.code,
+          },{
+            name: "Consumption",
+            value: item.consumption,
+          }]
+          Element.call(this, "VariableCostItem", attrs);
+        }
+
+        function FixedCost(sum) { 
+          const attrs = [{
+            name: "FixedCostSum",
+            value: sum,
+          }, {
+            name: "FixedRate",
+            value: 1,
+          }];
+          Element.call(this, "FixedCost", attrs);
+        }
+
+        function FixedCostItem(item) { 
+          const attrs = [{
+            name: "FixedCostNo",
+            value: item.code,
+          },{
+            name: "Sum",
+            value: item.consumption,
+          }]
+          Element.call(this, "FixedCostItem", attrs);
+
+        }
+        
+        
+
       }
 
       let Mps = new emptyElement("Mps");
       let Materials = new emptyElement("Materials");
       let Mechs = new emptyElement("Mechs");
+      let gljKeyMap = {};
+      let temMechs = [];
       for (let g of pgljData.gljList) {
         let type = g.type + "";
+        gljKeyMap[gljUtil.getIndex(g)] = g;
         type = type.charAt(0);
         if (type == "1") {
           Mps.children.push(new MP(g));
         } else if (type == "2" || type == 5) {
           Materials.children.push(new Material(g));
         } else if (type == "3") {
-          Mechs.children.push(new Mech(g));
+          temMechs.push(g);
         }
       }
+      for (let t of temMechs) { 
+        Mechs.children.push(new Mech(t,gljKeyMap));
+      }
 
       this.children.push(Mps);
       this.children.push(Materials);
@@ -1325,16 +1421,18 @@ INTERFACE_EXPORT = (() => {
     data.children.push(new SystemInfo());
     data.children.push(new CostBasis());
     let seriNo = 1;
+    let Rates = [];
+    let Practs = [];
+    let EprjInfos = [];
     for (let c of projectData.children) {
       let tenderProject = tenderDetailMap[c.ID];
       let feeRataDatas = tenderProject.FeeRate.datas;
-      data.children.push(new Rate(tenderProject, feeRataDatas, seriNo));
-
-      data.children.push(new Pract(tenderProject, seriNo));
-      data.children.push(new EprjInfo(tenderProject));
+      Rates.push(new Rate(tenderProject, feeRataDatas, seriNo));
+      Practs.push(new Pract(tenderProject, seriNo));
+      EprjInfos.push(new EprjInfo(tenderProject));
       seriNo++;
     }
-
+    data.children.push(...Rates,...Practs,...EprjInfos);
     return [{
       data: data,
       exportKind: exportKind,

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

@@ -85,6 +85,6 @@ const EXPORT_VIEW = (() => {
 })();
 
 $(document).ready(() => {
-  $('#interface-export-modal').on('show.bs.modal', () => STD_INTERFACE.initExportAreas($('#export-parent-area'), $('#export-sub-area')));
+  $('#interface-export-modal').on('show.bs.modal', () => STD_INTERFACE.initInterfaceAreas($('#export-parent-area'), $('#export-sub-area')));
   EXPORT_VIEW.exportListener();
 })

+ 196 - 0
web/building_saas/standard_interface/import/anhui_maanshan.js

@@ -0,0 +1,196 @@
+/*
+ * @Descripttion: 安徽马鞍山导入接口
+ * @Author: vian
+ * @Date: 2020-09-09 11:51:15
+ */
+
+// INTERFACE_EXPORT =,必须这么写,这样才能在导入时动态加载脚本后,覆盖前端代码
+INTERFACE_IMPORT = (() => {
+  'use strict';
+
+
+
+  /**
+   * 
+   * @param {String} areaKey - 地区标识,如:'安徽@马鞍山',有些地区的接口只是取值上有不同,共有一个接口脚本, 需要通过地区标识确定一些特殊处理
+   * @param {Object} xmlObj - xml经过x2js转换后的xml对象
+   * @param {Object} template - 单位工程的一些模板数据,例如清单模板、标准费率数据等。
+   */
+  async function entry(areaKey, xmlObj, template) {
+    const {
+      UTIL: {
+        getValue,
+        arrayValue,
+      }
+    } = INTERFACE_EXPORT_BASE;
+
+    const subArea = areaKey.split('@')[1];
+
+    // 提取基本信息,xml中提取出来的基本信息,最终会与模板基本信息进行合并处理。(接口内不需要处理合并)
+    function setupInformation(projectSrc) {
+      // key:基本信息模板中的key,作为合并时的匹配字段
+      const info = [
+        { key: 'projNum', value: getValue(projectSrc, ['_Xmbh']) },
+        { key: 'projType', value: getValue(projectSrc, ['_Bzlx']) },
+        { key: 'startAndChainages', value: getValue(projectSrc, ['_Xmqzzh']) },
+        { key: 'constructingUnits', value: getValue(projectSrc, ['_Jsdw']) },
+        { key: 'taxMode', value: getValue(projectSrc, ['_Jsfs']) },
+        { key: 'tendereeName', value: getValue(projectSrc, ['ZhaoBiaoXx', '_Zbr']) },
+        { key: 'costConsultant', value: getValue(projectSrc, ['ZhaoBiaoXx', '_Zxr']) },
+        { key: 'tenderAuthorizer', value: getValue(projectSrc, ['ZhaoBiaoXx', '_ZbrDb']) },
+        { key: 'consultantAuthorizer', value: getValue(projectSrc, ['ZhaoBiaoXx', '_ZxrDb']) },
+        { key: 'tenderCompiler', value: getValue(projectSrc, ['ZhaoBiaoXx', '_Bzr']) },
+        { key: 'tenderExaminer', value: getValue(projectSrc, ['ZhaoBiaoXx', '_Fhr']) },
+        { key: 'compilationTime', value: getValue(projectSrc, ['ZhaoBiaoXx', '_BzTime']) },
+        { key: 'reviewTime', value: getValue(projectSrc, ['ZhaoBiaoXx', '_FhTime']) },
+      ];
+      if (['淮北', '铜陵'].includes(subArea)) {
+        const extraInfo = [
+          { key: 'tendereeTaxpayerIdentificationNo', value: getValue(projectSrc, ['ZhaoBiaoXx', '_ZbrNssbh']) },
+          { key: 'costConsultantTaxpayerIdentificationNo', value: getValue(projectSrc, ['ZhaoBiaoXx', '_ZxrNssbh']) },
+          { key: 'tenderAuthorizerIDNo', value: getValue(projectSrc, ['ZhaoBiaoXx', '_ZbrDbSfzh']) },
+          { key: 'consultantAuthorizerTaxpayerIdentificationNo', value: getValue(projectSrc, ['ZhaoBiaoXx', '_ZxrDbSfzh']) },
+        ];
+        info.push(...extraInfo);
+      }
+      return info;
+    }
+
+    // 提取工程特征信息,xml中提取出来的工程特征,最终会与模板工程特征进行合并处理。(接口内不需要处理合并)
+    function setupFeature(projectSrc, tenderSrc) {
+      const feature = [
+        { key: 'singleProjNo', value: getValue(projectSrc, ['Dxgcxx', '_Dxgcbh']) },
+        { key: 'singleProjName', value: getValue(projectSrc, ['Dxgcxx', '_Dxgcmc']) },
+        { key: 'unitProjNo', value: getValue(tenderSrc, ['_Dwgcbh']) },
+      ];
+      // 工程所在地在费率项中
+      const feeRateItems = array(tenderSrc, ['Qfxx', 'JjFlx', 'JjFlxMx']);
+      const locationItem = feeRateItems.find(item => getValue(item, ['_Mc']) === '工程所在地');
+      if (locationItem) {
+        feature.push(locationItem);
+      }
+      return feature;
+    }
+
+    // 费率值映射表,“接口中可有效识别的费率值类型值,此接口是Bm”与“软件中对应费率标准的名称”映射。
+    const feeRateValueMap = {
+      'DJF': '冬季施工增加费',
+      'YJF': '雨季施工增加费',
+      'YEF': '夜间施工增加费',
+      'GYF': '高原地区施工增加费',
+      'FSF': '风沙地区施工增加费',
+      'YHF': '沿海地区施工增加费',
+      'XCF': '行车干扰施工增加费',
+      'ZYF': '工地转移费',
+      'SFF': '施工辅助费',
+      'JBF': '基本费用',
+      'YFF': '主副食运费补贴',
+      'TQF': '职工探亲路费',
+      'QNF': '职工取暖补贴',
+      'CWF': '财务费用',
+      'YLF': '养老保险费',
+      'SYF': '失业保险费',
+      'YBF': '医疗保险费',
+      'GSF': '工伤保险费',
+      'ZFF': '住房公积金',
+      'LR': '利润',
+      'SJ': '税金',
+    };
+
+    // 费率工程类型映射表, “接口中可有效识别的工程类型值,此接口是Qflb”与“袁建中对应费率标准的工程类型名称”映射
+    const feeRateParentTypeMap = {
+      1: '土方',
+      3: '石方',
+      2: '运输',
+      4: '路面',
+      11: '隧道',
+      5: '构造物Ⅰ',
+      16: '构造物Ⅰ(不计冬)',
+      6: '构造物Ⅱ',
+      9: '构造物Ⅲ(桥梁)',
+      8: '构造物Ⅲ(除桥以外不计雨夜)',
+      10: '技术复杂大桥',
+      12: '钢材及钢结构(桥梁)',
+      13: '钢材及钢结构(除桥以外不计夜)',
+      17: '费率为0',
+      4: '路面(不计雨)',
+      16: '构造物Ⅰ(不计雨)',
+      8: '构造物Ⅲ(除桥以外)',
+      13: '钢材及钢结构(除桥以外)',
+      15: '设备',
+      14: '量价',
+    };
+
+    const feeRateParamMap = {
+      djsg: '冬季施工',
+      yjsg: '雨季施工',
+      yjsg: '夜间施工',
+      gysg: '高原施工',
+      fssg: '风沙施工',
+      yhsg: '沿海施工',
+      hcgr: '行车干扰',
+      gdzy: '工地转移(km)',
+      sgfz: '施工辅助',
+      jbfy: '基本费用',
+      zhlc: '综合里程(km)',
+      zgtq: '职工探亲',
+      zgqn: '职工取暖',
+      cwfy: '财务费用',
+      ylbx: '养老保险(%)',
+      sybx: '失业保险(%)',
+      ylbx: '医疗保险(%)',
+      gsbx: '工伤保险(%)',
+      zfgj: '住房公积金(%)',
+    };
+
+    // 提取费率数据(费率值、费率参数),最终会与标准费率数据进行合并。(接口内不需要处理合并)
+    function setupFeeRate(feeRateSrc) {
+      // 提取费率值数据
+      const valuaItems = arrayValue(feeRateSrc, ['JjFlb', 'JjFlbMx'])
+        .map(item => ({
+          parentType: feeRateParentTypeMap[getValue(item, ['_Qflb'])] || '',
+          type: feeRateValueMap[getValue(item, ['_Bm'])] || '',
+          value: getValue(item, ['_Fl'])
+        }));
+      // 提取费率参数数据
+      const paramItems = arrayValue(feeRateSrc, ['JjFlx', '_JjFlxMx'])
+        .map(item => ({
+          
+        }));
+      return {
+
+      };
+    }
+
+    // 提取单位工程数据
+    function setupTender(projectSrc, tenderSrc) {
+      const feeRateSrc = getValue(tenderSrc, ['Qfxx']);
+      return {
+        name: getValue(tenderSrc, ['_Dwgcmc']),
+        feature: setupFeature(projectSrc, tenderSrc),
+        feeRate: setupFeeRate(feeRateSrc),
+      }
+    }
+
+    // 从xml对象提取需要的数据
+    function setupProject(projectSrc) {
+      const tenders = arrayValue(projectSrc, ['Dxgcxx', 'Dwgcxx'])
+        .map(tenderSrc => setupTender(projectSrc, tenderSrc))
+      return {
+        name: getValue(projectSrc, ['_Xmmc']),
+        info: setupInformation(projectSrc),
+        tenders,
+      };
+    }
+
+
+    const test = setupProject(getValue(xmlObj, ['JingJiBiao']));
+    console.log(test);
+    return test;
+  }
+
+  return {
+    entry
+  };
+
+})();

+ 274 - 0
web/building_saas/standard_interface/import/base.js

@@ -0,0 +1,274 @@
+/*
+ * @Descripttion: 导入通用代码
+ * @Author: vian
+ * @Date: 2020-09-09 10:45:54
+ */
+const INTERFACE_EXPORT_BASE = (() => {
+
+  // xml字符实体
+  const XMLEntity = {
+    '&#x0020;': 'escape{space}',
+    '&#x20;': 'escape{simpleSpace}',
+    '&#x0009;': 'escape{tab}',
+    '&#x9;': 'escape{simpleTab}',
+    '&#x000D;': 'escape{return}',
+    '&#xD;': 'escape{simpleReturn}',
+    '&#000A;': 'escape{newLine}',
+    '&#xA;': 'escape{simpleNewLine}',
+    '&lt;': 'escape{less}',
+    '&gt;': 'escape{greater}',
+    '&amp;': 'escape{and}',
+    '&quot;': 'escape{quot}',
+    '&apos;': 'escape{apos}'
+  };
+
+  // 避免字符实体进行转义。原文本中含有xml字符实体,转换为其他字符。
+  function escapeXMLEntity(str) {
+    for (const [key, value] of Object.entries(XMLEntity)) {
+      str = str.replace(new RegExp(key, 'g'), value);
+    }
+    return str;
+  }
+  // 将文本还原为字符实体
+  function restoreXMLEntity(str) {
+    for (const [key, value] of Object.entries(XMLEntity)) {
+      str = str.replace(new RegExp(value, 'g'), key);
+    }
+    return str;
+  }
+
+  /*
+   * 根据字段数组获得所要字段的值 eg: 要获取标段下的单项工程: ['标段', '单项工程'];
+   * 属性需要加前缀:“_”
+   * 节点的不需要加前缀
+   * @param {Object}source 源数据
+   *        {Array}fields 字段数组
+   * @return {String}
+   * @example getValue(source, ['标段', '_文件类型'])
+   * */
+  function getValue(source, fields) {
+    let cur = source;
+    for (const field of fields) {
+      if (!cur[field]) {
+        return '';
+      }
+      cur = cur[field];
+    }
+    return cur || '';
+  }
+
+  // 获取数据类型
+  function _plainType(v) {
+    return Object.prototype.toString.call(v).slice(8, -1);
+  }
+
+  /*
+   * 获取某字段的值,强制返回数组,防止一些错误。如果期待返回数组,可以用此方法。
+   * @param {Object}source 数据源
+   *        {Array}fields 取的字段
+   * @return {Array}
+   * @example arrayValue(source, ['标段', '单项工程'])
+   * */
+  function arrayValue(source, fields) {
+    let target = getValue(source, fields);
+    if (_plainType(target) === 'Object') {
+      target = [target];
+    } else if (_plainType(target) !== 'Array') {
+      target = []
+    }
+    return target;
+  }
+
+  // 获取费用
+  function getFee(fees, fields) {
+    if (!Array.isArray(fees) || !fees.length) {
+      return '0';
+    }
+    const feeData = fees.find(fee => fee.fieldName === fields[0]);
+    return feeData && feeData[fields[1]] || '0';
+  }
+
+  // 合并价格
+  function mergeFees(feesA, feesB) {
+    if (!feesA) {
+      return feesB;
+    }
+    if (!feesB) {
+      return [];
+    }
+    feesB.forEach(feeB => {
+      const sameKindFee = feesA.find(feeA => feeA.fieldName === feeB.fieldName);
+      if (sameKindFee) {
+        Object.assign(sameKindFee, feeB);
+      } else {
+        feesA.push(feeB);
+      }
+    });
+    return feesA;
+  }
+
+  // 将A对象的一部分属性赋值到B对象上
+  function assignAttr(target, source, attrs, isExcepted = false) {
+    if (!source || !target) {
+      return;
+    }
+    const sourceAttrs = attrs
+      ? isExcepted
+        ? Object.keys(source).filter(attr => !attrs.includes(attr))
+        : attrs
+      : Object.keys(source);
+    sourceAttrs.forEach(attr => {
+      // 如果是价格,不能简单地覆盖,要合并两个对象的价格
+      target[attr] = attr === 'fees' ? mergeFees(target[attr], source[attr]) : source[attr];
+    });
+  }
+
+  // 获取固定ID
+  function getFlag(data) {
+    return data.flags && data.flags[0] && data.flags[0].flag || 0;
+  }
+
+  // 获取布尔型的数据
+  function getBool(v) {
+    return v === 'true' ? true : false;
+  }
+
+  // 设置成树结构数据
+  function setTreeData(data, parent, next) {
+    const defalutID = -1;
+    data.ID = uuid.v1();
+    data.ParentID = parent && parent.ID || defalutID;
+    data.NextSiblingID = next && next.ID || defalutID;
+  }
+
+  // 递归设置树结构数据,并返回设置好的数据,递归items数组
+  function mergeDataRecur(parent, items) {
+    const rst = [];
+    for (let i = 0; i < items.length; i++) {
+      const cur = items[i];
+      const next = items[i + 1];
+      setTreeData(cur, parent, next);
+      rst.push(cur);
+      if (cur.items && cur.items.length) {
+        rst.push(...mergeDataRecur(cur, cur.items));
+      }
+    }
+    return rst;
+  }
+
+  /*
+   * 递归获取相关数据,eg:获取组织措施清单下的所有子清单,该子清单们可能由分类及公式措施项各种组合组成。(参考调用处)
+   * @param {Object}src(数据源) {Array}fields(二维数组,数组里的成员由需要取的字段组成)
+   *         eg: ['组织措施分类'], ['公式计算措施项'],该层数据可能由组织措施分类 或 公式计算措施项组成 (同层不可同时存在)
+   *        {Function} 获得源数据后,需要提取的数据方法
+   * @return {Array}
+   * */
+  function getItemsRecur(src, fields, extractFuc) {
+    let itemsSrc = [];
+    let curField = [''];
+    for (const field of fields) {
+      itemsSrc = arrayValue(src, field);
+      if (itemsSrc.length) {
+        curField = field;
+        break;
+      }
+    }
+    return itemsSrc.map(itemSrc => {
+      const obj = extractFuc(itemSrc, curField);
+      obj.items = getItemsRecur(itemSrc, fields, extractFuc);
+      return obj;
+    });
+  }
+
+  // 递归获取相关数据,与上方的方法不同的点在:同层可能出现不同节点,上方方法暂时不取消(防止bug)
+  // fields内字段的顺序即决定了提取数据类型的顺序,如fields = [['gruop'], ['item']],则提取的数据同层中group数据在item数据之前
+  function extractItemsRecur(src, fields, extractFuc) {
+    const rst = [];
+    for (const field of fields) {
+      const itemsSrc = arrayValue(src, field);
+      if (itemsSrc.length) {
+        const items = itemsSrc.map(itemSrc => {
+          const obj = extractFuc(itemSrc, field);
+          obj.items = extractItemsRecur(itemSrc, fields, extractFuc);
+          return obj;
+        });
+        rst.push(...items);
+      }
+    }
+    return rst;
+  }
+
+  const UTIL = Object.freeze({
+    escapeXMLEntity,
+    restoreXMLEntity,
+    getValue,
+    arrayValue,
+    getFee,
+    mergeFees,
+    assignAttr,
+    setTreeData,
+    mergeDataRecur,
+    getFlag,
+    getBool,
+    getItemsRecur,
+    extractItemsRecur,
+  });
+
+
+  /*
+  * 读取文件转换为utf-8编码的字符串
+  * @param {Blob}file
+  * @return {Promise}
+  * */
+  function readAsTextSync(file) {
+    return new Promise((resolve, reject) => {
+      const fr = new FileReader();
+      fr.readAsText(file);    // 默认utf-8,如果出现乱码,得看导入文件是什么编码
+      fr.onload = function () {
+        resolve(this.result);
+      };
+      fr.onerror = function () {
+        reject('读取文件失败,请重试。');
+      }
+    });
+  }
+
+  /**
+   * 
+   * @param {Function} entryFunc - 各导入接口提取导入数据方法
+   * @param {File} file - 导入的文件
+   * @param {String} areaKey - 地区标识,如:'安徽@马鞍山'
+   * @param {String} feeRateStandard - 导入接口地区对应的费率标准名称
+   * @param {Boolean} escape - 是否需要避免xml中的实体字符转换
+   * @return {Promise<Object>}
+   */
+  async function extractImportData(entryFunc, file, areaKey, feeRateStandard, escape = false) {
+    const valuationID = compilationData.ration_valuation[0].id;
+    const templateData = await ajaxPost('/pm/api/getImportTemplateData', { user_id: userID, valuationID, feeRateStandard });
+    if (!templateData) {
+      throw '无法获取有效模板数据。';
+    }
+    console.log(templateData);
+    // 将二进制文件转换成字符串
+    let xmlStr = await readAsTextSync(file);
+    if (escape) {
+      // x2js的str to json的实现方式基于DOMParser,DOMParser会自动将一些实体字符进行转换,比如 “&lt; to <”。如果不想进行自动转换,需要进行处理。
+      xmlStr = escapeXMLEntity(xmlStr);
+    }
+    // 将xml格式良好的字符串转换成对象
+    const x2js = new X2JS();
+    let xmlObj = x2js.xml_str2json(xmlStr);
+    xmlObj = JSON.parse(restoreXMLEntity(JSON.stringify(xmlObj)));
+    console.log(xmlObj);
+    if (!xmlObj) {
+      throw '无有效数据。';
+    }
+    return await entryFunc(areaKey, xmlObj, templateData);
+  }
+
+  return {
+    UTIL,
+    extractImportData,
+  }
+
+})();

+ 84 - 0
web/building_saas/standard_interface/import/view.js

@@ -0,0 +1,84 @@
+/*
+ * @Descripttion: 导入接口视图相关
+ * @Author: vian
+ * @Date: 2020-09-08 09:36:52
+ */
+const IMPORT_VIEW = (() => {
+
+  /**
+   * 根据接口配置文件,设置可被接受的导入文件类型
+   * @param {Object} $file - 文件选择Jquery dom
+   * @return {Void}
+   */
+  function initFileAccept($file) {
+    const set = new Set();
+    Object
+      .values(INTERFACE_CONFIG)
+      .forEach(config => {
+        Object
+          .values(config.fileSuffix)
+          .forEach(suffix => set.add(suffix));
+      });
+    const accept = [...set].join(',');
+    $file.prop('accept', accept);
+  }
+
+  // 导入相关事件监听器
+  function importListener() {
+    // 文件选择变更
+    $('#interface-import-file').change(function () {
+      const file = $(this)[0].files[0];
+      $('#interface-import-label').text(file && file.name || '请选择导入文件');
+    });
+    // 导入确认事件
+    $('#interface-import-confirm').click(async function () {
+      try {
+        const file = $('#interface-import-file')[0].files[0];
+        if (!file) {
+          throw '请选择导入文件。';
+        }
+        // 按照地区动态加载导入脚本
+        const parentArea = $('#import-parent-area').val();
+        const subArea = $('#import-sub-area').val();
+        if (!parentArea || !subArea) {
+          throw '请选择有效地区。';
+        }
+        const areaKey = `${parentArea}@${subArea}`;
+        $.bootstrapLoading.progressStart('导入文件', true);
+        $("#progress_modal_body").text('正在导入接口文件,请稍候……');
+        await STD_INTERFACE.loadScriptByArea(areaKey, STD_INTERFACE.ScriptType.IMPORT);
+        const feeRateStandard = INTERFACE_CONFIG[areaKey].feeRateStandard;
+        const importData = await INTERFACE_EXPORT_BASE.extractImportData(INTERFACE_IMPORT.entry, file, areaKey, feeRateStandard);
+        // 转换成File实例
+        /* const blob = new Blob([JSON.stringify(importData)], { type: 'text/plain;charset=utf-8' });
+        const key = `${uuid.v1()}.json`;
+        const file = new File([blob], key);
+        // 上传文件
+        await projTreeObj.getUploadToken();
+        await UPLOAD_CDN.uploadSync(file, key, projTreeObj.uptoken);
+        // 下载并处理文件
+        await ajaxPost('/pm/import/importInterface', { key });
+        await importProcessChecking(key, null, (projectData) => handleProjectAfterChecking(projectData)); */
+      } catch (err) {
+        alert(err);
+      } finally {
+        setTimeout(() => $.bootstrapLoading.progressEnd(), 500);
+      }
+    });
+  }
+
+  return {
+    importListener,
+    initFileAccept,
+  };
+})();
+
+$(document).ready(() => {
+  $('#interface-import-modal').on('show.bs.modal', () => {
+    $('#interface-import-file').val('');
+    $('#interface-import-label').text('请选择导入文件');
+    IMPORT_VIEW.initFileAccept($('#interface-import-file'));
+    STD_INTERFACE.initInterfaceAreas($('#import-parent-area'), $('#import-sub-area'));
+  });
+  IMPORT_VIEW.importListener();
+});

+ 9 - 5
web/building_saas/standard_interface/index.js

@@ -36,7 +36,7 @@ const STD_INTERFACE = (() => {
   'use strict';
 
   // 根据地区配置,初始化地区选项
-  function initExportAreas($parentAreas, $subAreas) {
+  function initInterfaceAreas($parentAreas, $subAreas) {
     const connectedAreas = Object.keys(INTERFACE_CONFIG);
     const parentMap = {};
     connectedAreas.forEach(connectedArea => {
@@ -51,13 +51,17 @@ const STD_INTERFACE = (() => {
     $subAreas.html(subAreasHtml);
     // 父级地区变更,子地区选项更新
     $parentAreas.change(function () {
-      EXPORT_VIEW.resetState(); // 清空接口缓存
+      if (typeof EXPORT_VIEW !== 'undefined') {
+        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();
+      if (typeof EXPORT_VIEW !== 'undefined') {
+        EXPORT_VIEW.resetState(); // 清空导出接口缓存
+      }
     })
   }
 
@@ -112,7 +116,6 @@ const STD_INTERFACE = (() => {
     if (area === curArea) {
       return;
     }
-    curArea = area;
     const configItem = INTERFACE_CONFIG[area];
     if (!configItem) {
       throw new Error(`[${area}]不存在有效配置。`);
@@ -123,10 +126,11 @@ const STD_INTERFACE = (() => {
     }
     const fullPath = `/web/building_saas/standard_interface/${scriptType}/${scriptName}`;
     await loadScript(fullPath);
+    curArea = area;
   }
 
   return {
-    initExportAreas,
+    initInterfaceAreas,
     ScriptType,
     loadScriptByArea,
   };

+ 3 - 1
web/over_write/js/quanguo_2018.js

@@ -192,7 +192,9 @@ if (typeof baseFigureMap !== 'undefined') {
     '施工场地建设费': {
       isProgressive: true,
       base: 'SGCDJSF',
-      fixedFlag: fixedFlag.CONSTRUCTION_INSTALL_FEE,
+      // 虽然用到了定额建筑安装工程费一部分费用baseFee,但是是扣除了专项费用的。施工场地建设费清单为专项费用清单子项。
+      // 当施工场地建设费清单使用此基数时,该清单的费用不会汇总到baseFee里,因此不会造成循环计算。如果fixedFlag设置成fixedFlag.CONSTRUCTION_INSTALL_FEE,则会造成循环计算
+      fixedFlag: null,
       filter: [fixedFlag.CONSTRUCTION_PLANT_COST],
       pick: true,
     },