Просмотр исходного кода

feat:公路云导入接口(未完成)

vian 5 лет назад
Родитель
Сommit
ee87188b73

+ 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'
     ],

Разница между файлами не показана из-за своего большого размера
+ 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 || []
+    };
 }

+ 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);

+ 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 - 1
web/building_saas/pm/html/project-management.html

@@ -722,7 +722,7 @@
                     <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" style="white-space: nowrap; overflow: hidden;">请选择导入文件</label>
+                            <label class="custom-file-label" for="customFile" id="interface-import-label" style="white-space: nowrap; overflow: hidden;">请选择导入文件</label>
                         </div>
                     </div>
                 </div>
@@ -750,6 +750,7 @@
 
 <!-- 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>
@@ -758,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>
@@ -776,6 +778,7 @@
 <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>

+ 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',

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

@@ -0,0 +1,97 @@
+/*
+ * @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) {
+      return [
+        { key: 'singleProjNo', value: getValue(projectSrc, ['Dxgcxx', '_Dxgcbh']) },
+        { key: 'singleProjName', value: getValue(projectSrc, ['Dxgcxx', '_Dxgcmc']) },
+        { key: 'unitProjNo', value: getValue(tenderSrc, ['_Dwgcbh']) },
+      ];
+    }
+
+    // 提取单位工程数据
+    function setupTender(projectSrc, tenderSrc) {
+      return {
+        name: getValue(tenderSrc, ['_Dwgcmc']),
+        feature: setupFeature(projectSrc, tenderSrc),
+      }
+    }
+
+    // 从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
+  };
+
+})();

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

@@ -0,0 +1,272 @@
+/*
+ * @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,
+  }
+
+})();

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

@@ -25,6 +25,11 @@ const IMPORT_VIEW = (() => {
 
   // 导入相关事件监听器
   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 {
@@ -39,9 +44,25 @@ const IMPORT_VIEW = (() => {
           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);
       }
     });
   }
@@ -54,6 +75,8 @@ const IMPORT_VIEW = (() => {
 
 $(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'));
   });