'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, mergeFees, getFlag, getBool, getItemsRecur, } = util; //导入的文件类型,界面选的文件类型是生成项目的文件类型,这里的文件类型指的是,要导入文件的类型, //导入文件类型不同,导入数据不同 let importFileKind = ''; //文件类型 const FileKind = { '6': 1, // 投标 'tender': 1, '4': 2, // 招标 'bid': 2, '5': 3, // 控制价 'control': 3, }; const countData = { projectCount: 0, //项目数量 projectGLJCount: 0, //项目人材机数量 ratioCount: 0, //组成物数量 unitPriceCount: 0, //单价数量 unitPriceFileCount: 0, //单价文件数量 }; // 建设项目 function extractProject(xmlObjMap) { Object.keys(countData).forEach(key => countData[key] = 0); // 清缓存 countData.projectCount++; const projectXMLObj = xmlObjMap['Project.xml']; const projectSrc = getValue(projectXMLObj, ['ConstructionProject']); importFileKind = FileKind[getValue(source, ['_FileKind'])]; // 标记当前导入的文件类型 const rst = { projectType: projectType.Project, name: getValue(projectSrc, ['_Name']), engs: extractEngs(projectSrc, xmlObjMap), property: { compilationIllustrationProject: getValue(projectSrc, ['_Explains']) }, basicInformation: extractBasicInfo(projectSrc) }; return rst; } // 从xml对象中提取基本信息相关 function extractBasicInfo(projectSrc) { const projectInfo = getValue(projectSrc, ['ProjectInfo']); // 估概预算信息 const tendereeInfo = getValue(projectSrc, ['TendereeInfo']); // 招标信息 const bidderInfo = getValue(projectSrc, ['BidderInfo']); // 投标信息 return [ { key: 'projNum', value: getValue(projectSrc, ['_Number']) }, // 编码 { key: 'projectCategory', value: getValue(projectSrc, ['_ProjectCategory']) }, // 工程类别 { key: 'constructionType', value: getValue(projectSrc, ['_ConstructionType']) }, // 建设性质 { key: 'regionalCategories', value: getValue(projectSrc, ['_AreaKind']) }, // 地区类被 { key: 'projLocation', value: getValue(projectSrc, ['_ProjectSite']) }, // 工程地点 { key: 'constructingUnits', value: getValue(projectSrc, ['_BulidUnit']) }, // 建设单位 { key: 'constructingUnitsPerson', value: getValue(projectSrc, ['_BulidAuthorizer']) }, // 建设单位法定代表人或其授权人 { key: 'rangeOfCompilation', value: getValue(projectSrc, ['_RangeOfCompilation']) }, // 建设(编制)范围 { key: 'scale', value: getValue(projectSrc, ['_Scale']) }, // 建设规模 { key: 'unit', value: getValue(projectSrc, ['_Unit']) }, // 建设规模单位 { key: 'designUnits', value: getValue(projectInfo, ['_Designer']) }, // 设计单位 { key: 'constructionUnits', value: getValue(projectInfo, ['_Contractor']) }, // 承包单位 { key: 'establishUnit', value: getValue(projectInfo, ['_CompileCompany']) }, // 编制单位 { key: importFileKind === FileKind.tender ? 'bidCompileDate' : 'tenderCompileDate', value: getValue(projectInfo, ['_CompileDate']) }, // 编制时间 { key: 'authorizer', value: getValue(projectInfo, ['_Authorizer']) }, // 编制单位法定代表人或其授权人 { key: 'tendereeName', value: getValue(tendereeInfo, ['_TendereeName']) }, // 招标人 { key: 'tenderAuthorizer', value: getValue(tendereeInfo, ['_TenderAuthorizer']) }, // 招标单位法定代表人或其授权人 { key: 'tenderCompiler', value: getValue(tendereeInfo, ['_TenderCompiler']) }, // 招标单位编制人 { key: 'tenderCompilerCertNo', value: getValue(tendereeInfo, ['_TenderCompilerCertNo']) }, // 招标单位编制人资格证书编号 { key: 'tenderCompileDate', value: getValue(tendereeInfo, ['_TenderCompileDate']) }, // 招标单位编制时间 { key: 'tenderExaminer', value: getValue(tendereeInfo, ['_TenderExaminer']) }, // 招标单位审核人 { key: 'tenderExaminerCertNo', value: getValue(tendereeInfo, ['_TenderExaminerCertNo']) }, // 招标单位审核人资格证书编号 { key: 'tenderExamineDate', value: getValue(tendereeInfo, ['_TenderExamineDate']) }, // 招标单位审核时间 { key: 'tenderApprover', value: getValue(tendereeInfo, ['_TenderApprover']) }, // 招标单位审定人 { key: 'tenderApproverCertNo', value: getValue(tendereeInfo, ['_TenderApproverCertNo']) }, // 招标单位审定人资格证书编号 { key: 'proxy', value: getValue(tendereeInfo, ['_Proxy']) }, // 招标代理 { key: 'proxyCertNo', value: getValue(tendereeInfo, ['_ProxyCertNo']) }, // 招标代理资质证书编号 { key: 'proxyAuthorizer', value: getValue(tendereeInfo, ['_ProxyAuthorizer']) }, // 招标代理法定代表人或其授权人 { key: 'proxyCompiler', value: getValue(tendereeInfo, ['_ProxyCompiler']) }, // 招标代理编制人员 { key: 'proxyCompilerCertNo', value: getValue(tendereeInfo, ['_ProxyCompilerCertNo']) }, // 招标代理编制人员资格证书编号 { key: 'proxyCompileDate', value: getValue(tendereeInfo, ['_ProxyCompileDate']) }, // 招标代理编制时间 { key: 'proxyExaminer', value: getValue(tendereeInfo, ['_ProxyExaminer']) }, // 招标代理审核人 { key: 'proxyExaminerCertNo', value: getValue(tendereeInfo, ['_ProxyExaminerCertNo']) }, // 招标代理审核人资格证书编号 { key: 'proxyExamineDate', value: getValue(tendereeInfo, ['_ProxyExamineDate']) }, // 招标代理审核时间 { key: 'proxyApprover', value: getValue(tendereeInfo, ['_ProxyApprover']) }, // 招标代理审定人 { key: 'proxyApproverCertNo', value: getValue(tendereeInfo, ['_ProxyApproverCertNo']) }, // 招标代理审定人资格证书编号 { key: 'proxyApproveDate', value: getValue(tendereeInfo, ['_ProxyApproveDate']) }, // 招标代理审定时间 { key: 'consultant', value: getValue(tendereeInfo, ['_Consultant']) }, // 造价咨询 { key: 'consultantCertNo', value: getValue(tendereeInfo, ['_ConsultantCertNo']) }, // 造价咨询资质证书编号 { key: 'consultantCompiler', value: getValue(tendereeInfo, ['_ConsultantCompiler']) }, // 造价咨询编制人 { key: 'consultantCompilerCertNo', value: getValue(tendereeInfo, ['_ConsultantCompilerCertNo']) }, // 造价咨询编制人资格证书 { key: 'consultantCompileDate', value: getValue(tendereeInfo, ['_ConsultantCompileDate']) }, // 造价咨询编制时间 { key: 'consultantExaminer', value: getValue(tendereeInfo, ['_ConsultantExaminer']) }, // 造价咨询审核人 { key: 'consultantExaminerCertNo', value: getValue(tendereeInfo, ['_ConsultantExaminerCertNo']) }, // 造价咨询审核人资格证书编号 { key: 'consultantExamineDate', value: getValue(tendereeInfo, ['_ConsultantExamineDate']) }, // 造价咨询审核时间 { key: 'consultantApprover', value: getValue(tendereeInfo, ['_ConsultantApprover']) }, // 造价咨询审定人 { key: 'consultantApproverCertNo', value: getValue(tendereeInfo, ['_ConsultantApproverCertNo']) }, // 造价咨询审定人资格证书编号 { key: 'consultantApproveDate', value: getValue(tendereeInfo, ['_ConsultantApproveDate']) }, // 造价咨询审定时间 { key: 'bidName', value: getValue(bidderInfo, ['_BidName']) }, // 投标人 { key: 'bidAuthorizer', value: getValue(bidderInfo, ['_BidAuthorizer']) }, // 投标单位法定代表人或其授权人 { key: 'bidCompiler', value: getValue(bidderInfo, ['_BidCompiler']) }, // 投标单位编制人 { key: 'bidCompilerCertNo', value: getValue(bidderInfo, ['_BidCompilerCertNo']) }, // 投标单位编制人资格证书编号 { key: 'bidCompileDate', value: getValue(bidderInfo, ['_BidCompileDate']) }, // 投标单位编制时间 { key: 'bidExaminer', value: getValue(bidderInfo, ['_BidExaminer']) }, // 投标单位审核人 { key: 'bidExaminerCertNo', value: getValue(bidderInfo, ['_BidExaminerCertNo']) }, // 投标单位审核人资格证书编号 { key: 'bidExamineDate', value: getValue(bidderInfo, ['_BidExamineDate']) }, // 投标单位审核时间 { key: 'bidApprover', value: getValue(bidderInfo, ['_BidApprover']) }, // 投标单位审定人 { key: 'bidApproverCertNo', value: getValue(bidderInfo, ['_BidApproverCertNo']) }, // 投标单位审定人资格证书 { key: 'bidApproveDate', value: getValue(bidderInfo, ['_BidApproveDate']) }, // 投标单位审定时间 ]; } // 从xml对象中提取单项工程数据 function extractEngs(projectSrc, xmlObjMap) { const sectionWorks = arrayValue(projectSrc, ['ProjectInstallationWorkCost', 'SectionalWorks']); return sectionWorks.map(src => { countData.projectCount++; return { projType: projectType.Engineering, name: getValue(src, ['_Name']), code: getValue(src, ['_Number']), tenders: extractTenders(src, xmlObjMap) } }); } // 从xml对象中提取单位工程数据 function extractTenders(sectionWorkSrc, xmlObjMap) { const unitWorks = arrayValue(sectionWorkSrc, ['UnitWorks']); 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 - 上传的文件 * @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; } //提取数据 return extractProject(xmlObjMap); }; // 接受上传的文件类型(不同的省份可以上传的文件不同) const accept = ['.zip', '.cos']; return { accept, extractData, } })();