Преглед на файлове

广东接口
1.广东接口导入
2.分部分项导出时导出其汇总价

vian преди 5 години
родител
ревизия
837be8899a

+ 20 - 0
web/building_saas/main/js/models/importStdInterfaceBase.js

@@ -1,5 +1,7 @@
 'use strict';
 
+const e = require("express");
+
 /**
  *
  *
@@ -113,10 +115,26 @@ const importXMLBase = (() => {
         const feeData = fees.find(fee => fee.fieldName === fields[0]);
         return feeData[fields[1]] || '0';
     }
+    // 合并价格
+    function mergeFees(feesA, feesB) {
+        feesB.forEach(feeB => {
+            const sameKindFee = feesA.find(feeA => feeA.fieldName === feeB.fieldName);
+            if (sameKindFee) {
+                Object.assign(sameKindFee, feeB);
+            } else {
+                feesA.push(feeB);
+            }
+        });
+        return feesA;
+    }
     // 获取固定ID
     function getFlag(data) {
         return data.flags && data.flags[0] && data.flags[0].flag || 0;
     }
+    // 获取布尔型的数据
+    function getBool(v) {
+        return v === 'true' ? true : false;
+    }
     /*
      * 递归获取相关数据,eg:获取组织措施清单下的所有子清单,该子清单们可能由分类及公式措施项各种组合组成。(参考调用处)
      * @param {Object}src(数据源) {Array}fields(二维数组,数组里的成员由需要取的字段组成)
@@ -219,7 +237,9 @@ const importXMLBase = (() => {
         getValue,
         arrayValue,
         getFee,
+        mergeFees,
         getFlag,
+        getBool,
         getItemsRecur,
         readAsTextSync,
     });

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

@@ -184,7 +184,7 @@ let contentOprObj = {
         if (!billsNode || !Array.isArray(billsNode.data.jobContent)) {
             return '';
         }
-        return billsNode.data.jobContent[0].content;
+        return billsNode.data.jobContent[0] && billsNode.data.jobContent[0].content || '';
     },
     // 从清单节点获取工作内容文本数据
     getContentTexts: function (billsNode) {

+ 2 - 1
web/over_write/js/guangdong_2018_export.js

@@ -2358,8 +2358,9 @@ const XMLStandard = (function () {
                 // 有子清单的是分部
                 if (node.source.children.length) {
                     ele = new DivisionalWorks(node.data);
+                    const summaryCost = new SummaryOfBasicCost(tenderDetail.mainTree.items, node.data);
                     // 递归获取子元素
-                    ele.children = loadFBFX(node.children);
+                    ele.children = [summaryCost, ...loadFBFX(node.children)];
                 } else { // 无子清单的是分项
                     ele = loadBills(node, kind);
                 }

+ 265 - 8
web/over_write/js/guangdong_2018_import.js

@@ -15,7 +15,9 @@ const importXML = (() => {
         getValue,
         arrayValue,
         getFee,
+        mergeFees,
         getFlag,
+        getBool,
         getItemsRecur,
     } = util;
 
@@ -49,12 +51,13 @@ const importXML = (() => {
         const rst = {
             projectType: projectType.Project,
             name: getValue(projectSrc, ['_Name']),
-            engs: extractEngs(projectSrc),
+            engs: extractEngs(projectSrc, xmlObjMap),
             property: {
                 compilationIllustrationProject: getValue(projectSrc, ['_Explains'])
             },
             basicInformation: extractBasicInfo(projectSrc)
-        }
+        };
+        return rst;
     }
     // 从xml对象中提取基本信息相关
     function extractBasicInfo(projectSrc) {
@@ -122,10 +125,10 @@ const importXML = (() => {
             { key: 'bidApproverCertNo', value: getValue(bidderInfo, ['_BidApproverCertNo']) }, // 投标单位审定人资格证书
             { key: 'bidApproveDate', value: getValue(bidderInfo, ['_BidApproveDate']) }, // 投标单位审定时间
         ];
-        
+
     }
     // 从xml对象中提取单项工程数据
-    function extractEngs(projectSrc) {
+    function extractEngs(projectSrc, xmlObjMap) {
         const sectionWorks = arrayValue(projectSrc, ['ProjectInstallationWorkCost', 'SectionalWorks']);
         return sectionWorks.map(src => {
             countData.projectCount++;
@@ -133,23 +136,277 @@ const importXML = (() => {
                 projType: projectType.Engineering,
                 name: getValue(src, ['_Name']),
                 code: getValue(src, ['_Number']),
-                tenders: extractTenders(src)
+                tenders: extractTenders(src, xmlObjMap)
             }
         });
     }
     // 从xml对象中提取单位工程数据
-    function extractTenders(sectionWorkSrc) {
+    function extractTenders(sectionWorkSrc, xmlObjMap) {
         const unitWorks = arrayValue(sectionWorkSrc, ['UnitWorks']);
-        return unitWorks.map(src => {
+        return unitWorks.map(unitWorckSrc => {
             countData.projectCount++;
             countData.unitPriceFileCount++;
+            const fileName = getValue(unitWorckSrc, ['_FileName']);
+            const tenderXMLObj = xmlObjMap[fileName];
+            const src = getValue(tenderXMLObj, ['UnitWorks']); // 单位工程文件的数据
             return {
                 projType: projectType.Tender,
                 name: getValue(src, ['_Name']),
                 code: getValue(src, ['_Number']),
+                engineering: getValue(src, ['_ProjectType']),
+                projectFeature: extractProjectFeature(src),
+                workSummary: extractWorkSummary(src),
+                fbfx: extractFBFX
+            };
+        });
+    }
+    // 提取工程特征数据
+    function extractProjectFeature(tenderSrc) {
+        // TODO 支持嵌套子项
+        const attrInfoItems = arrayValue(tenderSrc, ['AttrInfo', 'AttrInfoItem']);
+        return attrInfoItems.map(item => {
+            return { name: getValue(item, ['_Name']), value: getValue(item, ['_Value']) };
+        });
+    }
+    // 提取大项费用
+    function extractWorkSummary(tenderSrc) {
+        const summaryList = arrayValue(tenderSrc, ['UnitWorksSummary']);
+        return summaryList.map(src => {
+            const item = {
+                code: getValue(src, ['_Number']),
+                name: getValue(src, ['_Name']),
+                quantity: getValue(src, ['_Quantity']),
+                calcBase: getValue(src, ['_QtyFormula']),
+                remark: getValue(src, ['_Remark']),
+                feeCode: getValue(src, ['_Code']) //费用字典
+            };
+            if (importFileKind === FileKind.tender) { // 投标
+                item.feeRate = getValue(src, ['_Rate']);
+                item.fees = [{
+                    fieldName: 'common', totalFee: getValue(itemSrc, ['_Total']) || '0',
+                    unitFee: getValue(src, ['_TechnicalAndEconomicIndex']) || '0'
+                }];
+            }
+        });
+    }
+    // 从src子项的SummaryOfBasicCost中获取一部分费用
+    function getFeesFromBasicCost(src) {
+        const summaryCost = getValue(src, ['SummaryOfBasicCost']);
+        if (!summaryCost) {
+            return [];
+        }
+        // SummaryOfBasicCost中有用的价格只有:人工费、材料费、主材费、机械费、管理费、利润
+        return [
+            { fieldName: 'labour', totalFee: getValue(summaryCost, ['_Labor']) || '0' },
+            { fieldName: 'material', totalFee: getValue(summaryCost, ['_Material']) || '0' },
+            { fieldName: 'mainMaterial', totalFee: getValue(summaryCost, ['_MainMaterial']) || '0' },
+            { fieldName: 'machine', totalFee: getValue(summaryCost, ['_Machine']) || '0' },
+            { fieldName: 'manage', totalFee: getValue(summaryCost, ['_Overhead']) || '0' },
+            { fieldName: 'profit', totalFee: getValue(summaryCost, ['_Profit']) || '0' },
+        ];
+    }
+    // 提取分部分项
+    function extractFBFX(tenderSrc) {
+        const fbfxSrc = getValue(tenderSrc, ['DivisionalAndElementalWorks']);
+        const fields = [['DivisionalWorks'], ['WorkElement']];
+        const fees = getFeesFromBasicCost(fbfxSrc); // 分部分项的汇总价
+        const items = getItemsRecur(fbfxSrc, fields, (itemSrc, curField) => {
+            if (curField[0] === fields[0][0]) { // 分部
+                return extractDivisionalWorks(itemSrc);
+            } else {
+                return extractWorkElement(itemSrc, billType.FX);
             }
         });
+        return {
+            fees,
+            items
+        };
     }
+    // 提取分部
+    function extractDivisionalWorks(divisionalSrc) {
+        // 分部不需要基数,因此不用把基数导入
+        const item = {
+            type: billType.FB,
+            code: getValue(divisionalSrc, ['_Number']),
+            name: getValue(divisionalSrc, ['_Name']),
+            unit: getValue(divisionalSrc, ['_Unit']),
+            quantity: getValue(divisionalSrc, ['_Quantity']),
+            feeCode: getValue(divisionalSrc, ['_Code']),
+            remark: getValue(divisionalSrc, ['_Remark']),
+        };
+        if (importFileKind === FileKind.tender) {
+            const summaryFees = getFeesFromBasicCost(divisionalSrc);
+            const fees = [{ fieldName: 'common', totalFee: getValue(divisionalSrc, ['_Total']), unitFee: getValue(divisionalSrc, ['_TechnicalAndEconomicIndex']) }];
+            item.fees = mergeFees(fees, summaryFees);
+        }
+        return item;
+    }
+    // 提取分项清单数据(包含定额、定额人材机)
+    function extractWorkElement(workElementSrc, type) {
+        // 分项不需要基数,因此不用把基数导入
+        const itemCharacterText = getItemCharacterText(getValue(workElementSrc, ['_Attr']));
+        const bills = {
+            type: type, // 清单类型
+            code: getValue(workElementSrc, ['_Number']),
+            name: getValue(workElementSrc, ['_Name']),
+            itemCharacterText,
+            itemCharacter: getItemCharacter(itemCharacterText),
+            jocContentText: getValue(workElementSrc, ['_WorkContent']),
+            jobContent: getJobContent(workElementSrc),
+            unit: getValue(workElementSrc, ['_Unit']),
+            quantity: getValue(workElementSrc, ['_Quantity']),
+            mainBills: getBool(getValue(workElementSrc, ['_Major'])),
+            feeCode: getValue(workElementSrc, ['_Code']),
+            remark: getValue(workElementSrc, ['_Remark']),
+        };
+        // 投标和控制价,需要导入最高限价
+        if ([FileKind.tender, FileKind.control].includes(importFileKind)) {
+            const maxPrice = getValue(workElementSrc, ['_PriceHigh']);
+            //不为0才输出
+            if (maxPrice && maxPrice !== '0') {
+                bills.outPutMaxPrice = true;
+                bills.maxPrice = maxPrice;
+            }
+            if (importFileKind === FileKind.tender) {
+                const summaryFees = getFeesFromBasicCost(workElementSrc);
+                const fees = [
+                    { fieldName: 'common', unitFee: getValue(workElementSrc, ['_Price']), totalFee: getValue(workElementSrc, ['_Total']) },
+                    { fieldName: 'equipment', unitFee: getValue(workElementSrc, ['_EquipmentPrice']) },
+                ];
+                bills.fees = mergeFees(fees, summaryFees);
+                // 输出定额数据
+                bills.rations = extractRations(workElementSrc);
+            }
+        }
+    }
+    // 获取项目特征文本
+    function getItemCharacterText(attr) {
+        const matchStrs = ['
', ';'];
+        const matchStr = matchStrs.find(str => attr.includes(str));
+        return attr.replace(new RegExp(matchStr, 'g'), '\n');
+    }
+    // 获取清单项目特征窗口数据
+    function getItemCharacter(itemText) {
+        const itemsList = itemText.split('\n');
+        const reg = /\d+\.([^:]+)[::](.*)?/;
+        const itemCharacter = [];
+        let idx = 1;
+        itemsList.forEach(str => {
+            str = str.replace(/\s/g, '');
+            const result = reg.exec(str);
+            if (!result) {
+                return;
+            }
+            const name = result[1];
+            const value = result[2] || '';
+            const eigenvalue = value ? [{ value, isSelected: true }] : [];
+            itemCharacter.push({
+                character: name,
+                isChecked: true,
+                serialNo: idx++,
+                eigenvalue
+            });
+        });
+        return itemCharacter;
+    }
+    // 获取清单工作内容窗口数据
+    function getJobContent(workElementSrc) {
+        return arrayValue(workElementSrc, ['WorkContent']).map((workContent, idx) => ({
+            content: getValue(workContent, ['_Name']),
+            serialNo: idx + 1,
+            isChecked: true
+        }));
+
+    }
+    // 提取定额
+    function extractRations(workElementSrc) {
+        // 正常情况下定额肯定是在WorkContent内的,但是也做定额不在WorkContent内的处理(外层定额)
+        const rations = [];
+        let serialNo = 1;
+        const workContents = arrayValue(workElementSrc, ['WorkContent']);
+        workContents.forEach(workContent => {
+            const innerRationsSrc = arrayValue(workContent, ['Norm']);
+            const jobContentText = getValue(workContent, ['_Name']);
+            const innerRations = innerRationsSrc.map(rationSrc => extractRation(rationSrc, jobContentText));
+            rations.push(...innerRations);
+        });
+        const outerRationsSrc = arrayValue(workElementSrc, ['Norm']);
+        const outerRations = outerRationsSrc.map(rationSrc => extractRation(rationSrc, ''));
+        rations.push(...outerRations);
+        return rations;
+        // 获取类型
+        function getType(rationSrc) {
+            const overHeightMap = {
+                '1': true,
+                '2': false
+            };
+            const isOverHeight = overHeightMap[getValue(rationSrc, ['EfficiencyKind'])];
+            if (isOverHeight) {
+                return rationType.overHeight;
+            }
+            const itemIncreaseMap = {
+                '1': true,
+                '2': false
+            };
+            const isItemIncrease = itemIncreaseMap[getValue(rationsSrc, ['IncFeeKind'])];
+            if (isItemIncrease) {
+                return rationType.itemIncrease;
+            }
+            return rationType.ration;
+        }
+        // 从定额的计算程序提取价格
+        function getFeesFromCalculationOfItem(rationSrc) {
+            const calculationOfItems = arrayValue(rationSrc, ['UnitPriceCalculationOfItem']);
+            const fees = [];
+            const codeFiedNameMap = {
+                'DEZJF': 'direct',
+                'RGF': 'labour',
+                'CLF': 'material',
+                'JXF': 'machine',
+                'ZCF': 'mainMaterial',
+                'SBF': 'equipment',
+                'LR': 'profit',
+                'DJ': 'common',
+
+            };
+            calculationOfItems.forEach(item => {
+                const totalFee = +getValue(item, ['_Total']);
+                const fieldName = codeFiedNameMap[getValue(item, ['_Code'])];
+                if (totalFee && fieldName) {
+                    fees.push({ fieldName, totalFee });
+                }
+            });
+            return fees;
+        }
+        // 提取定额人材机
+        function extractRationGLJs(rationSrc) {
+            const rationGLJsSrc = arrayValue(rationSrc, ['LabourMaterialsEquipmentsMachinesElement']);
+            return rationGLJsSrc.map(src => ({
+                code: getValue(src, ['_Number']),
+                quantity: getValue(src, ['_Quantity'])
+            }));
+        }
+        // TODO 目前无法确定定额的取费专业programID,暂时用专业类别Specialty
+        function extractRation(rationSrc, jobContentText) {
+            const ration = {
+                serialNo: serialNo++,
+                code: getValue(rationSrc, ['_Number']),
+                name: getValue(rationSrc, ['_Name']),
+                unit: getValue(rationSrc, ['_Unit']),
+                quantity: getValue(rationSrc, ['_Quantity']),
+                type: getType(rationSrc),
+                programID: getValue(rationSrc, ['_Specialty']),
+                libCode: getValue(rationSrc, ['_NormIdentity']),
+                jobContentText,
+                remark: getValue(rationSrc, ['_Remark'])
+            };
+            const fees = [{ fieldName: 'common', unitFee: getValue(rationSrc, ['_Price']), totalFee: getValue(rationSrc, ['_Total']) }]
+            const feesFromCalcItem = getFeesFromCalculationOfItem(rationSrc);
+            ration.fees = mergeFees(fees, feesFromCalcItem);
+        }
+    }
+
+
     /**
      * 解压cos、zip文件
      * @param {File} file - 上传的文件 
@@ -186,7 +443,7 @@ const importXML = (() => {
             xmlObjMap[fileName] = xmlObj;
         }
         //提取数据
-        return loadData(xmlObjMap);
+        return extractProject(xmlObjMap);
     };
 
     // 接受上传的文件类型(不同的省份可以上传的文件不同)