/** * @author Zhong * @date 2019/6/20 * @version */ const INTERFACE_EXPORT_BASE = (() => { 'use strict'; const { hasValue, isHan } = window.commonUtil; // 属性类型 const TYPE = { DATE: 1, // 日期类型YYYY-MM-DD DATE_TIME: 2, // 日期类型YYY-MM-DDTHH:mm:ss INT: 3, // 整数类型 DECIMAL: 4, // 数值类型,不限制小数位数 NUM2: 5, // 数值类型2:最多两位小数 BOOL: 6 // 布尔型 }; // 需要特殊处理的属性类型默认空值(当一个值为undefined、null的时候,默认给赋什么值) const DEFAULT_VALUE = { [TYPE.INT]: '0', [TYPE.DECIMAL]: '0', [TYPE.NUM2]: '0', [TYPE.BOOL]: 'false' }; // 空白字符处理 const WHITE_SPACE = { COLLAPSE: 1 // 移除所有空白字符(换行、回车、空格以及制表符会被替换为空格,开头和结尾的空格会被移除,而多个连续的空格会被缩减为一个单一的空格) }; // 承包人材料调整类型 const ADJUST_TYPE = { info: 'priceInfo', // 造价信息差额调整法 coe: 'priceCoe' // 价格指数调整法 }; // 加载数据间隔,减少服务器压力 const TIMEOUT_TIME = 400; const { GRANULARITY, EXPORT_KIND } = window.commonConstants; /* const EXPORT_KIND_NAME = { 1: '招标', 2: '投标', 3: '控制价' }; */ // 配置项 const CONFIG = Object.freeze({ TYPE, WHITE_SPACE, ADJUST_TYPE, TIMEOUT_TIME, }); // 缓存项 不需要的时候需要清空 const _cache = { // 错误提示列表 failList: [], // 项目数据(不包含详细数据,项目管理数据) projectData: {}, // 当前导出类型,默认投标 exportKind: EXPORT_KIND.BID_SUBMISSION, // 记录拉取的单位工程项目详细数据,导出的时候,可能会导出多个文件,只有导出第一个文件的时候需要请求数据 tenderDetailMap: {} }; // 返回缓存项 function getItem(key) { return _cache[key] || null; } // 设置缓存项 function setItem(key, value) { // 与原数据是同类型的数据才可设置成功 if (_cache[key] && Object.prototype.toString.call(_cache[key]) === Object.prototype.toString.call(value)) { _cache[key] = value; } } // 清空缓存项 function clear() { _cache.failList = []; _cache.projectData = {}; _cache.exportKind = EXPORT_KIND.BID_SUBMISSION; _cache.tenderDetailMap = {}; } const CACHE = Object.freeze({ getItem, setItem, clear }); /* * 定义不设置一个Node方法统一进入的原因:模板化比较直观,不分开定义节点的话,调用传参也很麻烦而且不直观。 * 一个节点对应一个构造方法,方便调整配置、方便其他版本开发、接手的人看起来更直观 * @param {String}name 节点名 * {Array}attrs 节点属性数据 * @return {void} * */ function Element(name, attrs = []) { this.name = name; this.attrs = attrs; this.attrs = check(this.attrs); handleXMLEntity(this.attrs); this.children = []; } /* * xml字符实体的处理,这些特殊字符不处理会导致xml文件格式出错:""、<>、& * 要先处理& * */ const _xmlEntity = { '&': '&', '\n': ' ', '"': '"', '\'': ''', '<': '<', '>': '>' }; // 对每个元素的所有属性值进行特殊字符处理 function handleXMLEntity(attrs) { for (const attr of attrs) { if (!attr.value) { continue; } for (const [key, value] of Object.entries(_xmlEntity)) { attr.value = attr.value.replace(new RegExp(key, 'g'), value); } } } // 获取处理实体字符后的数据 function getParsedData(arr) { return arr.map(data => { for (const [key, value] of Object.entries(_xmlEntity)) { data = data.replace(new RegExp(key, 'g'), value); } return data; }); } // 获取Date类型默认值 function getDateTypeDefaultValue(date) { const month = String(date.getMonth() + 1); const formattedMonth = month.length === 1 ? `0${month}` : month; const day = String(date.getDate()); const formattedDay = day.length === 1 ? `0${day}` : day; return `${date.getFullYear()}-${formattedMonth}-${formattedDay}`; } /* * 检查 * 创建节点时检查节点的数据(原本是用于自检,现在来处理默认值) * @param {Array}datas 需要检查的属性数据 * @return {Array} * */ function check(datas) { const rst = []; for (const data of datas) { // 错误提示 if (data.fail && data.fail.hint) { _cache.failList.push(data.fail); } const isHasValue = hasValue(data.value); // 设置了mustHasValue,意味着必须要有值才输出该属性 if (data.mustHasValue && !isHasValue) { continue; } rst.push(data); // 值统一转换成String,并且处理各类型属性空值时的默认取值 data.value = !isHasValue ? DEFAULT_VALUE[data.type] ? DEFAULT_VALUE[data.type] : '' : String(data.value); // 如果有限定最少长度,则当长度不够时,自动凑 const autoStr = '1'; if (data.minLen && data.value.length < data.minLen) { let diff = data.minLen - data.value.length; while (diff--) { data.value += autoStr; } } if (data.whiteSpace && data.whiteSpace === WHITE_SPACE.COLLAPSE) { //处理空格相关 data.value = data.value.replace(/[\r\n\t]/g, ' '); data.value = data.value.trim(); data.value = data.value.replace(/\s{1,}/g, ' '); } // 类型对应得值不正确时,赋类型对应默认值 if (!data.type) { continue; } const dateReg = /([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8])))/; if (data.type === TYPE.DATE && !dateReg.test(data.value)) { data.value = getDateTypeDefaultValue(new Date()); } else if (data.type === TYPE.INT && !Number.isInteger(parseFloat(data.value))) { data.value = DEFAULT_VALUE[TYPE.INT]; } else if (data.type === TYPE.DECIMAL && isNaN(parseFloat(data.value))) { data.value = DEFAULT_VALUE[TYPE.DECIMAL]; } else if (data.type === TYPE.NUM2) { data.value = DEFAULT_VALUE[TYPE.NUM2]; } else if (data.type === TYPE.BOOL && !['true', 'false'].includes(String(data.value))) { data.value = DEFAULT_VALUE[TYPE.BOOL]; } //自动补0 if (data.toFix) { data.value = scMathUtil.roundToString(data.value,data.toFix) } } return rst; } // 等待一段时间 function setTimeoutSync(handle, time) { return new Promise((resolve, reject) => { setTimeout(() => { if (handle && typeof handle === 'function') { handle(); } resolve(); }, time); }); } /* * 将节点属性数据(attr数组)转换成简单key-value数据 * @param {Object}ele 元素节点数据Element实例 * @return {Object} * */ function getPlainAttrs(ele) { const obj = {}; ele.attrs.forEach(attr => obj[attr.name] = attr.value); return obj; } /* * 从fees数组中获取相关费用 * @param {Array}fees 费用数组 * {String}feeFields 费用字段 * @return {Number} * @example getFee(source.fees, 'common.totalFee') * */ function getFee(fees, feeFields, exportKind = null) { if (exportKind === EXPORT_KIND.BID_INVITATION || !Array.isArray(fees)) { return 0; } const fields = feeFields.split('.'); const fee = fees.find(data => data.fieldName === fields[0]); if (!fee) { return 0; } return fee[fields[1]] || 0; } //取单价等 function getUnitFee(fee,quantity,decimal){ if(!!+quantity && +quantity !== 0){ return scMathUtil.roundForObj(fee/parseFloat(quantity),decimal) } return fee } // 获取节点的汇总价格 function getAggregateFee(nodes) { const total = nodes.reduce((acc, node) => { const price = getFee(node.data.fees, 'common.totalFee'); return acc += price; }, 0); return scMathUtil.roundTo(total, -2); } // 获取固定类别行的费用 function getFeeByFlag(items, flag, feeFields) { const node = items.find(node => node.getFlag() === flag); return node ? getFee(node.data.fees, feeFields) : '0'; } // 获取固定类别节点 function getNodeByFlag(tree, flag) { return tree.items.find(node => node.getFlag() === flag); } /* * 根据key获取对应的基本信息、工程特征数据 * @param {Array}data * {String}key * @return {String} * @example getValueByKey(source.basicInformation, 'projectScale') * */ function getValueByKey(items, key) { for (const item of items) { if (item.key === key) { return item.value; } if (item.items && item.items.length) { const value = getValueByKey(item.items, key); if (value) { return value; } } } return ''; } //获取当前日期,格式YYYY-MM-DD function getNowFormatDay(nowDate) { var char = "-"; if (nowDate == null) { nowDate = new Date(); } var day = nowDate.getDate(); var month = nowDate.getMonth() + 1; //注意月份需要+1 var year = nowDate.getFullYear(); //补全0,并拼接 return year + char + completeDate(month) + char + completeDate(day); } //获取当前时间,格式YYYY-MM-DD HH:mm function getNowFormatTime(T) { var nowDate = new Date(); var colon = ":"; var h = nowDate.getHours(); var m = nowDate.getMinutes(); var s = nowDate.getSeconds(); if (T) { return getNowFormatDay(nowDate) + "T" + completeDate(h) + colon + completeDate(m)+colon+completeDate(s); } //补全0,并拼接 return getNowFormatDay(nowDate) + " " + completeDate(h) + colon + completeDate(m); } //补全0 function completeDate(value) { return value < 10 ? "0" + value : value; } // 获取关联材料 function getRelGLJ(allGLJs, gljId) { return allGLJs.find(glj => glj.id === gljId); } // 随机生成机器信息码:CPU信息;硬盘序列号;mac地址; // 保存在localStorage中 function generateHardwareId() { const hardwareCacheId = window.localStorage.getItem('hardwareId'); if (hardwareCacheId) { return hardwareCacheId; } const charList = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ]; function generateCpuId() { let id = ''; let count = 16; while (count--) { const randomIdx = parseInt(Math.random() * 16); id += charList[randomIdx]; } return id; } function generateDiskId() { let id = ''; let count = 8; while (count--) { const randomIdx = parseInt(Math.random() * 36); id += charList[randomIdx]; } return id; } function generateMacId() { const idList = []; let outerCount = 6; while (outerCount--) { let tempId = ''; let innerCount = 2; while (innerCount--) { const randomIdx = parseInt(Math.random() * 16); tempId += charList[randomIdx]; } idList.push(tempId); } return idList.join('-'); } const cpuId = generateCpuId(); const diskId = generateDiskId(); const macId = generateMacId(); const hardwareId = [cpuId, diskId, macId].join(';'); window.localStorage.setItem('hardwareId', hardwareId); return hardwareId; } // 数组打平成对象 function arrayToObj(arr) { const rst = {}; for (const data of arr) { rst[data.key] = data.value; } return rst; } /* * 检测层数是否有效 * @param {Number}maxDepth(最大深度) * {Object}node(需要检测的清单树节点) * @return {Boolean} * */ function validDepth(maxDepth, node) { const nodeDepth = node.depth(); const allNodes = node.getPosterity(); //检测相对深度 for (const n of allNodes) { const relativeDepth = n.depth() - nodeDepth; if (relativeDepth > maxDepth) { return false; } } return true; } // 根据数据的NextSiblingID进行排序,返回排序后的数组 function sortByNext(datas) { const target = []; const temp = {}; for (const data of datas) { temp[data.ID] = { me: data, next: null, prev: null }; } for (const data of datas) { const next = temp[data.NextSiblingID] || null; temp[data.ID].next = next; if (next) { next.prev = temp[data.ID]; } } let first = null; for (const data of datas) { const me = temp[data.ID]; if (!me.prev) { first = me; } } if (!first) { return datas; } while (first) { target.push(first.me); first = first.next; } return target; } /* * 根据粒度获取项目(不包含详细数据)数据 * @param {Number}granularity 导出粒度 * {Object}requestForSummaryInfo 项目表级汇总字段(建设项目、单位工程汇总) * {Number}tenderID 单位工程ID * {String}userID 用户ID * @return {Object} 返回的数据结构:{children: [{children: []}]} 最外层为建设项目,中间为单项工程,最底层为单位工程 * */ async function getProjectByGranularity(granularity, requestForSummaryInfo, tenderID, userID) { let projectData = _cache.projectData; // 没有数据,需要拉取 if (projectData === undefined || projectData === null || !Object.keys(projectData).length) { projectData = await ajaxPost('/pm/api/getProjectByGranularity', { user_id: userID, tenderID, granularity, requestForSummaryInfo }); _cache.projectData = projectData; } return projectData; } /* * 通过getData接口获取单位工程详细数据(带缓存功能) * @param {Number}tenderID 单位工程ID * {String}userID 用户ID * @return {Object} 跟projectObj.project的数据结构一致 * */ async function getTenderDetail(tenderID, userID) { // 获取单位工程详细数据 let tenderDetail = _cache.tenderDetailMap[tenderID]; if (!tenderDetail) { tenderDetail = PROJECT.createNew(tenderID, userID); await tenderDetail.loadDataSync(); // 标记序号 const count = Object.keys(_cache.tenderDetailMap).length; tenderDetail.serialNo = count + 1; _cache.tenderDetailMap[tenderID] = tenderDetail; } return tenderDetail; } // 获取普通基数: {xxx} function getNormalBase(str) { const reg = /{.+?}/g; const matchs = str.match(reg); return matchs || []; } // 获取id引用基数: @xxx-xxx-xx function getIDBase(str) { const reg = /@.{36}/g; const matchs = str.match(reg); return matchs || []; } // 转换基数表达式 // 1.有子项,则取固定清单对应基数 // 2.无子项,有基数,a.优先转换为行代号(不可自身) b.不能转换为行代号则找对应字典 // 3.基数中有无法转换的,根据导出类型决定 function transformCalcBase(exportKind, tenderDetail, node, { CalcBaseMap, FlagCalcBaseMap }) { let expr = node.data.calcBase || ''; if (node.children.length) { const flag = node.getFlag(); return FlagCalcBaseMap[flag] || ''; } if (expr) { let illegal = false; const normalBase = getNormalBase(expr); const idBase = getIDBase(expr); // 普通基数转基数字典 normalBase.forEach(base => { let replaceStr = CalcBaseMap[base]; // 转换成行代号的优先级比较高,进行清单匹配 const flag = FlagCalcBaseMap[base]; if (flag) { const flagNode = tenderDetail.mainTree.items.find(mNode => mNode.getFlag() === flag); // 匹配到了 普通基数转换成行引用 if (flagNode) { replaceStr = `F${flagNode.serialNo() + 1}`; } } // 存在无法处理的基数 if (!replaceStr) { illegal = true; return; } expr = expr.replace(new RegExp(base, 'g'), replaceStr); }); // id引用转行代号引用 idBase.forEach(base => { const id = base.match(/[^@]+/)[0]; const theNode = tenderDetail.mainTree.getNodeByID(id); const rowCode = theNode ? `F${theNode.serialNo() + 1}` : ''; if (!rowCode) { illegal = true; return; } expr = expr.replace(new RegExp(base, 'g'), rowCode); }); // 不合法 // 在我们软件中的基数无法找到映射代号的情况下 // 导出招标、控制价时,基数为空 // 导出投标时,基数=综合合价/费率 if (illegal) { if (exportKind === EXPORT_KIND.BID_INVITATION || exportKind === EXPORT_KIND.CONTROL) { return ''; } else { const totalFee = getFee(node.data.fees, 'common.totalFee'); const feeRate = node.data.feeRate; return +feeRate ? scMathUtil.roundTo(totalFee / (feeRate / 100), -2) : totalFee } } return expr; } } // 转换基数说明,根据转换后的基数处理 // 1.行引用转换为对应行的名称 // 2.基数字典转换为中文 function transformCalcBaseState(tenderDetail, expr, CalcStateMap) { if (!expr) { return ''; } expr = String(expr); // 提取基数 const bases = expr.split(/[\+\-\*\/]/g); // 提取操作符 const oprs = expr.match(/[\+\-\*\/]/g); // 转换后的基数 const newBase = []; let illegal = false; for (const base of bases) { // 行引用转换为名称. if (/F\d+/.test(base)) { const rowCode = base.match(/\d+/)[0]; const node = tenderDetail.mainTree.items[rowCode - 1]; if (!node || !node.data.name) { illegal = true; break; } newBase.push(node && node.data.name ? node.data.name : ''); } else if (CalcStateMap[base]) { // 字典转换为中文 newBase.push(CalcStateMap[base]); } else if (/^\d+(\.\d+)?$/.test(base)) { // 金额 newBase.push(base); } else { illegal = true; break; } } if (illegal) { return ''; } let newExpr = ''; for (let i = 0; i < newBase.length; i++) { newExpr += newBase[i]; if (oprs && oprs[i]) { newExpr += oprs[i]; } } return newExpr; } // 获取节点的某属性 function getAttr(ele, name) { return (ele.attrs.find(attr => attr.name === name) || {}).value; } // 设置节点的某属性 function setAttr(ele, name, value) { const attr = ele.attrs.find(attr => attr.name === name); if (attr) { attr.value = value; } } // 从srcEle节点中获取元素名为eleName的元素 function getElementFromSrc(srcEle, eleName) { if (!srcEle || !srcEle.children || !srcEle.children.length) { return []; } return srcEle.children.filter(ele => ele.name === eleName); } /* * 设置完工程编号后,更新原始数据的工程编号 * 更新原始数据前需要将编号里的特殊字符进行转换 * @param {Array}exportData 提取出来的需要导出的数据 * {Array}codes 工程编号表中填写的工程编号 * {String}EngineeringName 单项工程元素的名称 * {String}tenderName 单位工程元素的名称 * {String}codeName 编号属性的名称 * @return {void} * */ function setupCode(exportData, codes, EngineeringName, tenderName, codeName) { // 转换xml实体字符 let parsedCodes = getParsedData(codes); // 给导出数据里的单项工程、单位工程填上用户设置的工程编号 exportData.forEach(orgData => { let curIdx = 0; let engs = getElementFromSrc(orgData.data, EngineeringName); engs.forEach(eng => { eng.attrs.find(attr => attr.name === codeName).value = parsedCodes[curIdx++]; let tenders = getElementFromSrc(eng, tenderName); tenders.forEach(tender => { tender.attrs.find(attr => attr.name === codeName).value = parsedCodes[curIdx++]; }); }); }); } // 将文本的中文提取出来 function getHan(str) { if (!str) { return ''; } return str .split('') .reduce((acc, cur) => { if (isHan(cur)) { acc.push(cur); } return acc; }, []) .join(''); } // 将错误提示数据进行分类处理 function transformFailList(failList) { const grouped = _.groupBy(failList, 'type'); const rst = []; Object .entries(grouped) .forEach(([type, items]) => { if (type) { rst.push(`${type}:`); } items.forEach(({ hint }) => { rst.push(hint); }); }); return rst; } const UTIL = Object.freeze({ hasValue, setTimeoutSync, getFee, getUnitFee, getAggregateFee, getFeeByFlag, getNodeByFlag, getPlainAttrs, getValueByKey, getRelGLJ, generateHardwareId, arrayToObj, validDepth, sortByNext, getTenderDetail, getProjectByGranularity, getNormalBase, getIDBase, transformCalcBase, transformCalcBaseState, getElementFromSrc, getAttr, setAttr, getParsedData, setupCode, getHan, getNowFormatTime, transformFailList, }); // 开始标签 function _startTag(ele) { let rst = `<${ele.name}`; for (const attr of ele.attrs) { rst += ` ${attr.name}="${attr.value}"`; } rst += ele.children.length > 0 ? '>' : '/>'; return rst; } // 结束标签 function _endTag(ele) { return ``; } // 拼接成xml字符串 function _toXMLStr(eles) { let rst = ''; for (const ele of eles) { rst += _startTag(ele); if (ele.children.length > 0) { rst += _toXMLStr(ele.children); rst += _endTag(ele); } } return rst; } // 格式化xml字符串 function _formatXml(text) { // 去掉多余的空格 text = '\n' + text.replace(/>\s*?\n<"); // 调整格式 const reg = /\n(<(([^\?]).+?)(?:\s|\s*?>|\s*?(\/)>)(?:.*?(?:(?:(\/)>)|(?:<(\/)\2>)))?)/mg; const nodeStack = []; const output = text.replace(reg, function ($0, all, name, isBegin, isCloseFull1, isCloseFull2, isFull1, isFull2) { const isClosed = (isCloseFull1 === '/') || (isCloseFull2 === '/') || (isFull1 === '/') || (isFull2 === '/'); let prefix = ''; if (isBegin === '!') { prefix = getPrefix(nodeStack.length); } else { if (isBegin !== '/') { prefix = getPrefix(nodeStack.length); if (!isClosed) { nodeStack.push(name); } } else { nodeStack.pop(); prefix = getPrefix(nodeStack.length); } } return '\n' + prefix + all; }); return output.substring(1); function getPrefix(prefixIndex) { const span = ' '; const output = []; for (let i = 0; i < prefixIndex; i++) { output.push(span); } return output.join(''); } } /** * 提取要导出的数据 * @param {Function} entryFunc - 提取数据的入口方法 * @param {Object} requestForSummaryInfo - 项目表级汇总字段(建设项目、单位工程汇总) * @param {Number} exportKind - 导出的文件类型:1-招标、2-投标、3-控制价 * @param {String} areaKey - 地区标识,如:'安徽@马鞍山' * @param {Number} tenderID - 单位工程ID * @param {String} userID - 用户ID * @return {Promise} - [{data: Object, exportKind: Number, fileName: String}] */ async function extractExportData(entryFunc, requestForSummaryInfo, exportKind, areaKey, tenderID, userID) { // 默认导出投标文件 if (!exportKind || ![1, 2, 3].includes(exportKind)) { exportKind = EXPORT_KIND.BID_SUBMISSION; } // 拉取标段数据:建设项目、单位工程数据(projects表数据) const projectData = await getProjectByGranularity(GRANULARITY.PROJECT, requestForSummaryInfo, tenderID, userID); if (!projectData) { throw '获取项目数据错误'; } // 单位工程按照树结构数据进行排序,这样导出才的单位工程顺序才是对的 projectData.children = sortByNext(projectData.children); // 先获取需要导出的单位工程的详细数据 const tenderDetailMap = getItem('tenderDetailMap'); for (const tenderItem of projectData.children) { if (!tenderDetailMap[tenderItem.ID]) { await setTimeoutSync(() => { }, TIMEOUT_TIME); // 需要请求项目详细数据的时候,间隔一段时间再初始单位工程数据,减少服务器压力 } // 获取单位工程详细数据 const detail = await getTenderDetail(tenderItem.ID, userID); tenderDetailPretreatment(detail); console.log(detail); } // 提取相关项目的详细导出数据 return await entryFunc(areaKey, exportKind, projectData, tenderDetailMap); } // 对getData返回的数据进行一些通用预处理,方便各接口直接取值、处理 function tenderDetailPretreatment(tenderDetail) { const bidEvaluationList = tenderDetail.bid_evaluation_list.datas; const evaluateList = tenderDetail.evaluate_list.datas; const decimalInfo = tenderDetail.projectInfo.property.decimal; // 项目人材机汇总排序 tenderDetail.projectGLJ.datas.gljList = gljUtil.sortProjectGLJ(tenderDetail.projectGLJ.datas.gljList); const projectGLJList = tenderDetail.projectGLJ.datas.gljList; // 计算人材机总消耗量,否则projectGLJ.datas.gljList里的数据不会有消耗量数据 gljUtil.calcProjectGLJQuantity(tenderDetail.projectGLJ.datas, tenderDetail.ration_glj.datas, tenderDetail.Ration.datas, tenderDetail.Bills.datas, tenderDetail.property.decimal.glj.quantity, _, scMathUtil); const connectKeyMap = {}; const projectGLJIDMap = {}; projectGLJList.forEach(glj => { connectKeyMap[gljUtil.getIndex(glj, gljKeyArray)] = glj.id; // 为了方便后续导出处理,给组成物数据设置上对应项目人材机ID projectGLJIDMap[glj.id] = glj; // 项目人材机设置价格信息,否则projectGLJ.datas.gljList里的数据不会有相关价格信息 // 价格信息存在新的priceInfo字段,以免对一些方法造成影响 glj.priceInfo = gljUtil.getGLJPrice(glj, tenderDetail.projectGLJ.datas, tenderDetail.property.calcOptions, tenderDetail.labourCoe.datas, decimalInfo, false, _, scMathUtil, {}, tenderDetail.projectGLJ.getTenderPriceCoe(glj, tenderDetail.property)); // 计算合价:人材料总消耗量*预算价 glj.priceInfo.totalPrice = scMathUtil.roundForObj(glj.priceInfo.tenderPrice * glj.tenderQuantity, 2); }); const ratiosArr = Object.values(tenderDetail.projectGLJ.datas.mixRatioMap); ratiosArr.forEach(ratios => { ratios.forEach(ratio => { const ratioKey = gljUtil.getIndex(ratio, gljKeyArray); const projectGLJID = connectKeyMap[ratioKey]; if (projectGLJID) { ratio.projectGLJID = projectGLJID; } }); }); // 处理定额人材机,将定额人材机挂到定额的rationGLJList字段中,同时将定额人材机进行排序、计算调价消耗量 tenderDetail.Ration.datas.forEach(ration => { ration.rationGLJList = tenderDetail.ration_glj.datas.filter(glj => { const pGLJ = projectGLJIDMap[glj.projectGLJID]; glj.tenderQuantity = gljUtil.getRationGLJTenderQuantity(glj, ration, decimalInfo.glj.quantity, scMathUtil, pGLJ); return glj.rationID === ration.ID; }); ration.rationGLJList = gljUtil.sortRationGLJ(ration.rationGLJList); }); // 获取暂估价材料数据,getData原始数据evaluate_list.datas里的数据缺少一些价格数据,需要调用额外接口 tenderDetail.evaluateMaterialData = configMaterialObj.getEvaluateMaterialDatas(projectGLJList, evaluateList, decimalInfo); // 获取评标材料数据 tenderDetail.bidMaterialData = configMaterialObj.getBidMaterialDatas(projectGLJList, bidEvaluationList, decimalInfo); } /** * 根据各自费用定额的文件结构,导出文件 * 每个费用定额可能导出的结果文件都不同 * 比如广东18需要将一个建设项目文件,多个单位工程文件打包成一个zip文件。重庆18就没这种要求 * @param {Array} extractData - 提取的数据 * @param {Function} saveAsFunc - 各自费用定额的导出方法,适应不同接口需要不同的最终文件形式 */ async function exportFile(extractData, saveAsFunc) { // 获取文件数据 const fileData = extractData.map(extractObj => { // 转换成xml字符串 let xmlStr = _toXMLStr([extractObj.data]); // 加上xml声明 xmlStr = `${xmlStr}`; // 格式化 xmlStr = _formatXml(xmlStr); const blob = new Blob([xmlStr], { type: 'text/plain;charset=utf-8' }); return { blob: blob, exportKind: extractObj.exportKind, fileName: extractObj.fileName }; }); if (!saveAsFunc) { return fileData; } // 导出 await saveAsFunc(fileData); } /** * 默认的通用导出文件方法:一个文件数据对应一个xml文件(变更后缀) * @param {Array} fileData - 默认的通用导出文件方法:一个文件数据对应一个xml文件(变更后缀) * @return {Void} */ async function defaultSaveAs(fileData) { fileData.forEach(fileItem => saveAs(fileItem.blob, fileItem.fileName)); } return { CONFIG, CACHE, UTIL, Element, extractExportData, defaultSaveAs, exportFile, }; })();