|
|
@@ -8,9 +8,37 @@
|
|
|
* @version
|
|
|
*/
|
|
|
/*
|
|
|
-* 尽可能减少服务器压力,前端提取后端所需数据
|
|
|
+* 尽可能减少服务器压力,前端提取后端所需数据,提取成更符合我们程序数据结构的数据
|
|
|
+* 源数据中,含有前缀"_"的数据,为原xml节点中的属性,不含前缀的数据为原xml节点的节点名
|
|
|
* */
|
|
|
const ImportXML = (() => {
|
|
|
+ //文件类型
|
|
|
+ const FileKind = {
|
|
|
+ '招标': 1,
|
|
|
+ '投标': 2,
|
|
|
+ };
|
|
|
+ //计税方法
|
|
|
+ const TaxTypeMap = {
|
|
|
+ '一般计税法': 1,
|
|
|
+ '一般计税': 1,
|
|
|
+ '简易计税法': 2,
|
|
|
+ '简易计税': 2,
|
|
|
+ '': 1 //默认
|
|
|
+ };
|
|
|
+ //清单类型
|
|
|
+ const BillsType ={
|
|
|
+ DXFY: 1, //大项费用
|
|
|
+ FB: 2, //分部
|
|
|
+ FX: 3, //分项
|
|
|
+ BILLS: 4, //清单
|
|
|
+ BX: 5 //补项
|
|
|
+ };
|
|
|
+ //项目类型
|
|
|
+ const ProjectType = {
|
|
|
+ Project: 'Project',
|
|
|
+ Engineering: 'Engineering',
|
|
|
+ Tender: 'Tender'
|
|
|
+ };
|
|
|
//读取文件转换为utf-8编码的字符串
|
|
|
function readAsTextSync(file) {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
@@ -26,10 +54,10 @@ const ImportXML = (() => {
|
|
|
}
|
|
|
/*
|
|
|
* 根据字段数组获得所要字段的值 eg: 要获取标段下的单项工程: ['标段', '单项工程'];
|
|
|
- * @param {Object}obj原始对象 {Array}fields字段数组
|
|
|
+ * @param {Object}source源数据 {Array}fields字段数组
|
|
|
* */
|
|
|
- function getValue(obj, fields) {
|
|
|
- let cur = obj;
|
|
|
+ function getValue(source, fields) {
|
|
|
+ let cur = source;
|
|
|
for (let field of fields) {
|
|
|
if (!cur[field]) {
|
|
|
return '';
|
|
|
@@ -43,7 +71,7 @@ const ImportXML = (() => {
|
|
|
}
|
|
|
return function () {
|
|
|
/*
|
|
|
- * 获取某字段的值,返回数组
|
|
|
+ * 获取某字段的值,强制返回数组
|
|
|
* */
|
|
|
function arrayValue(source, fields) {
|
|
|
let target = getValue(source, fields);
|
|
|
@@ -55,36 +83,337 @@ const ImportXML = (() => {
|
|
|
return target;
|
|
|
}
|
|
|
//标段
|
|
|
- function project(source) {
|
|
|
- return {
|
|
|
+ function loadProject(source) {
|
|
|
+ let obj = {
|
|
|
+ projType: ProjectType.Project,
|
|
|
name: getValue(source, ['标段', '_项目名称']),
|
|
|
- engs: eng(source)
|
|
|
+ engs: loadEng(source),
|
|
|
+ property: {
|
|
|
+ taxType: TaxTypeMap[getValue(source, ['标段', '_计税方法'])],
|
|
|
+ fileKind: FileKind[getValue(source, ['标段', '_文件类型'])],
|
|
|
+ compilationIllustration: getValue(source, ['标段', '编制说明', '_内容'])
|
|
|
+ },
|
|
|
+ basicInformation: loadBasicInfo(source),
|
|
|
};
|
|
|
+ //统计项目数量
|
|
|
+ let count = 1 + obj.engs.length;
|
|
|
+ obj.engs.forEach(eng => {
|
|
|
+ count += eng.tenders.length;
|
|
|
+ });
|
|
|
+ obj.projectCount = count;
|
|
|
+ return obj;
|
|
|
+ }
|
|
|
+ //基本信息相关
|
|
|
+ function loadBasicInfo(source) {
|
|
|
+ let info = [];
|
|
|
+ info.push({key: 'projNum', value: getValue(source, ['标段', '_项目编号'])});
|
|
|
+ info.push({key: 'constructionUnit', value: getValue(source, ['标段', '_建设单位'])});
|
|
|
+ //项目信息
|
|
|
+ let projInfoSrc = getValue(source, ['标段', '项目信息']);
|
|
|
+ if (projInfoSrc) {
|
|
|
+ info.push({key: 'projectScale', value: getValue(projInfoSrc, ['_工程规模'])});
|
|
|
+ info.push({key: 'projLocation', value: getValue(projInfoSrc, ['_工程所在地'])});
|
|
|
+ info.push({key: 'projAddress', value: getValue(projInfoSrc, ['_工程地址'])});
|
|
|
+ info.push({key: 'buildingUnit', value: getValue(projInfoSrc, ['_施工单位'])});
|
|
|
+ info.push({key: 'establishmentUnit', value: getValue(projInfoSrc, ['_编制单位'])});
|
|
|
+ info.push({key: 'auditUnit', value: getValue(projInfoSrc, ['_审核单位'])});
|
|
|
+ info.push({key: 'buildingUnitAuthor', value: getValue(projInfoSrc, ['_编制人'])});
|
|
|
+ info.push({key: 'auditUnitAuditor', value: getValue(projInfoSrc, ['_审核人'])});
|
|
|
+ info.push({key: 'commencementDate', value: getValue(projInfoSrc, ['_开工日期'])});
|
|
|
+ info.push({key: 'completionDate', value: getValue(projInfoSrc, ['_竣工日期'])});
|
|
|
+ info.push({key: 'establishDate', value: getValue(projInfoSrc, ['_编制日期'])});
|
|
|
+ info.push({key: 'auditDate', value: getValue(projInfoSrc, ['_审核日期'])});
|
|
|
+ info.push({key: 'materialPricePeriod', value: getValue(projInfoSrc, ['_材料价格期'])});
|
|
|
+ info.push({key: 'contractPriceType', value: getValue(projInfoSrc, ['_合同价类型'])});
|
|
|
+ //招标信息
|
|
|
+ info.push({key: 'tenderingAgency', value: getValue(projInfoSrc, ['招标信息', '_招标代理机构'])});
|
|
|
+ info.push({key: 'tenderCostEngineer', value: getValue(projInfoSrc, ['招标信息', '_造价工程师'])});
|
|
|
+ info.push({key: 'tenderCostEngineerNo', value: getValue(projInfoSrc, ['招标信息', '_造价工程师注册证号'])});
|
|
|
+ info.push({key: 'tenderPeriod', value: getValue(projInfoSrc, ['招标信息', '_招标工期'])});
|
|
|
+ info.push({key: 'engineeringCost', value: getValue(projInfoSrc, ['招标信息', '_控制总价'])});
|
|
|
+ let firstEng = arrayValue(source, ['标段', '单项工程'])[0];
|
|
|
+ if (firstEng) {
|
|
|
+ let firstTender = arrayValue(firstEng, ['单位工程'])[0];
|
|
|
+ if (firstTender) {
|
|
|
+ info.push({key: 'projectCategory', value: getValue(firstTender, ['工程特征', '_工程类别'])});
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return info;
|
|
|
}
|
|
|
//单项工程
|
|
|
- function eng(source) {
|
|
|
- let engSrcs = arrayValue(source, ['标段', '单项工程']),
|
|
|
- rst = [];
|
|
|
- for (let src of engSrcs) {
|
|
|
- rst.push({
|
|
|
+ function loadEng(source) {
|
|
|
+ return arrayValue(source, ['标段', '单项工程']).map(src => {
|
|
|
+ return {
|
|
|
+ projType: ProjectType.Engineering,
|
|
|
name: getValue(src, ['_名称']),
|
|
|
- tenders: tender(src)
|
|
|
- });
|
|
|
- }
|
|
|
- return rst;
|
|
|
+ tenders: loadTender(src)
|
|
|
+ };
|
|
|
+ });
|
|
|
}
|
|
|
//单位工程
|
|
|
- function tender(engSrc) {
|
|
|
- let tenderSrcs = arrayValue(engSrc, ['单位工程']),
|
|
|
- rst = [];
|
|
|
- for (let src of tenderSrcs) {
|
|
|
- rst.push({
|
|
|
+ function loadTender(engSrc) {
|
|
|
+ return arrayValue(engSrc, ['单位工程']).map(src => {
|
|
|
+ return {
|
|
|
+ projType: ProjectType.Tender,
|
|
|
name: getValue(src, ['_名称']),
|
|
|
engineering: getValue(src, ['_专业']),
|
|
|
+ taxType: TaxTypeMap[getValue(src, ['_计税方法'])],
|
|
|
+ projectFeature: loadProjectFeature(src),
|
|
|
+ fbfx: loadFBFX(src),
|
|
|
+ csxm: loadCSXM(src),
|
|
|
+ other: loadOther(src),
|
|
|
+ chargeTax: loadChargeTax(src)
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ //工程特征相关,特征项没有固定的项,不清楚导入的项无法用已有key对应,而且工程特征名称不会有重复,因此用名称与我们软件的工程特征对应
|
|
|
+ function loadProjectFeature(tenderSrc) {
|
|
|
+ let feature = [];
|
|
|
+ feature.push({name: '建筑分类', value: getValue(tenderSrc, ['工程特征', '_建筑分类'])});
|
|
|
+ feature.push({name: '工程分类', value: getValue(tenderSrc, ['工程特征', '_工程分类'])});
|
|
|
+ feature.push({name: '建设规模', value: getValue(tenderSrc, ['工程特征', '_建设规模'])});
|
|
|
+ let items = arrayValue(tenderSrc, ['工程特征', '特征项']);
|
|
|
+ feature.push(...items.map(item => {
|
|
|
+ return {name: getValue(item, ['_名称']), value: getValue(item, ['_内容'])};
|
|
|
+ }));
|
|
|
+ return feature;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * 递归获取相关数据,eg:获取组织措施清单下的所有子清单,该子清单们可能由分类及公式措施项各种组合组成。(参考调用处)
|
|
|
+ * @param {Object}src(数据源) {Array}fields(二维数组,数组里的成员由需要取的字段组成)
|
|
|
+ * eg: ['组织措施分类'], ['公式计算措施项'],该层数据可能由组织措施分类 或 公式计算措施项组成 (同层不可同时存在)
|
|
|
+ * {Function} 获得源数据后,需要提取的数据方法
|
|
|
+ * @return {Array}
|
|
|
+ * */
|
|
|
+ function getItemsRecur(src, fields, extractFuc) {
|
|
|
+ let itemsSrc = [],
|
|
|
+ curField = [''];
|
|
|
+ for (let field of fields) {
|
|
|
+ itemsSrc = arrayValue(src, field);
|
|
|
+ if (itemsSrc.length) {
|
|
|
+ curField = field;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return itemsSrc.map(itemSrc => {
|
|
|
+ let obj = extractFuc(itemSrc, curField);
|
|
|
+ obj.items = getItemsRecur(itemSrc, fields, extractFuc);
|
|
|
+ return obj;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ //分部分项,两种情况 分部分项-清单分部-清单项目、分部分项-清单项目
|
|
|
+ //return {Array}
|
|
|
+ function loadFBFX(tenderSrc) {
|
|
|
+ let fbfxSrc = getValue(tenderSrc, ['分部分项清单']),
|
|
|
+ fields = [['清单分部'], ['清单项目']];
|
|
|
+ return getItemsRecur(fbfxSrc, fields, (itemSrc, curField) => {
|
|
|
+ if (curField[0] === fields[0][0]) { //提取分部所需数据
|
|
|
+ return {
|
|
|
+ type: BillsType.FB,
|
|
|
+ code: getValue(itemSrc, ['_编号']),
|
|
|
+ name: getValue(itemSrc, ['_名称']),
|
|
|
+ remark: getValue(itemSrc, ['_备注']),
|
|
|
+
|
|
|
+ }
|
|
|
+ } else { //提取清单项目所需数据
|
|
|
+ return extractBills(itemSrc, BillsType.FX);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ //提取清单项目数据
|
|
|
+ function extractBills(fxSrc, type) {
|
|
|
+ return {
|
|
|
+ type: type, //清单类型
|
|
|
+ code: getValue(fxSrc, ['_项目编码']),
|
|
|
+ name: getValue(fxSrc, ['_项目名称']),
|
|
|
+ unit: getValue(fxSrc, ['_单位']),
|
|
|
+ isEstimate: JSON.parse(getValue(fxSrc, ['_暂估清单标志'])),
|
|
|
+ mainBills: JSON.parse(getValue(fxSrc, ['_主要清单标志'])),
|
|
|
+ quantity: getValue(fxSrc, ['_工程量']),
|
|
|
+ remark: getValue(fxSrc, ['_备注']),
|
|
|
+ itemCharacterText: itemCharacterText(fxSrc)
|
|
|
+ };
|
|
|
+ //特征及内容文本(默认显示在项目特征列)
|
|
|
+ function itemCharacterText(fxSrc) {
|
|
|
+ let textArr = [];
|
|
|
+ //项目特征
|
|
|
+ let itemCharacter = getValue(fxSrc, ['项目特征']);
|
|
|
+ if (itemCharacter) {
|
|
|
+ textArr.push('[项目特征]');
|
|
|
+ let features = arrayValue(itemCharacter, ['特征']);
|
|
|
+ textArr.push(...features.map(feature => {
|
|
|
+ let fName = getValue(feature, ['_特征名称']),
|
|
|
+ fDesr = getValue(feature, ['_特征描述']);
|
|
|
+ //若不存在特征描述,同时特征名称中含有“:”,则直接返回特征名称 (广联达招标文件中,特征名称包含了特征名:值)
|
|
|
+ if (!fDesr && /[\:,:]/.test(fName)) {
|
|
|
+ return fName;
|
|
|
+ } else {
|
|
|
+ return `${fName}: ${fDesr}`;
|
|
|
+ }
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ //工作内容
|
|
|
+ if (textArr.length) {
|
|
|
+ textArr.push('[工作内容]');
|
|
|
+ }
|
|
|
+ let jobContent = getValue(fxSrc, ['工程内容']);
|
|
|
+ if (jobContent) {
|
|
|
+ let jobs = arrayValue(jobContent, ['内容']);
|
|
|
+ textArr.push(...jobs.map(job => getValue(job, ['_内容'])));
|
|
|
+ }
|
|
|
+ return textArr.join('\n');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //措施项目
|
|
|
+ function loadCSXM(tenderSrc) {
|
|
|
+ let target = {};
|
|
|
+ let zzcsxmSrc = getValue(tenderSrc, ['措施项目清单', '组织措施清单']);
|
|
|
+ target.zzcs = loadZZCS(zzcsxmSrc);
|
|
|
+ let jscsxmSrc = getValue(tenderSrc, ['措施项目清单', '技术措施清单']);
|
|
|
+ target.jscs = loadJSCS(jscsxmSrc);
|
|
|
+ return target;
|
|
|
+ //组织措施清单
|
|
|
+ function loadZZCS(zzcsSrc) {
|
|
|
+ let fields = [['组织措施分类'], ['公式计算措施项']];
|
|
|
+ return getItemsRecur(zzcsSrc, fields, (itemSrc, curField) => {
|
|
|
+ if (curField[0] === fields[0][0]) { //组织措施分类
|
|
|
+ return {
|
|
|
+ type: BillsType.BILLS,
|
|
|
+ code: getValue(itemSrc, ['_编码']),
|
|
|
+ name: getValue(itemSrc, ['_名称']),
|
|
|
+ remark: getValue(itemSrc, ['_备注'])
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return { //公式计算措施项
|
|
|
+ type: BillsType.BILLS,
|
|
|
+ code:getValue(itemSrc, ['_序号']),
|
|
|
+ name: getValue(itemSrc, ['_名称']),
|
|
|
+ calcBase: getValue(itemSrc, ['_计算基础表达式']),
|
|
|
+ feeRate: getValue(itemSrc, ['_费率']),
|
|
|
+ isEstimate: JSON.parse(getValue(itemSrc, ['_暂估价标志'])),
|
|
|
+ remark: getValue(itemSrc, ['_备注'])
|
|
|
+ };
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ //技术措施清单
|
|
|
+ function loadJSCS(jscsSrc) {
|
|
|
+ let fields = [['技术措施分类'], ['清单项目']];
|
|
|
+ return getItemsRecur(jscsSrc, fields, (itemSrc, curField) => {
|
|
|
+ if (curField[0] === fields[0][0]) { //技术措施分类
|
|
|
+ return {
|
|
|
+ type: BillsType.BILLS,
|
|
|
+ code: getValue(itemSrc, ['_编号']),
|
|
|
+ name: getValue(itemSrc, ['_名称'])
|
|
|
+ }
|
|
|
+ } else { //清单项目
|
|
|
+ return extractBills(itemSrc, BillsType.BILLS);
|
|
|
+ }
|
|
|
});
|
|
|
}
|
|
|
- return rst;
|
|
|
}
|
|
|
+ //其他项目
|
|
|
+ function loadOther(tenderSrc) {
|
|
|
+ let otherSrc = getValue(tenderSrc, ['其他项目清单']);
|
|
|
+ //暂列金额
|
|
|
+ let provisional = arrayValue(otherSrc, ['暂列金额', '暂列金额明细']).map(src => {
|
|
|
+ return {
|
|
|
+ type: BillsType.BILLS,
|
|
|
+ code: getValue(src, ['_编号']),
|
|
|
+ name: getValue(src, ['_项目名称']),
|
|
|
+ unit: getValue(src, ['_计量单位']),
|
|
|
+ remark: getValue(src, ['_备注'])
|
|
|
+ };
|
|
|
+ });
|
|
|
+ //专业工程暂估价
|
|
|
+ let engineeringPro = arrayValue(otherSrc, ['专业工程暂估价', '专业工程暂估明细']).map(src => {
|
|
|
+ return {
|
|
|
+ type: BillsType.BILLS,
|
|
|
+ code: getValue(src, ['_编号']),
|
|
|
+ name: getValue(src, ['_工程名称']),
|
|
|
+ engineeringContent: getValue(src, ['_工程内容']),
|
|
|
+ remark: getValue(src, ['_备注'])
|
|
|
+ };
|
|
|
+ });
|
|
|
+ //计日工
|
|
|
+ function extractDayWorkItem(src) {
|
|
|
+ return {
|
|
|
+ type: BillsType.BILLS,
|
|
|
+ code: getValue(src, ['_编号']),
|
|
|
+ name: getValue(src, ['_名称']),
|
|
|
+ unit: getValue(src, ['_单位']),
|
|
|
+ quantity: getValue(src, ['_数量']),
|
|
|
+ remark: getValue(src, ['_备注'])
|
|
|
+ };
|
|
|
+ }
|
|
|
+ let labour = arrayValue(otherSrc, ['计日工', '人工', '计日工项目']).map(extractDayWorkItem),
|
|
|
+ material = arrayValue(otherSrc, ['计日工', '材料', '计日工项目']).map(extractDayWorkItem),
|
|
|
+ machine = arrayValue(otherSrc, ['计日工', '施工机械', '计日工项目']).map(extractDayWorkItem);
|
|
|
+ let dayWork = {labour, material, machine};
|
|
|
+ //总承包服务费
|
|
|
+ let fields = [['总承包服务费分类'], ['总承包服务费费用项']],
|
|
|
+ serviceSrc = getValue(otherSrc, ['总承包服务费']);
|
|
|
+ let service = getItemsRecur(serviceSrc, fields, (src, curField) => {
|
|
|
+ if (curField[0] === fields[0][0]) {
|
|
|
+ return { //总承包服务费分类
|
|
|
+ type: BillsType.BILLS,
|
|
|
+ code: getValue(src, ['_编号']),
|
|
|
+ name: getValue(src, ['_名称']),
|
|
|
+ remark: getValue(src, ['_备注'])
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return { //总承包服务费费用项
|
|
|
+ type: BillsType.BILLS,
|
|
|
+ code:getValue(src, ['_编号']),
|
|
|
+ name: getValue(src, ['_工程名称']),
|
|
|
+ //calcBase: getValue(src, ['_计算基础']),
|
|
|
+ serviceContent: getValue(src, ['_服务内容']),
|
|
|
+ feeRate: getValue(src, ['_费率']),
|
|
|
+ remark: getValue(src, ['_备注'])
|
|
|
+ };
|
|
|
+ }
|
|
|
+ });
|
|
|
+ //索赔、签证
|
|
|
+ function extractClaimVisa(src) {
|
|
|
+ return {
|
|
|
+ type: BillsType.BILLS,
|
|
|
+ code: getValue(src, ['_编号']),
|
|
|
+ name: getValue(src, ['_项目名称']),
|
|
|
+ unit: getValue(src, ['_计量单位']),
|
|
|
+ quantity: getValue(src, ['_数量']),
|
|
|
+ claimVisa: getValue(src, ['_依据'])
|
|
|
+ };
|
|
|
+ }
|
|
|
+ let claim = arrayValue(otherSrc, ['索赔计价汇总', '签证索赔计价汇总费用项']).map(extractClaimVisa),
|
|
|
+ visa = arrayValue(otherSrc, ['现场签证计价汇总', '签证索赔计价汇总费用项']).map(extractClaimVisa);
|
|
|
+ //其他
|
|
|
+ let others = arrayValue(otherSrc, ['其他', '其他列项']).map(src => {
|
|
|
+ return {
|
|
|
+ type: BillsType.BILLS,
|
|
|
+ code: getValue(src, ['_编号']),
|
|
|
+ name: getValue(src, ['_项目名称']),
|
|
|
+ calcBase: getValue(src, ['_计算基础']),
|
|
|
+ feeRate: getValue(src, ['_费率']),
|
|
|
+ remark: getValue(src, ['_备注'])
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return {provisional, engineeringPro, dayWork, service, claim, visa, others};
|
|
|
+ }
|
|
|
+ //规费和税金清单
|
|
|
+ function loadChargeTax(tenderSrc) {
|
|
|
+ return arrayValue(tenderSrc, ['规费和税金清单', '费用项']).map(src => {
|
|
|
+ return {
|
|
|
+ type: BillsType.BILLS,
|
|
|
+ code: getValue(src, ['_序号']),
|
|
|
+ name: getValue(src, ['_名称']),
|
|
|
+ calcBase: getValue(src, ['_计算基础表达式']),
|
|
|
+ feeRate: getValue(src, ['_费率']),
|
|
|
+ remark: getValue(src, ['_备注'])
|
|
|
+ };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ //公式计算措施项
|
|
|
this.extractData = async (file) => {
|
|
|
//将二进制文件转换成字符串
|
|
|
let xmlStr = await readAsTextSync(file);
|
|
|
@@ -96,8 +425,8 @@ const ImportXML = (() => {
|
|
|
}
|
|
|
console.log(xmlObj);
|
|
|
//提取数据
|
|
|
- console.log(project(xmlObj));
|
|
|
- return project(xmlObj);
|
|
|
+ console.log(loadProject(xmlObj));
|
|
|
+ return loadProject(xmlObj);
|
|
|
};
|
|
|
};
|
|
|
})();
|