/**
* @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) {
if (!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 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';
}
/*
* 根据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 (!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,
getAggregateFee,
getFeeByFlag,
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 `${ele.name}>`;
}
// 拼接成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,
};
})();