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

+ 3 - 0
config/gulpConfig.js

@@ -52,6 +52,8 @@ module.exports = {
         'web/building_saas/glj/js/socket.io.slim.js',
         'public/web/socket/connection.js',
         'public/billsUtil.js',
+        'public/common_constants.js',
+        'web/building_saas/main/js/models/importStdInterfaceBase.js',
         'web/building_saas/main/js/models/importStandardInterface.js',
         'web/building_saas/pm/js/**/*.js',
         'lib/ztree/*.js',
@@ -73,6 +75,7 @@ module.exports = {
         // 'test/tmp_data/test_ration_calc/ration_calc_base.js',
         'lib/pinyinjs/pinyin_dict_firstletter.js',
         'lib/pinyinjs/pinyinUtil.js',
+        'public/common_constants.js',
         'web/building_saas/main/js/models/main_consts.js',
         'web/over_write/config/compilation_config.js',
         'public/web/common_util.js',

+ 112 - 0
public/common_constants.js

@@ -0,0 +1,112 @@
+// 部分数据从main_consts.js中抽出来,因为除了造价书界面,有一些页面也需要用到其中的变量
+// 但是其他页面直接引入整个main_consts.js不合理,且会报错(main_consts.js正常运行依赖main.html的一些内容)
+const commonConstants = (() => {
+    // 清单固定行
+    const fixedFlag = {
+        // 分部分项工程
+        SUB_ENGINERRING: 1,
+        // 措施项目
+        MEASURE: 2,
+        // 施工技术措施项目
+        CONSTRUCTION_TECH: 3,
+        // 安全文明施工按实计算费用
+        SAFETY_CONSTRUCTION_ACTUAL: 4,
+        // 施工组织措施专项费用
+        CONSTRUCTION_ORGANIZATION: 5,
+        // 安全文明施工专项费用
+        SAFETY_CONSTRUCTION: 6,
+        // 其他项目
+        OTHER: 7,
+        // 暂列金额
+        PROVISIONAL: 8,
+        // 暂估价
+        ESTIMATE: 9,
+        // 材料(工程设备)暂估价
+        MATERIAL_PROVISIONAL: 10,
+        // 专业工程暂估价
+        ENGINEERING_ESITIMATE: 11,
+        // 计日工
+        DAYWORK: 12,
+        // 总承包服务费
+        TURN_KEY_CONTRACT: 13,
+        // 索赔与现场签证
+        CLAIM_VISA: 14,
+        // 规费
+        CHARGE: 15,
+        // 社会保险费及住房公积金 Social insurance fee and housing accumulation fund
+        SOCIAL_INSURANCE_HOUSING_FUND: 16,
+        // 工程排污费 charges for disposing pollutants
+        POLLUTANTS: 17,
+        // 税金
+        TAX: 18,
+        //工程造价
+        ENGINEERINGCOST: 19,
+        //增值税
+        ADDED_VALUE_TAX: 20,
+        //专项技术措施暂估价
+        SPECIAL_TECH_PROVISIONAL: 21,
+        //专业发包工程管理费
+        LET_CONTRACT_MANAGEMENT: 22,
+        //人工
+        LABOUR: 23,
+        //材料
+        MATERIAL: 24,
+        //施工机械
+        MACHINE: 25,
+        //索赔
+        CLAIM: 26,
+        //现场签证
+        VISA: 27,
+        //附加税
+        ADDITIONAL_TAX: 28,
+        //环境保护税
+        ENVIRONMENTAL_PROTECTION_TAX: 29,
+        //建设工程竣工档案编制费
+        PROJECT_COMPLETE_ARCH_FEE: 30,
+        //住宅工程质量分户验收费
+        HOUSE_QUALITY_ACCEPT_FEE: 31,
+        //组织措施费
+        ORGANIZATION: 32,
+        //其他措施费
+        OTHER_MEASURE_FEE: 33,
+        // 绿色施工安全防护措施费
+        GREEN_MEASURE_FEE: 34,
+        // 预算包干费
+        BUDGET_INCLUDE_WORK_FEE: 35,
+        // 工程优质费
+        PROJECT_HIGH_QUALITY_FEE: 36,
+        // 概算幅度差
+        BUDGET_ESTIMATE_DIFF: 37,
+        // 其他费用(与其他项目不同,参考广东的用法)
+        OTHER_FEE: 38
+    };
+    // 清单类型
+    const billType = {
+        DXFY: 1,//大项费用
+        FB: 2,//分部
+        FX: 3,//分项
+        BILL: 4,//清单
+        BX: 5//补项
+    };
+    // 定额类型
+    const rationType = {
+        ration: 1,
+        volumePrice: 2,
+        gljRation: 3,
+        install: 4,
+        overHeight: 5, // 超高子目
+        itemIncrease: 6//子目增加
+    };
+    //项目类型
+    const projectType = {
+        Project: 'Project',
+        Engineering: 'Engineering',
+        Tender: 'Tender'
+    };
+    return {
+        fixedFlag,
+        billType,
+        rationType,
+        projectType,
+    }
+})();

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

@@ -2390,6 +2390,7 @@
     <!--<script type="text/javascript" src="/test/tmp_data/test_ration_calc/ration_calc_base.js"></script>-->
     <script src="/lib/pinyinjs/pinyin_dict_firstletter.js"></script>
     <script src="/lib/pinyinjs/pinyinUtil.js"></script>
+    <script src="/public/common_constants.js"></script>
     <script type="text/javascript" src="/web/building_saas/main/js/models/main_consts.js"></script>
     <script type="text/javascript" src="/web/over_write/config/compilation_config.js"></script>
     <script type="text/javascript" src="/public/web/common_util.js"></script>

Разница между файлами не показана из-за своего большого размера
+ 1457 - 1629
web/building_saas/main/js/models/importStandardInterface.js


+ 65 - 32
web/building_saas/main/js/models/importStdInterfaceBase.js

@@ -8,33 +8,48 @@
  * @version
  */
 
-const XML_IMPORT_BASE = (() => {
-    // 清单类型
-    const BILLS_TYPE ={
-        DXFY: 1, //大项费用
-        FB: 2,   //分部
-        FX: 3,   //分项
-        BILLS: 4, //清单
-        BX: 5    //补项
+const importXMLBase = (() => {
+    //人材机调整法
+    const AdjustType = {
+        info: 'priceInfo',  //造价信息差额调整法
+        coe: 'priceCoe'     //价格指数调整法
     };
-    // 项目类型
-    const PROJECT_TYPE = {
-        PROJECT: 'Project',
-        ENGINEERING: 'Engineering',
-        TENDER: 'Tender'
-    };
-    // 人材机调整法
-    const ADJUST_TYPE = {
-        INFO: 'priceInfo',  //造价信息差额调整法
-        COE: 'priceCoe'     //价格指数调整法
-    };
-
     const CONFIG = Object.freeze({
-        BILLS_TYPE,
-        PROJECT_TYPE,
-        ADJUST_TYPE
+        AdjustType
     });
 
+    // 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;
+    }
+
     /*
     * 读取文件转换为utf-8编码的字符串
     * @param {Blob}file
@@ -42,7 +57,7 @@ const XML_IMPORT_BASE = (() => {
     * */
     function readAsTextSync(file) {
         return new Promise((resolve, reject) => {
-            let fr = new FileReader();
+            const fr = new FileReader();
             fr.readAsText(file);    // 默认utf-8,如果出现乱码,得看导入文件是什么编码
             fr.onload = function () {
                 resolve(this.result);
@@ -62,7 +77,7 @@ const XML_IMPORT_BASE = (() => {
      * */
     function getValue(source, fields) {
         let cur = source;
-        for (let field of fields) {
+        for (const field of fields) {
             if (!cur[field]) {
                 return '';
             }
@@ -95,7 +110,7 @@ const XML_IMPORT_BASE = (() => {
         if (!Array.isArray(fees) || !fees.length) {
             return '0';
         }
-        let feeData = fees.find(fee => fee.fieldName === fields[0]);
+        const feeData = fees.find(fee => fee.fieldName === fields[0]);
         return feeData[fields[1]] || '0';
     }
     // 获取固定ID
@@ -110,9 +125,9 @@ const XML_IMPORT_BASE = (() => {
      * @return {Array}
      * */
     function getItemsRecur(src, fields, extractFuc) {
-        let itemsSrc = [],
-            curField = [''];
-        for (let field of fields) {
+        let itemsSrc = [];
+        let curField = [''];
+        for (const field of fields) {
             itemsSrc = arrayValue(src, field);
             if (itemsSrc.length) {
                 curField = field;
@@ -120,7 +135,7 @@ const XML_IMPORT_BASE = (() => {
             }
         }
         return itemsSrc.map(itemSrc => {
-            let obj = extractFuc(itemSrc, curField);
+            const obj = extractFuc(itemSrc, curField);
             obj.items = getItemsRecur(itemSrc, fields, extractFuc);
             return obj;
         });
@@ -194,14 +209,32 @@ const XML_IMPORT_BASE = (() => {
     // 获取必要的清单模板
     async function getNeedfulTemplate(templateLibID) {
         return await ajaxPost('/template/bills/api/getNeedfulTemplate',
-            {templateLibID});
+            { templateLibID });
     }
 
 
     const UTIL = Object.freeze({
+        escapeXMLEntity,
+        restoreXMLEntity,
         getValue,
         arrayValue,
         getFee,
-        transformCalcBase
+        getFlag,
+        getItemsRecur,
+        readAsTextSync,
     });
+
+    // 获取接受上传的文件类型正则表达式
+    function getAcceptReg(accepts) {
+        const acceptsExp = accepts.reduce((acc, cur, index) => {
+            return acc + `${index ? '|' : ''}\\${cur}`;
+        }, '');
+        return new RegExp(`(${acceptsExp})`, 'i');
+    }
+
+    return {
+        CONFIG,
+        UTIL,
+        getAcceptReg,
+    }
 })();

+ 3 - 93
web/building_saas/main/js/models/main_consts.js

@@ -133,14 +133,7 @@ const volumePriceMaps = {
     4: "量主",
     5: "量设"
 };
-const rationType = {
-    ration: 1,
-    volumePrice: 2,
-    gljRation: 3,
-    install:4,
-    overHeight: 5, // 超高子目
-    itemIncrease:6//子目增加
-};
+const rationType = commonConstants.rationType;
 const rationPrefix = { //定额前缀,补/借
     none: '',
     complementary: '补',
@@ -211,84 +204,7 @@ const materialTypeMap = {
 }
 
 //清单固定行
-const fixedFlag = {
-    // 分部分项工程
-    SUB_ENGINERRING: 1,
-    // 措施项目
-    MEASURE: 2,
-    // 施工技术措施项目
-    CONSTRUCTION_TECH: 3,
-    // 安全文明施工按实计算费用
-    SAFETY_CONSTRUCTION_ACTUAL: 4,
-    // 施工组织措施专项费用
-    CONSTRUCTION_ORGANIZATION: 5,
-    // 安全文明施工专项费用
-    SAFETY_CONSTRUCTION: 6,
-    // 其他项目
-    OTHER: 7,
-    // 暂列金额
-    PROVISIONAL: 8,
-    // 暂估价
-    ESTIMATE: 9,
-    // 材料(工程设备)暂估价
-    MATERIAL_PROVISIONAL: 10,
-    // 专业工程暂估价
-    ENGINEERING_ESITIMATE: 11,
-    // 计日工
-    DAYWORK: 12,
-    // 总承包服务费
-    TURN_KEY_CONTRACT: 13,
-    // 索赔与现场签证
-    CLAIM_VISA: 14,
-    // 规费
-    CHARGE: 15,
-    // 社会保险费及住房公积金 Social insurance fee and housing accumulation fund
-    SOCIAL_INSURANCE_HOUSING_FUND: 16,
-    // 工程排污费 charges for disposing pollutants
-    POLLUTANTS: 17,
-    // 税金
-    TAX: 18,
-    //工程造价
-    ENGINEERINGCOST: 19,
-    //增值税
-    ADDED_VALUE_TAX: 20,
-    //专项技术措施暂估价
-    SPECIAL_TECH_PROVISIONAL: 21,
-    //专业发包工程管理费
-    LET_CONTRACT_MANAGEMENT: 22,
-    //人工
-    LABOUR: 23,
-    //材料
-    MATERIAL: 24,
-    //施工机械
-    MACHINE: 25,
-    //索赔
-    CLAIM: 26,
-    //现场签证
-    VISA: 27,
-    //附加税
-    ADDITIONAL_TAX: 28,
-    //环境保护税
-    ENVIRONMENTAL_PROTECTION_TAX: 29,
-    //建设工程竣工档案编制费
-    PROJECT_COMPLETE_ARCH_FEE:30,
-    //住宅工程质量分户验收费
-    HOUSE_QUALITY_ACCEPT_FEE:31,
-    //组织措施费
-    ORGANIZATION:32,
-    //其他措施费
-    OTHER_MEASURE_FEE:33,
-    // 绿色施工安全防护措施费
-    GREEN_MEASURE_FEE: 34,
-    // 预算包干费
-    BUDGET_INCLUDE_WORK_FEE: 35,
-    // 工程优质费
-    PROJECT_HIGH_QUALITY_FEE: 36,
-    // 概算幅度差
-    BUDGET_ESTIMATE_DIFF: 37,
-    // 其他费用(与其他项目不同,参考广东的用法)
-    OTHER_FEE: 38
-};
+const fixedFlag = commonConstants.fixedFlag;
 // 只读的固定类别(工程量、单价、综合合价只读,相当于是标题)
 const titleFlags = [
     fixedFlag.PROVISIONAL,
@@ -306,13 +222,7 @@ const titleFlags = [
 const gljKeyArray =['code','name','specs','unit','type'];
 const rationKeyArray =['code','name','specs','unit','subType'];
 const gljLibKeyArray =['code', 'name', 'specs', 'unit', 'gljType'];
-const billType ={
-    DXFY:1,//大项费用
-    FB:2,//分部
-    FX:3,//分项
-    BILL:4,//清单
-    BX:5//补项
-};
+const billType = commonConstants.billType;
 const billText = {
     1:'费用',
     2:'分部',

+ 3 - 1
web/building_saas/pm/html/project-management.html

@@ -824,7 +824,7 @@
                         <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="customFile" accept=".xml,.qtf,.QTF" lang="zh">
+                                <input type="file" class="custom-file-input" id="customFile" accept=".xml,.qtf" lang="zh">
                                 <label class="custom-file-label" for="customFile" style="white-space: nowrap; overflow: hidden;">请选择导入文件</label>
                             </div>
                             <div class="alert alert-success mt-3" id="uploadAlert" role="alert" style="display: none;"></div>
@@ -1016,6 +1016,8 @@
 <script src="/web/building_saas/glj/js/socket.io.slim.js"></script>
 <script src="/public/web/socket/connection.js"></script>
 <script src="/public/billsUtil.js"></script>
+<script src="/public/common_constants.js"></script>
+<script src="/web/building_saas/main/js/models/importStdInterfaceBase.js"></script>
 <script src="/web/building_saas/main/js/models/importStandardInterface.js"></script>
 <script src="/web/building_saas/pm/js/pm_ajax.js"></script>
 <script src="/web/building_saas/pm/js/pm_newMain.js"></script>

+ 9 - 22
web/building_saas/pm/js/pm_import.js

@@ -9,7 +9,6 @@
  */
 //操作接口为eventListen
 const importView = (() => {
-    let importXML = null;
     let xmlObj = null;  //导入xml转化后的对象
     let tbcObj = null;  //待确认对象
     //显示、隐藏提示上传文件窗口相关提示信息
@@ -286,18 +285,19 @@ const importView = (() => {
             $('.selFile').hide();
             hideTBCInfo();
             if (file) {
-                let reg = /(xml|XML|qtf|QTF)$/;
+                const reg = importXMLBase.getAcceptReg(importXML.accept);
                 if (file.name && !reg.test(file.name)) {
                     $('.selFile').hide();
-                    showUploadAlert(false, '请选择xml或qtf文件。');
+                    const acceptAlert = importXML.accept.join('、');
+                    showUploadAlert(false, `请选择${acceptAlert}文件。`);
                     return;
                 }
                 $.bootstrapLoading.start();
                 $('#loadingPage').css('z-index', '2000');
-                //转换数据
-                importXML = new ImportXML();
                 try {
+                    //转换数据
                     xmlObj = await importXML.extractData(file, false);
+                    console.log(xmlObj);
                     debugger;
                     $('.selFile input:eq(0)').val(xmlObj && xmlObj.name ? xmlObj.name : '');
                     $('.selFile input[name="fileKind-import"]:eq(0)').prop('checked', true);    //文件类型恢复成投标
@@ -311,22 +311,6 @@ const importView = (() => {
                 $.bootstrapLoading.end();
             }
         });
-        // --- test --- 解压cos文件
-        /* $('#customFile').change(async function () {
-            let file = $(this)[0].files[0];
-            //test 解压
-            const zipObj = new JSZip();
-            zipObj.loadAsync(file)
-                .then(function (zip) {
-                    debugger;
-                    console.log(zip);
-                    return zipObj.file("Project.xml").async("string");
-                })
-                .then(text => {
-                    console.log(text);
-                });
-
-        }); */
         //下一步
         $('#import-next').click(function () {
             debugger;
@@ -465,6 +449,8 @@ const importView = (() => {
                 $('#importInterface').modal('hide');
                 pr.start('导入文件', '正在生成文件,请稍候……');
                 let importData = await importXML.transformData(xmlObj);
+                debugger;
+                console.log(importData);
                 let blob = new Blob([JSON.stringify(importData)], { type: 'text/plain;charset=utf-8' });
                 // 转换成File实例
                 const key = `${uuid.v1()}.json`;
@@ -492,12 +478,13 @@ const importView = (() => {
         });
         // 导入窗口激活
         $('#importInterface').on('show.bs.modal', function () {
+            // 设置上传文件输入框accept属性
+            $('#customFile').attr('accept', importXML.accept.join(','));
             // 恢复计价规则下拉
             setValuationSels($('#import-valuation'), billValuation);
         });
         //导入窗口消失后
         $('#importInterface').on('hidden.bs.modal', function () {
-            importXML = null;
             xmlObj = null;  //重置数据
             tbcObj = null;
             $('#importInterface .modal-content:eq(0)').show();  //显示第一步内容

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

@@ -386,7 +386,7 @@ const projTreeObj = {
             name: '导入招投标接口文件',
             icon: 'fa-cloud-upload',
             visible: function () {
-               return compilationData && compilationData.name === '重庆定额(2018)';
+                return compilationData && compilationData.name === '重庆定额(2018)';
             },
             callback: function () {
                 $('#importInterface').modal('show');

+ 3 - 3
web/over_write/js/guangdong_2018_export.js

@@ -540,7 +540,7 @@ const XMLStandard = (function () {
                 // 总说明 编制说明
                 {
                     name: 'Explains',
-                    value: projectData.property.compilationIllustration
+                    value: projectData.property.compilationIllustrationProject
                 }
             ];
             //唯一约束
@@ -573,7 +573,7 @@ const XMLStandard = (function () {
                 // 工程量、数量类小数精度
                 { name: 'QuantityPrecision', type: _type.INT, required: true, value: Decimal.QUANTITY },
                 // 金额、合价、费用类小数精度
-                { name: 'CostPrecision', type: _type.INT, required: true, value: Decimal.Fee },
+                { name: 'CostPrecision', type: _type.INT, required: true, value: Decimal.FEE },
                 // 费率、指数、比例(%)类小数精度
                 { name: 'RatePrecision', type: _type.INT, required: true, value: Decimal.RATE }
             ];
@@ -1159,7 +1159,7 @@ const XMLStandard = (function () {
                 // 总说明
                 {
                     name: 'Explains',
-                    value: tenderData.property.compilationIllustrationProject
+                    value: tenderData.property.compilationIllustration
                 },
                 // 导出XML文件名,工程编号+工程名称.xml, 暂时取“名称.xml”,用户设置完工程编号后修改此值
                 {

+ 93 - 0
web/over_write/js/guangdong_2018_import.js

@@ -0,0 +1,93 @@
+'use strict';
+
+const importXML = (() => {
+    // 通用设置和工具
+    const config = importXMLBase.CONFIG;
+    const util = importXMLBase.UTIL;
+    const {
+        fixedFlag,
+        billType,
+        rationType,
+        projectType
+    } = commonConstants;
+    const { AdjustType } = config;
+    const {
+        getValue,
+        arrayValue,
+        getFee,
+        getFlag,
+        getItemsRecur,
+    } = util;
+
+    //导入的文件类型,界面选的文件类型是生成项目的文件类型,这里的文件类型指的是,要导入文件的类型,
+    //导入文件类型不同,导入数据不同
+    let importFileKind = '';
+
+    const countData = {
+        projectCount: 0,    //项目数量
+        projectGLJCount: 0,  //项目人材机数量
+        ratioCount: 0,      //组成物数量
+        unitPriceCount: 0,   //单价数量
+        unitPriceFileCount: 0,  //单价文件数量
+    };
+
+    // 建设项目
+    function loadProject(xmlObj) {
+
+    }
+
+    /**
+     * 解压cos、zip文件
+     * @param {File} file - 上传的文件 
+     * @return {Object} 解压出来的xml文件名称与xml文件文本内容映射 
+     */
+    async function unzipFile(file) {
+        const jsZip = new JSZip();
+        const zip = await jsZip.loadAsync(file);
+        const map = {};
+        for (const fileName in zip.files) {
+            // 将二进制数据转换成字符串
+            map[fileName] = await jsZip.file(fileName).async('string');
+        }
+        return map;
+    }
+
+    //从xml文件中提取数据
+    async function extractData(file, escape = false) {
+        const fileMap = await unzipFile(file);
+        const projectXML = fileMap['Project.xml'];
+        if (!projectXML) {
+            throw '无有效数据';
+        }
+        const xmlObjMap = {};
+        for (const fileName in fileMap) {
+            const xmlStr = escape ? util.escapeXMLEntity(fileMap[fileName]) : fileMap[fileName];
+            //将xml格式良好的字符串转换成对象
+            const x2js = new X2JS();
+            let xmlObj = x2js.xml_str2json(xmlStr);
+            xmlObj = JSON.parse(util.restoreXMLEntity(JSON.stringify(xmlObj)));
+            if (!xmlObj) {
+                throw '无有效数据。';
+            }
+            xmlObjMap[fileName] = xmlObj;
+        }
+        if (escape) {
+            xmlStr = util.escapeXMLEntity(xmlStr);
+        }
+        let xmlObj = x2js.xml_str2json(xmlStr);
+        xmlObj = JSON.parse(util.restoreXMLEntity(JSON.stringify(xmlObj)));
+        if (!xmlObj) {
+            throw '无有效数据。';
+        }
+        //console.log(xmlObj);
+        //提取数据
+        return loadProject(xmlObj);
+    };
+
+    // 接受上传的文件类型(不同的省份可以上传的文件不同)
+    const accept = ['.zip', '.cos'];
+
+    return {
+        accept,
+    }
+})();