ソースを参照

广东接口导入

vian 5 年 前
コミット
2e5818223e

+ 48 - 3
web/building_saas/main/js/models/importStdInterfaceBase.js

@@ -1,5 +1,3 @@
-'use strict';
-
 /**
  *
  *
@@ -8,6 +6,8 @@
  * @version
  */
 
+'use strict';
+
 const importXMLBase = (() => {
     //人材机调整法
     const AdjustType = {
@@ -111,10 +111,16 @@ const importXMLBase = (() => {
             return '0';
         }
         const feeData = fees.find(fee => fee.fieldName === fields[0]);
-        return feeData[fields[1]] || '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) {
@@ -125,6 +131,21 @@ const importXMLBase = (() => {
         });
         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;
@@ -133,6 +154,27 @@ const importXMLBase = (() => {
     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(二维数组,数组里的成员由需要取的字段组成)
@@ -255,6 +297,9 @@ const importXMLBase = (() => {
         arrayValue,
         getFee,
         mergeFees,
+        assignAttr,
+        setTreeData,
+        mergeDataRecur,
         getFlag,
         getBool,
         getItemsRecur,

+ 2 - 3
web/building_saas/pm/js/pm_import.js

@@ -298,7 +298,6 @@ const importView = (() => {
                     //转换数据
                     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);    //文件类型恢复成投标
                     $('#import-taxType').val('1');  //计税方法显示回默认的一般计税法
@@ -451,7 +450,7 @@ const importView = (() => {
                 let importData = await importXML.transformData(xmlObj);
                 debugger;
                 console.log(importData);
-                /* let blob = new Blob([JSON.stringify(importData)], { type: 'text/plain;charset=utf-8' });
+                let blob = new Blob([JSON.stringify(importData)], { type: 'text/plain;charset=utf-8' });
                 // 转换成File实例
                 const key = `${uuid.v1()}.json`;
                 const file = new File([blob], key);
@@ -464,7 +463,7 @@ const importView = (() => {
                 console.timeEnd();
                 if (Array.isArray(rstData)) {
                     doAfterImport(rstData);
-                } */
+                }
             } catch (err) {
                 console.log(err);
                 alert(err);

+ 58 - 1
web/over_write/js/guangdong_2018.js

@@ -153,4 +153,61 @@ if (typeof gljOprObj !== 'undefined') {
         {headerName: "类型", headerWidth: 60, dataCode: "typeName", dataType: "String", hAlign: "center", vAlign: "center"},
         {headerName: "新增", headerWidth: 40, dataCode: "isComplementary", dataType: "String", hAlign: "center", vAlign: "center", cellType: "checkBox", readOnly: true}
     ]
-}
+}
+
+// 导入导出接口
+// 需要用固定类别关联的费用字典,用固定类别来映射
+// 数据节选自标准pdf文件《《建设工程政府投资项目造价数据标准》信息公开版》,附录C-费用名称与费用代号
+// 若映射表中没有映射关系,则费用字典取名称首字母
+const FlagFeeCodeMap = {
+    // 分部分项工程
+    [commonConstants.fixedFlag.SUB_ENGINERRING]: 'QDF',
+    // 措施项目
+    [commonConstants.fixedFlag.MEASURE]: 'CSF',
+    // 其他项目
+    [commonConstants.fixedFlag.OTHER]: 'QTF',
+    // 措施项目的子项
+    [commonConstants.fixedFlag.GREEN_MEASURE_FEE]: 'AQWMSGF', // 绿色施工安全防护措施费
+    [commonConstants.fixedFlag.OTHER_MEASURE_FEE]: 'QTCSF', // 其他措施费
+    // 其他项目的子项
+    [commonConstants.fixedFlag.PROVISIONAL]: 'ZLF', // 暂列金额
+    [commonConstants.fixedFlag.ESTIMATE]: 'ZGJ', // 暂估价
+    [commonConstants.fixedFlag.MATERIAL_PROVISIONAL]: 'ZGC', // 材料(工程设备)暂估价
+    [commonConstants.fixedFlag.ENGINEERING_ESITIMATE]: 'ZGGC', // 专业工程暂估价
+    [commonConstants.fixedFlag.DAYWORK]: 'LXF', // 计日工
+    [commonConstants.fixedFlag.TURN_KEY_CONTRACT]: 'ZCBFWF', // 总承包服务费
+    [commonConstants.fixedFlag.BUDGET_INCLUDE_WORK_FEE]: 'YSBGF', // 预算包干费
+    [commonConstants.fixedFlag.PROJECT_HIGH_QUALITY_FEE]: 'GCYZF', // 工程优质费
+    [commonConstants.fixedFlag.BUDGET_ESTIMATE_DIFF]: 'GSFDC', // 概算幅度差
+    [commonConstants.fixedFlag.CLAIM_VISA]: 'SPYXCQZ', // 索赔与现场签证
+    [commonConstants.fixedFlag.CLAIM]: 'SPFY', // 索赔费用
+    [commonConstants.fixedFlag.VISA]: 'XCQZFY', // 现场签证
+    [commonConstants.fixedFlag.OTHER_FEE]: 'QTFY', // 其他费用
+    // 税金
+    [commonConstants.fixedFlag.TAX]: 'SJ',
+    // 工程造价
+    [commonConstants.fixedFlag.ENGINEERINGCOST]: 'ZZJ',
+};
+// 需要用计算基数关联的费用字典
+const FormulaFeeCodeMap = {
+    '{分部分项工程费}': 'QDF',
+    '{分部分项人工费}': 'QRG',
+    '{分部分项材料费}': 'QCL',
+    '{分部分项施工机具费}': 'QJX',
+    '{分部分项主材费}': 'QZCF',
+    '{分部分项设备费}': 'QSBF',
+    '{分部分项人工工日}': 'FBFXRGGR', // 标准没有,自增
+    '{建筑面积}': 'JZMZ', // 自增
+    '{措施项目费}': 'CSF',
+    '{其他项目费}': 'QTF',
+    '{甲供人工费}': 'JGRGF', // 自增
+    '{甲供材料费}': 'JGC',
+    '{甲供施工机具费}': 'JGSGJJF', // 自增
+    '{甲定人工费}': 'JDRGF', // 自增
+    '{甲定材料费}': 'JDCLF', // 自增
+    '{甲定施工机具费}': 'JDSGJJF', // 自增
+    '{甲定主材费}': 'JDZCF', // 自增
+    '{甲定设备费}': 'JDSBF', // 自增
+    '{暂估材料费(从子目汇总)}': 'ZGCLFCZMHZ', // 自增
+    '{税金}': 'SJ',
+};

+ 16 - 5
web/over_write/js/guangdong_2018_export.js

@@ -93,7 +93,7 @@ const XMLStandard = (function () {
         '修复': '23',
         '其他': '99',
     };
-    // 需要用固定类别关联的费用字典,用固定类别来映射
+/*     // 需要用固定类别关联的费用字典,用固定类别来映射
     // 数据节选自标准pdf文件《《建设工程政府投资项目造价数据标准》信息公开版》,附录C-费用名称与费用代号
     // 若映射表中没有映射关系,则费用字典取名称首字母
     const FlagFeeCodeMap = {
@@ -123,9 +123,9 @@ const XMLStandard = (function () {
         [fixedFlag.TAX]: 'SJ',
         // 工程造价
         [fixedFlag.ENGINEERINGCOST]: 'ZZJ',
-    };
+    }; */
     // 需要用计算基数关联的费用字典
-    const FormulaFeeCodeMap = {
+/*     const FormulaFeeCodeMap = {
         '{分部分项工程费}': 'QDF',
         '{分部分项人工费}': 'QRG',
         '{分部分项材料费}': 'QCL',
@@ -147,7 +147,7 @@ const XMLStandard = (function () {
         '{暂估材料费(从子目汇总)}': 'ZGCLFCZMHZ', // 自增
         '{税金}': 'SJ',
 
-    };
+    }; */
     // 费用字典占用列表,普通清单根据首字母获取费用字典时,与下列占用费用字典重复时,需要加上_序号后缀
     const feeCodeList = [
         'QDF', 'QRG', 'QCL', 'QJX', 'ZCSB', 'QZCF', 'QSBF', 'QGL', 'QLR', 'QZGJ', 'CSF', 'AQWMSGF', 'AXSJSCSXMF',
@@ -364,6 +364,12 @@ const XMLStandard = (function () {
             //普通基数转费用字典
             normalBase.forEach(base => {
                 const feeCode = getFeeCode(null, base);
+                if (/[\(\)]/.test(base)) {
+                    // 将()进行处理不然会被当成组
+                    base = base.replace(/[\(\)]/g, function(matched) {
+                        return matched === '(' ? '\\(' : '\\)'
+                    });
+                }
                 expr = expr.replace(new RegExp(base, 'g'), feeCode);
             });
             //id引用转换为费用字典
@@ -1643,6 +1649,9 @@ const XMLStandard = (function () {
         // 其他项目费明细
         function SundryCostsItem(node) {
             const bills = node.data;
+            if (bills.name === '材料暂估价') {
+                debugger;
+            }
             const attrs = [
                 // 名称
                 { name: 'Name', dName: '名称', required: true, value: bills.name },
@@ -2184,6 +2193,7 @@ const XMLStandard = (function () {
                     await _util.setTimeoutSync(() => { }, _config.TIMEOUT_TIME);
                 }
                 // 获取单位工程详细数据
+                debugger;
                 tenderDetail = await _util.getTenderDetail(tData.ID, userID);
                 // 先计算人材机总消耗量,以供后面需要
                 gljUtil.calcProjectGLJQuantity(tenderDetail.projectGLJ.datas,
@@ -2477,6 +2487,7 @@ const XMLStandard = (function () {
 
             const sundry = new Sundry();
             // 其他项目费
+            debugger;
             const other = tenderDetail.mainTree.roots.find(node => node.getFlag() === fixedFlag.OTHER);
             const sundryCosts = new SundryItem('SundryCosts', other.data);
             sundryCosts.children = loadGroupAndItems(
@@ -2542,7 +2553,7 @@ const XMLStandard = (function () {
                 (node) => new ClaimVisaCostGroup('SiteInstructionCostGroup', node),
                 (node) => new ClaimVisaCostItem('SiteInstructionCostItem', node)
             );
-            sundry.children.push(sundryCosts, provisionalSums, specialtyProvisional, dayworkRate, mainContractorAttendance, claimsCost, siteInstructionCost);
+            sundry.children.push(sundryCosts, provisionalSums, provisionalMaterialEle, specialtyProvisional, dayworkRate, mainContractorAttendance, claimsCost, siteInstructionCost);
             return sundry;
         }
 

+ 651 - 62
web/over_write/js/guangdong_2018_import.js

@@ -17,6 +17,8 @@ const importXML = (() => {
         arrayValue,
         getFee,
         mergeFees,
+        assignAttr,
+        mergeDataRecur,
         getFlag,
         getBool,
         getItemsRecur,
@@ -35,6 +37,7 @@ const importXML = (() => {
         '5': 3, // 控制价
         'control': 3,
     };
+    // 一些数据是需要从后端获取自增数字后赋值的,这里是记录,用完会在extractProject清空
     const countData = {
         projectCount: 0,    //项目数量
         projectGLJCount: 0,  //项目人材机数量
@@ -42,7 +45,17 @@ const importXML = (() => {
         unitPriceCount: 0,   //单价数量
         unitPriceFileCount: 0,  //单价文件数量
     };
-
+    // 精度
+    const Decimal = {
+        // 工料机消耗量、含量、用量类小数精度
+        GLJ: 4,
+        // 工程量、数量类小数精度
+        QUANTITY: 3,
+        // 金额、合价、费用类小数精度
+        FEE: 2,
+        // 费率、指数、比例(%)类小数精度
+        RATE: 3,
+    };
 
     /* 
      * 从导入的xml文件中提取有用的数据
@@ -57,7 +70,7 @@ const importXML = (() => {
         const projectSrc = getValue(projectXMLObj, ['ConstructionProject']);
         importFileKind = FileKind[getValue(projectSrc, ['_FileKind'])]; // 标记当前导入的文件类型
         const rst = {
-            projectType: projectType.Project,
+            projType: projectType.Project,
             name: getValue(projectSrc, ['_Name']),
             engs: extractEngs(projectSrc, xmlObjMap),
             property: {
@@ -280,13 +293,16 @@ const importXML = (() => {
             jobContent: getJobContent(workElementSrc),
             unit: getValue(workElementSrc, ['_Unit']),
             quantity: getValue(workElementSrc, ['_Quantity']),
-            calcBase: getValue(workElementSrc, ['_QtyFormula']),
             feeRate: getValue(workElementSrc, ['_Rate']),
             mainBills: getBool(getValue(workElementSrc, ['_Major'])),
             feeCode: getValue(workElementSrc, ['_Code']),
             remark: getValue(workElementSrc, ['_Remark']),
             rations: extractRations(workElementSrc)
         };
+        if (type === billType.BILL) {
+            // 分项不需要基数
+            bills.calcBase = getValue(workElementSrc, ['_QtyFormula']);
+        }
         // 投标和控制价,需要导入最高限价
         if ([FileKind.tender, FileKind.control].includes(importFileKind)) {
             const maxPrice = getValue(workElementSrc, ['_PriceHigh']);
@@ -469,11 +485,17 @@ const importXML = (() => {
         const sundryCosts = getValue(sundry, ['SundryCosts']);
         // 其他项目汇总费用
         const sundryFees = [{ fieldName: 'common', totalFee: getValue(sundryCosts, ['_Total']) }];
-        debugger;
+        const provisionalMaterialEquipment = getValue(sundry, ['ProvisionalMaterialEquipment']);
         return {
             fees: importFileKind === FileKind.tender ? sundryFees : [],
             sundryCosts: extractData(sundry, 'SundryCosts', [['SundryCostsGroup'], ['SundryCostsItem']]),
             provisional: extractData(sundry, 'ProvisionalSums', [['ProvisionalSumsGroup'], ['ProvisionalSumsItem']]),
+            ProvisionalMaterialEquipment: {
+                name: getValue(provisionalMaterialEquipment, ['_Name']),
+                feeCode: getValue(provisionalMaterialEquipment, ['_Code']),
+                remark: getValue(provisionalMaterialEquipment, ['_Remark']),
+                fees: [{ fieldName: 'common', totalFee: getValue(provisionalMaterialEquipment, ['_Total']) }],
+            },
             specialty: extractData(sundry, 'SpecialtyProvisionalPrice', [['SpecialtyProvisionalPriceGroup'], ['SpecialtyProvisionalPriceItem']],
                 { engineeringContent: ['_Content'] }, { engineeringContent: ['_Content'] }),
             dayWork: extractData(sundry, 'DayWorkRate', [['DayWorkRateGroup'], ['DayWorkRateItem']]),
@@ -505,25 +527,28 @@ const importXML = (() => {
         // 提取数据
         function extractData(sundry, srcName, fields, groupExtendMap, itemExtendMap) {
             const src = getValue(sundry, [srcName]);
-            return extractItemsRecur(src, fields, (itemSrc, curField) => {
-                const groupExtend = groupExtendMap
-                    ? Object.keys(groupExtendMap).reduce((acc, key) => {
-                        acc[key] = getValue(itemSrc, groupExtendMap[key]);
-                        return acc;
-                    }, {})
-                    : null;
-                const itemExtend = itemExtendMap
-                    ? Object.keys(itemExtendMap).reduce((acc, key) => {
-                        acc[key] = getValue(itemSrc, itemExtendMap[key]);
-                        return acc;
-                    }, {})
-                    : null;
-                return curField[0] === fields[0][0]
-                    ? extractGroupOrItem(itemSrc, true, groupExtend)
-                    : extractGroupOrItem(itemSrc, false, itemExtend);
-            });
+            return {
+                fees: importFileKind === FileKind.tender ? [{ fieldName: 'common', totalFee: getValue(src, ['_Total']) }] : [],
+                feeCode: getValue(src, ['_Code']),
+                items: extractItemsRecur(src, fields, (itemSrc, curField) => {
+                    const groupExtend = groupExtendMap
+                        ? Object.keys(groupExtendMap).reduce((acc, key) => {
+                            acc[key] = getValue(itemSrc, groupExtendMap[key]);
+                            return acc;
+                        }, {})
+                        : null;
+                    const itemExtend = itemExtendMap
+                        ? Object.keys(itemExtendMap).reduce((acc, key) => {
+                            acc[key] = getValue(itemSrc, itemExtendMap[key]);
+                            return acc;
+                        }, {})
+                        : null;
+                    return curField[0] === fields[0][0]
+                        ? extractGroupOrItem(itemSrc, true, groupExtend)
+                        : extractGroupOrItem(itemSrc, false, itemExtend);
+                })
+            };
         }
-        // TODO 材料设备暂估价明细
     }
     // 提取税金
     function extractTax(tenderSrc) {
@@ -547,9 +572,9 @@ const importXML = (() => {
 
     // 供料方式
     const Provider = {
-        '1': [commonConstants.supplyType.ZXCG],
-        '2': [commonConstants.supplyType.BFJG],
-        '3': [commonConstants.supplyType.WQJG]
+        '1': commonConstants.supplyType.ZXCG,
+        '2': commonConstants.supplyType.BFJG,
+        '3': commonConstants.supplyType.WQJG
     };
 
     // 提取人材机汇总相关(人材机汇总表、承包材料表)
@@ -562,8 +587,14 @@ const importXML = (() => {
         const gljListSrc = arrayValue(tenderSrc, ['LabourMaterialsEquipmentsMachinesSummary']);
         return gljListSrc.reduce((acc, gljSrc) => {
             acc.gljSummary.push(extractGLJ(gljSrc));
-            acc.differentiaSummary.push(extractDifferentiaGLJ(gljSrc));
-            acc.exponentialSummary.push(extractExponentialGLJ(gljSrc));
+            const differentiaGLJ = extractDifferentiaGLJ(gljSrc);
+            if (differentiaGLJ) {
+                acc.differentiaSummary.push(differentiaGLJ);
+            }
+            const exponentialGLJ = extractExponentialGLJ(gljSrc);
+            if (exponentialGLJ) {
+                acc.exponentialSummary.push(exponentialGLJ);
+            }
             return acc;
         }, initData);
 
@@ -638,29 +669,42 @@ const importXML = (() => {
         }
         // 提取造价信息差额调整法
         function extractDifferentiaGLJ(gljSrc) {
-            return {
-                code: getValue(gljSrc, ['_Number']),
-                name: getValue(gljSrc, ['_Name']),
-                specs: getValue(gljSrc, ['_Specification']),
-                unit: getValue(gljSrc, ['_Unit']),
-                quantity: getValue(gljSrc, ['_Quantity']),
-                riskCoe: getValue(gljSrc, ['_ProviderExp']),
-                standardPrice: getValue(gljSrc, ['_ProviderBase']),
-                market_price: getPriceByTaxType(gljSrc, taxType).market_price,
-                remark: getValue(gljSrc, ['_Remark']),
-            };
+            // 风险系数或者基准单价有值,才算一条差额数据
+            const riskCoe = getValue(gljSrc, ['_ProviderExp']);
+            const standardPrice = getValue(gljSrc, ['_ProviderBase']);
+            if ((riskCoe && riskCoe !== '0') || (standardPrice && standardPrice !== '0')) {
+                return {
+                    code: getValue(gljSrc, ['_Number']),
+                    name: getValue(gljSrc, ['_Name']),
+                    specs: getValue(gljSrc, ['_Specification']),
+                    unit: getValue(gljSrc, ['_Unit']),
+                    quantity: getValue(gljSrc, ['_Quantity']),
+                    riskCoe,
+                    standardPrice,
+                    market_price: getPriceByTaxType(gljSrc, taxType).market_price,
+                    remark: getValue(gljSrc, ['_Remark']),
+                };
+            }
+            return null;
         }
         // 提取价格指数调整法
         function extractExponentialGLJ(gljSrc) {
-            return {
-                code: getValue(gljSrc, ['_Number']),
-                name: getValue(gljSrc, ['_Name']),
-                specs: getValue(gljSrc, ['_Specification']),
-                varWeight: getValue(gljSrc, ['_Weight']),
-                FO: getValue(gljSrc, ['_BasicPrice']),
-                FI: getValue(gljSrc, ['_CurrentPrice']),
-                remark: getValue(gljSrc, ['_Remark'])
-            };
+            // 变值权重或基本、现行价格指数有值,才算一条指数数据
+            const varWeight = getValue(gljSrc, ['_Weight']);
+            const FO = getValue(gljSrc, ['_BasicPrice']);
+            const FI = getValue(gljSrc, ['_CurrentPrice']);
+            if ((varWeight && varWeight !== '0') || (FO && FO !== '0') || (FI && FI !== '0')) {
+                return {
+                    code: getValue(gljSrc, ['_Number']),
+                    name: getValue(gljSrc, ['_Name']),
+                    specs: getValue(gljSrc, ['_Specification']),
+                    varWeight: varWeight,
+                    FO: FO,
+                    FI: FI,
+                    remark: getValue(gljSrc, ['_Remark'])
+                };
+            }
+            return null;
         }
     }
     // 提取评标材料
@@ -669,7 +713,7 @@ const importXML = (() => {
             return [];
         }
         const srcList = arrayValue(tenderSrc, ['BidEvaluationMainMaterial']);
-        return srcList.map(src => ({
+        return srcList.map(gljSrc => ({
             seq: getValue(gljSrc, ['_Code']),
             code: getValue(gljSrc, ['_Number']),
             name: getValue(gljSrc, ['_Name']),
@@ -747,22 +791,33 @@ const importXML = (() => {
                     preTender.tender.NextSiblingID = curTender.ID;
                 }
                 // 项目清单模板(后台清单模板中提取含有固定类别的数据)
-                const projectBills = _.cloneDeep(templateMapping[curTender.property.templateLibID]);
-                debugger;
+                const billsTemplate = _.cloneDeep(templateMapping[curTender.property.templateLibID]);
                 // 重设模板树结构数据
-                BILLS_UTIL.resetTreeData(projectBills, uuid.v1);
-                console.log(projectBills);
-                const tenderData = transformTender(curTender, IDPlaceholder, projectBills);
+                BILLS_UTIL.resetTreeData(billsTemplate, uuid.v1);
+                console.log(billsTemplate);
+                const tenderData = transformTender(curTender, IDPlaceholder, billsTemplate);
                 tenderData.tender.property.rootProjectID = transformedData.ID;
                 engData.tenders.push(tenderData);
             }
         }
+        console.log(`transformedData`);
+        console.log(transformedData);
         return transformedData;
     }
-
+    // 获取承包人材料类型
+    function getAdjustType(tenderData) {
+        if (tenderData.differentiaSummary.length) {
+            return AdjustType.info;
+        } else if (tenderData.exponentialSummary.length) {
+            return AdjustType.coe;
+        } else {
+            return AdjustType.info;
+        }
+    }
+    // 获取当前文件
     // 转换单位工程的数据(直接可插入数据库的数据),返回{tender: {}, bills: [], ration: [], rationGLJ: [], projectGLJ: [], unitPrice: [], mixRatio: []}
-    function transformTender(tenderData, IDPlaceholder, projectBills) {
-        const detailData = transformDetail(tenderData, IDPlaceholder, projectBills);
+    function transformTender(tenderData, IDPlaceholder, billsTemplate) {
+        const detailData = transformDetail(tenderData, IDPlaceholder, billsTemplate);
         // 提取需要插入的单位工程数据
         const transformedTender = {
             ID: tenderData.ID,
@@ -779,20 +834,554 @@ const importXML = (() => {
             gljLibIDs: tenderData.gljLibIDs,
             shareInfo: []
         };
-        transformedTender.property.gljAdjustType = AdjustType.info;
+        transformedTender.property.gljAdjustType = getAdjustType(tenderData)
         detailData.tender = transformedTender;
         return detailData;
     }
-    // 转换清单
-    function transformBills(tenderData, IDPlaceholder, projectBills) {
-
+    // 过滤掉相关费用代号的数据(递归地)
+    // 返回被过滤掉的数据
+    function filterDataByFeeCodes(billsData, feeCodes) {
+        const filterd = [];
+        billsData.forEach(bills => {
+            if (bills.items && bills.items.length) {
+                const newItems = [];
+                bills.items.forEach(subBills => {
+                    if (!feeCodes.includes(subBills.feeCode)) {
+                        newItems.push(subBills);
+                    } else {
+                        filterd.push(subBills);
+                    }
+                });
+                bills.items = newItems;
+                // bills.items = bills.items.filter(subBills => !feeCodes.includes(subBills.feeCode));
+                const subFilterd = filterDataByFeeCodes(bills.items, feeCodes);
+                filterd.push(...subFilterd);
+            }
+        });
+        return filterd;
     }
-    // 转换详细的项目数据,清单、定额、定额人材机、项目人材机、单价文件等
-    function transformDetail() {
+    // 合并清单,将提取出来的清单相关数据,合并进项目清单(模板)
+    function mergeBills(tenderData, billsTemplate) {
+        debugger;
+        const mergedBills = [...billsTemplate];
+        const roots = billsTemplate.filter(bills => bills.ParentID === -1);
+        let lastRoot = roots.find(root => root.NextSiblingID === -1);
+        mergeWorkSummary(tenderData.workSummary);
+        mergeFBFX(tenderData.fbfx);
+        mergeCSXM(tenderData.csxm);
+        mergeOther(tenderData.other);
+        mergeTax(tenderData.tax);
+        return mergedBills;
 
+        // 合并清单数据
+        function mergeitems(fixedBills, items) {
+            const daryWorkMap = {
+                '人工': fixedFlag.LABOUR,
+                '材料': fixedFlag.MATERIAL,
+                '机械': fixedFlag.MACHINE,
+                '施工机械': fixedFlag.MACHINE,
+                '施工机具': fixedFlag.MACHINE
+            };
+            const fixedBillsFlag = getFlag(fixedBills);
+            let preBills = billsTemplate.find(bills => bills.ParentID === fixedBills.ID && bills.NextSiblingID === -1);
+            items.forEach(bills => {
+                // 计日工需要特殊处理,兼容广联达的数据,广联达的计日工下的人工、材料、机械的费用字典不对,因此用名称匹配
+                let matched;
+                if (fixedBillsFlag === fixedFlag.DAYWORK) {
+                    const dayWorkSubFlag = daryWorkMap[bills.name];
+                    matched = dayWorkSubFlag
+                        ? billsTemplate.find(templateBills => getFlag(templateBills) === dayWorkSubFlag)
+                        : null;
+                } else {
+                    matched = billsTemplate.find(templateBills => FlagFeeCodeMap[getFlag(templateBills)] === bills.feeCode);
+                }
+                if (matched) {
+                    assignAttr(matched, bills, ['items'], true);
+                    if (bills.items && bills.items.length) {
+                        mergeitems(matched, bills.items);
+                        //mergedBills.push(...mergeDataRecur(fixedBills, [bills]))    
+                    }
+                    preBills = matched;
+                } else {
+                    const nextID = preBills && preBills.NextSiblingID || -1;
+                    mergedBills.push(...mergeDataRecur(fixedBills, [bills]));
+                    bills.NextSiblingID = nextID;
+                    if (preBills) {
+                        preBills.NextSiblingID = bills.ID;
+                    }
+                    preBills = bills;
+                }
+            });
+        }
+        // 简单地合并: 设置某清单的金额,数据递归插入到某清单的子项
+        function simpleMerge(flag, src) {
+            const fixedBills = billsTemplate.find(bills => getFlag(bills) === flag);
+            if (fixedBills) {
+                assignAttr(fixedBills, src, ['fees', 'feeCode']);
+                mergedBills.push(...mergeDataRecur(fixedBills, src.items));
+            }
+        }
+        // 合并大项费用
+        function mergeWorkSummary(workSummary) {
+            // 首层数据与清单模板根据费用字典进行匹配,匹配到的则赋值一些属性,匹配不到则将其及其所有子数据插入到模板最末大项费用
+            workSummary.forEach(summaryBills => {
+                const matched = roots.find(root => FlagFeeCodeMap[getFlag(root)] === summaryBills.feeCode);
+                if (matched) {
+                    if (!summaryBills.calcBase) {
+                        summaryBills.calcBase = matched.calcBase;
+                        matched.ignoreValidator = true; // 用了模板的基数,基数无视基数验证
+                    }
+                    assignAttr(matched, summaryBills, ['items'], true);
+                } else {
+                    mergedBills.push(...mergeDataRecur(null, [summaryBills]));
+                    if (lastRoot) {
+                        lastRoot.NextSiblingID = summaryBills.ID;
+                        lastRoot = summaryBills;
+                    }
+                }
+            });
+        }
+        // 合并分部分项
+        function mergeFBFX(fbfxSrc) {
+            simpleMerge(fixedFlag.SUB_ENGINERRING, fbfxSrc);
+        }
+        // 合并措施项目
+        function mergeCSXM(csxmSrc) {
+            const fixedBills = billsTemplate.find(bills => getFlag(bills) === fixedFlag.MEASURE);
+            fixedBills.fees = csxmSrc.fees;
+            mergeitems(fixedBills, csxmSrc.items);
+        }
+        // 合并其他项目
+        function mergeOther(otherSrc) {
+            const fixedBills = billsTemplate.find(bills => getFlag(bills) === fixedFlag.OTHER);
+            fixedBills.fees = otherSrc.fees;
+            // 合并其他项目费
+
+            // SundryCosts其他项目费会包含暂列金额等后续会再次出现的数据,因此合并SundryCosts的数据时,过滤掉一部分数据
+            const feeCodeFilter = [
+                FlagFeeCodeMap[fixedFlag.PROVISIONAL], // 暂列金额
+                FlagFeeCodeMap[fixedFlag.MATERIAL_PROVISIONAL], // 材料暂估
+                FlagFeeCodeMap[fixedFlag.ENGINEERING_ESITIMATE], // 专业工程暂估
+                FlagFeeCodeMap[fixedFlag.DAYWORK], // 计日工
+                FlagFeeCodeMap[fixedFlag.TURN_KEY_CONTRACT], // 总承包服务费
+                FlagFeeCodeMap[fixedFlag.CLAIM], // 索赔
+                FlagFeeCodeMap[fixedFlag.VISA], // 现场签证
+            ];
+            const filterdBills = filterDataByFeeCodes([otherSrc.sundryCosts], feeCodeFilter);
+            /**
+             * 有些源xml文件中,被过滤掉SundryCosts中的子项,相对于Sundry的子项会多一些属性
+             * 比如:Sundry->SunryCosts->SundryCostsGroup->SundryCostsItem中的材料暂估价,含有QtyFormula属性,但是Sundry->ProvisionalMaterialEquipment中不含有QtyFormula
+             * 因此,即使后续Sundry的子项会赋值给模板,也要将被过滤掉的子项的属性先赋给模板
+             */
+            filterdBills.forEach(filterdItem => {
+                const matched = billsTemplate.find(templateBills => FlagFeeCodeMap[getFlag(templateBills)] === filterdItem.feeCode);
+                if (matched) {
+                    assignAttr(matched, filterdItem, ['items'], true); // 不拷贝item属性
+                    matched.ignoreA
+                }
+            });
+
+            const sundryCostsBills = otherSrc.sundryCosts.items;
+            mergeitems(fixedBills, sundryCostsBills);
+            // 合并暂列金额
+            simpleMerge(fixedFlag.PROVISIONAL, otherSrc.provisional);
+            // 合并材料暂估,由于材料暂估的子元素是暂估人材机,因此对清单来说,只需处理材料暂估根元素的属性
+            const zgcFixedBills = billsTemplate.find(bills => getFlag(bills) === fixedFlag.MATERIAL_PROVISIONAL);
+            assignAttr(zgcFixedBills, otherSrc.ProvisionalMaterialEquipment);
+            // 合并专业工程暂估价
+            simpleMerge(fixedFlag.ENGINEERING_ESITIMATE, otherSrc.specialty);
+            // 合并计日工
+            const dayWorkFixedBills = billsTemplate.find(bills => getFlag(bills) === fixedFlag.DAYWORK);
+            assignAttr(dayWorkFixedBills, otherSrc.dayWork, ['fees', 'feeCode']);
+            mergeitems(dayWorkFixedBills, otherSrc.dayWork.items);
+            // 合并总承包服务费
+            simpleMerge(fixedFlag.TURN_KEY_CONTRACT, otherSrc.mainContractor);
+            // 合并索赔
+            simpleMerge(fixedFlag.CLAIM, otherSrc.claim);
+            // 合并现场签证
+            simpleMerge(fixedFlag.VISA, otherSrc.visa);
+        }
+        // 合并税金
+        function mergeTax(taxSrc) {
+            const taxFixedBills = billsTemplate.find(bills => getFlag(bills) === fixedFlag.TAX);
+            if (!taxSrc.calcBase) {
+                taxSrc.calcBase = taxFixedBills.calcBase;
+                taxFixedBills.ignoreValidator = true; // 用了模板的基数,基数无视基数验证
+            } else {
+                taxFixedBills.ignoreValidator = false;
+            }
+            assignAttr(taxFixedBills, taxSrc);
+        }
+    }
+    // 给固定清单设置上费用字典,因为在合并清单的时候,清单模板中可能有一些数据应该有规定的费用字典,但是没有被设置上费用字典。
+    function setupFeeCode(billsData) {
+        billsData.forEach(bills => {
+            if (bills.feeCode) {
+                return;
+            }
+            bills.feeCode = FlagFeeCodeMap[getFlag(bills)] || '';
+        })
+    }
+    // 检查清单是否引用了自身,比如广联达导出文件中 材料保管费基数为CLBGF
+    function isCalcBaseCycle(bills) {
+        return bills.calcBase.match(new RegExp(`\\b${bills.feeCode}\\b`)); // \b: 匹配前一个字符和后一个字符不全为\w的位置
     }
+    // 转换计算基数
+    // 1.有子项数据,则清空基数
+    // 2.引用的基数造成自身循环,比如分部分项部分引用了QDF
+    // 3.费用代号匹配清单基数的代号
+    // 4.费用代号匹配不到清单基数的代号,则匹配此费用代号的清单,作为行引用(即ID引用)
+    // 5.对应字典里找不到则设置成金额
+    function transformCalcBase(billsData) {
+        // 费用代号 - 计算基数映射
+        const feeCodeFormulaMap = {}
+        Object
+            .entries(FormulaFeeCodeMap)
+            .forEach(([formula, feeCode]) => {
+                feeCodeFormulaMap[feeCode] = formula;
+            });
+        // 费用代号 - ID映射
+        const feeCodeIDMap = {};
+        billsData.forEach(data => {
+            if (data.feeCode) {
+                feeCodeIDMap[data.feeCode] = data.ID;
+            }
+        });
+        for (const bills of billsData) {
+            if (!bills.calcBase || bills.ignoreValidator) {
+                continue;
+            }
+            const sub = billsData.find(data => data.ParentID === bills.ID);
+            // 有子项数据,则清空基数,费率
+            if (sub) {
+                bills.calcBase = '';
+                bills.feeRate = '';
+                continue;
+            }
+            if (typeof bills.calcBase !== 'string') {
+                bills.calcBase = String(bills.calcBase);
+            }
+            // 引用的基数造成自循环,清空基数
+            const isCycle = isCalcBaseCycle(bills);
+            if (isCycle) {
+                bills.calcBase = '';
+                continue;
+            }
+            // 提取基数中的费用代号
+            bills.calcBase = bills.calcBase.replace(/\s/g, '');
+            const feeCodes = bills.calcBase.split(/[\+\-\*\/]/g);
+            // 提取操作符
+            const oprs = bills.calcBase.match(/[\+\-\*\/]/g);
+            // 转换后的基数
+            const newBase = [];
+            let illegal = false;    //不合法
+            for (const feeCode of feeCodes) {
+                const formula = feeCodeFormulaMap[feeCode];
+                const refID = feeCodeIDMap[feeCode];
+                if (formula) { // 匹配到公式
+                    newBase.push(formula);
+                } else if (refID) { // 匹配到行引用
+                    newBase.push(`@${refID}`) // 转换成ID引用
+                } else { // 无法识别
+                    illegal = true;
+                    break;
+                }
+            }
+            if (illegal) {
+                const fee = getFee(bills.fees, ['common', 'totalFee']);
+                const feeRate = bills.feeRate && parseFloat(bills.feeRate) !== 0 ? parseFloat(bills.feeRate) : 0;
+                if (fee && parseFloat(fee) !== 0 && feeRate) {
+                    bills.calcBase = scMathUtil.roundForObj(parseFloat(fee) * 100 / feeRate, 2);
+                } else {
+                    bills.calcBase = fee !== '0' ? fee : '';
+                }
+            } else {
+                let newCalcBase = '';
+                for (let i = 0; i < newBase.length; i++) {
+                    newCalcBase += newBase[i];
+                    if (oprs && oprs[i]) {
+                        newCalcBase += oprs[i];
+                    }
+                }
+                bills.calcBase = newCalcBase;
+            }
+        }
+    }
+    // 转换清单
+    function transformBills(tenderData, billsTemplate) {
+        const billsData = mergeBills(tenderData, billsTemplate);
+        setupFeeCode(billsData);
+        transformCalcBase(billsData);
+        // 处理综合单价
+        // 没有子清单,且没有综合单价,且有综合合价,则综合单价 = 综合合价 / 工程量
+        billsData.forEach(bills => {
+            const hasChild = billsData.some(data => data.ParentID === bills.ID);
+            let unitFee = getFee(bills.fees, ['common', 'unitFee']);
+            const totalFee = getFee(bills.fees, ['common', 'totalFee']);
+            if (!hasChild && !parseFloat(unitFee) && totalFee && parseFloat(totalFee)) {
+                // 不存工程量
+                if (!bills.quantity || !parseFloat(bills.quantity)) {
+                    unitFee = totalFee;
+                    // 源不存在工程量,没有计算基数、但是却有综合合价,工程量要设置为1
+                    if (!bills.calcBase) {
+                        bills.quantity = '1';
+                    }
+                } else {
+                    // 综合合价的小数位数
+                    const totalFeeDecimal = totalFee.match(/\.\d+/);
+                    unitFee = scMathUtil.roundForObj(totalFee / bills.quantity, totalFeeDecimal ? totalFeeDecimal[0] : 0);
+                }
+                const commonFee = bills.fees.find(fee => fee.fieldName === 'common');
+                if (commonFee) {
+                    commonFee.unitFee = unitFee;
+                }
+            }
+            bills.projectID = tenderData.ID;
+            bills.quantityEXP = bills.quantity;
+            // TODO 删除items rations feeCode
+        });
+        return billsData;
+    }
+    // 转换人材机汇总相关数据(人材机汇总、承包人材料、评标材料、暂估价材料、组成物、单价文件)
+    function transformGLJList(tenderData, IDPlaceholder, projectGLJMap) {
+        const rst = {
+            projectGLJ: [], // 项目人材机
+            contractorList: [], // 承包人材料
+            bidEvaluationList: [], // 评标材料
+            evaluationList: [], // 暂估材料
+            unitPrice: [], // 单价文件
+            mixRatio: [] // 组成物
+        };
+        transformGLJSummary();
+        transformContractorSummary();
+        transformRelatedGLJList(tenderData.bidEvaluationSummary, rst.bidEvaluationList, 'is_eval_material');
+        transformEvalSummary();
+        return rst;
 
+        // 转换项目人材机、组成物
+        function transformGLJSummary() {
+            // 项目人材机
+            tenderData.gljSummary.forEach(projectGLJ => {
+                projectGLJ.project_id = tenderData.ID;
+                projectGLJ.id = IDPlaceholder.projectGLJ++;
+                projectGLJMap[projectGLJ.code] = projectGLJ;
+            });
+            tenderData.gljSummary.forEach(projectGLJ => {
+                //组成物数据
+                projectGLJ.ratios.forEach(ratio => {
+                    const matched = projectGLJMap[ratio.code];
+                    ratio.code = matched.code;
+                    ratio.projectGLJID = projectGLJ.id;
+                    ratio.id = IDPlaceholder.ratio++;
+                    ratio.unit_price_file_id = tenderData.property.unitPriceFile.id;
+                    ratio.unit = matched && matched.unit || '';
+                    ratio.name = matched && matched.name || '';
+                    ratio.specs = matched && matched.specs || '';
+                    ratio.type = matched && matched.type || 1;
+                    ratio.connect_key = [projectGLJ.code || 'null', projectGLJ.name || 'null', projectGLJ.specs || 'null', projectGLJ.unit || 'null', projectGLJ.type].join('|-|');
+                    rst.mixRatio.push(ratio);
+                });
+                rst.projectGLJ.push(projectGLJ);
+                // 单价文件数据:
+                rst.unitPrice.push(generateUnitPrice(projectGLJ));
+                // TODO 删除ratios
+            });
+        }
+        // 转换与项目人材机关联的材料
+        function transformRelatedGLJList(list, container, relatedType) {
+            list.forEach(glj => {
+                glj.ID = uuid.v1();
+                glj.projectID = tenderData.ID;
+                glj.projectGLJID = -1;
+                if (typeof glj.seq === 'undefined') {
+                    glj.seq = glj.code;
+                }
+                const projectGLJ = projectGLJMap[glj.code];
+                if (projectGLJ) {
+                    projectGLJ[relatedType] = 1;
+                    glj.is_related = 1;
+                    glj.projectGLJID = projectGLJ.id;
+                }
+                container.push(glj);
+            });
+        }
+        // 转换承包人材料
+        function transformContractorSummary() {
+            // 由于承包人差额数据和指数数据共用承包人材料表,因此需要合并差额数据和指数数据
+            const mergedList = [];
+            tenderData.exponentialSummary.forEach(eGLJ => {
+                const matched = tenderData.differentiaSummary.find(dGLJ => dGLJ.code === eGLJ.code);
+                if (matched) {
+                    assignAttr(matched, eGLJ, ['varWeight', 'FO', 'FI']);
+                } else {
+                    mergedList.push(eGLJ);
+                }
+            });
+            mergedList.push(...tenderData.differentiaSummary);
+            transformRelatedGLJList(mergedList, rst.contractorList, 'is_contractor_material');
+        }
+        // 转换暂估价材料
+        function transformEvalSummary() {
+            const evalGLJList = tenderData.gljSummary
+                .filter(glj => glj.is_evaluate)
+                .map(glj => {
+                    const evalGLJ = {};
+                    assignAttr(evalGLJ, glj, [
+                        'code', 'name', 'specs', 'unit', 'quantity', 'market_price',
+                        'originPlace', 'vender', 'remark'
+                    ]);
+                    evalGLJ.seq = evalGLJ.code;
+                    return evalGLJ;
+                });
+            transformRelatedGLJList(evalGLJList, rst.evaluationList, 'is_evaluate');
+        }
+        // 生成单价文件
+        function generateUnitPrice(projectGLJ) {
+            return {
+                projectGLJID: projectGLJ.id,    //做个标记,后端查找标准数据后,方便更新组成物数据
+                unit_price_file_id: tenderData.property.unitPriceFile.id,
+                id: IDPlaceholder.unitPrice++,
+                code: projectGLJ.code,
+                original_code: projectGLJ.original_code,
+                name: projectGLJ.name,
+                unit: projectGLJ.unit,
+                specs: projectGLJ.specs,
+                type: projectGLJ.type,
+                short_name: projectGLJ.shortName,
+                base_price: projectGLJ.base_price,
+                market_price: projectGLJ.market_price
+            };
+        }
+    }
+    // 转换定额、定额人材机、定额系数
+    // @param {Object} tenderData - 提取的单位工程数据
+    // @param {Array} billsData - 转换后的清单数据
+    // @param {Object} projectGLJMap - 转换项目人材机时生成的项目人材机编码-数据的映射
+    function transformRations(tenderData, billsData, projectGLJMap) {
+        const rst = {
+            ration: [],
+            rationGLJ: [],
+            rationCoe: []
+        };
+        billsData
+            .filter(bills => bills.rations && bills.rations.length)
+            .forEach(bills => {
+                bills.rations.forEach(ration => {
+                    // TODO 取费专业不知如何取,暂时取单位工程取费专业
+                    ration.programID = tenderData.property.projectEngineering;
+                    ration.ID = uuid.v1();
+                    ration.projectID = tenderData.ID;
+                    ration.billsItemID = bills.ID;
+                    ration.contain = getContain(bills, ration);
+                    rst.ration.push(ration);
+                    // 定额人材机
+                    rst.rationGLJ.push(...transformRationGLJs(bills, ration));
+                    // 定额系数
+                    rst.rationCoe.push(generateRationCoe(ration));
+                    // TODO 删除rationGljs
+                });
+            });
+        return rst;
 
+        // 含量:定额工程量/清单工程量
+        function getContain(bills, ration) {
+            if (!bills.quantity || !ration.quantity) {
+                return '0';
+            } else {
+                const tempQuantity = ration.quantity / bills.quantity;
+                return isFinite(tempQuantity) ? scMathUtil.roundForObj(tempQuantity, Decimal.QUANTITY) : '0';
+            }
+        }
+        // 转换定额人材机
+        function transformRationGLJs(bills, ration) {
+            return ration.rationGLJs.map(rationGLJ => {
+                const matched = projectGLJMap[rationGLJ.code];
+                if (matched) {
+                    rationGLJ.projectGLJID = matched.id;
+                    rationGLJ.code = matched.code;
+                    rationGLJ.type = matched.type;
+                    rationGLJ.shortName = matched.shortName;
+                    rationGLJ.name = matched.name;
+                    rationGLJ.original_code = matched.original_code;
+                    rationGLJ.unit = matched.unit;
+                    rationGLJ.specs = matched.specs;
+                }
+                rationGLJ.ID = uuid.v1();
+                rationGLJ.projectID = tenderData.ID;
+                rationGLJ.billsItemID = bills.ID;
+                rationGLJ.rationID = ration.ID;
+                rationGLJ.rationCode = ration.code;  // 暂时跟定额编码关联,后端好匹配标准数据
+                rationGLJ.rationItemQuantity = rationGLJ.quantity;  // 定额消耗,暂时取消耗量,需要后端匹配标准数据后更新
+                return rationGLJ;
+            });
+        }
+        // 生成定额系数(项目数据处理的方法很有可能无法共用,因此不把重庆的方法抽离了)
+        function generateRationCoe(ration) {
+            return {
+                projectID: ration.projectID,
+                rationID: ration.ID,
+                coeID: -1,
+                ID: uuid.v1(),
+                seq: 1, // 以前排序有的字段,暂时没什么用,不过还是赋上一个值
+                name: '自定义系数',
+                content: '人工×1,材料×1,机械×1,主材×1,设备×1',
+                coes: [
+                    { amount: 1, operator: '*', gljCode: null, coeType: '定额' },
+                    { amount: 1, operator: '*', gljCode: null, coeType: '人工' },
+                    { amount: 1, operator: '*', gljCode: null, coeType: '材料' },
+                    { amount: 1, operator: '*', gljCode: null, coeType: '机械' },
+                    { amount: 1, operator: '*', gljCode: null, coeType: '主材' },
+                    { amount: 1, operator: '*', gljCode: null, coeType: '设备' },
+                ],
+                option_list: []
+            }
+        }
+    }
+    // 删除一些无用属性,减少通信数据量
+    function clean(detailData) {
+        const {
+            bills,
+            ration,
+            projectGLJ
+        } = detailData;
+        bills.forEach(item => delete item.items && delete item.rations && delete item.feeCode);
+        ration.forEach(item => delete item.rationGLJs);
+        projectGLJ.forEach(item => delete item.ratios);
+    }
+    // 需要给清单、定额价格字段赋上调价数据,不然打开项目后会被重算: project_view.js -> loadProjectData方法内
+    function setupTenderFees(detailData) {
+        const { bills, ration } = detailData;
+        [...bills, ...ration].forEach(item => {
+            if (item.fees && item.fees.length) {
+                item.fees.forEach(feeItem => {
+                    if (commonUtil.isDef(feeItem.unitFee)) {
+                        feeItem.tenderUnitFee = feeItem.unitFee;
+                    }
+                    if (commonUtil.isDef(feeItem.totalFee)) {
+                        feeItem.tenderTotalFee = feeItem.totalFee;
+                    }
+                })
+            }
+        });
+    }
+    // 转换详细的项目数据,清单、定额、定额人材机、项目人材机、单价文件等
+    function transformDetail(tenderData, IDPlaceholder, billsTemplate) {
+        const bills = transformBills(tenderData, billsTemplate);
+        // 转换定额的处理依赖转换后的项目人材机数据,因此需要先转换项目人材机相关的数据
+        const projectGLJMap = {};
+        const relatedljGLJData = transformGLJList(tenderData, IDPlaceholder, projectGLJMap);
+        const relatedRationData = transformRations(tenderData, bills, projectGLJMap);
+        const detailData = {
+            bills,
+            ...relatedRationData,
+            ...relatedljGLJData
+        };
+        clean(detailData);
+        setupTenderFees(detailData);
+        console.log(`detailData`);
+        console.log(detailData);
+        return detailData;
+    }
 
     /**
      * 解压cos、zip文件