/** * * * @author Zhong * @date 2019/6/28 * @version */ 'use strict'; const importXMLBase = (() => { //人材机调整法 const AdjustType = { info: 'priceInfo', //造价信息差额调整法 coe: 'priceCoe' //价格指数调整法 }; const CONFIG = Object.freeze({ AdjustType }); // xml字符实体 const XMLEntity = { ' ': 'escape{space}', ' ': 'escape{simpleSpace}', ' ': 'escape{tab}', ' ': 'escape{simpleTab}', ' ': 'escape{return}', ' ': 'escape{simpleReturn}', '�A;': 'escape{newLine}', ' ': 'escape{simpleNewLine}', '<': 'escape{less}', '>': 'escape{greater}', '&': 'escape{and}', '"': 'escape{quot}', ''': 'escape{apos}' }; // 避免字符实体进行转义。原文本中含有xml字符实体,转换为其他字符。 function escapeXMLEntity(str) { for (const [key, value] of Object.entries(XMLEntity)) { str = str.replace(new RegExp(key, 'g'), value); } return str; } // 将文本还原为字符实体 function restoreXMLEntity(str) { for (const [key, value] of Object.entries(XMLEntity)) { str = str.replace(new RegExp(value, 'g'), key); } return str; } /* * 读取文件转换为utf-8编码的字符串 * @param {Blob}file * @return {Promise} * */ function readAsTextSync(file) { return new Promise((resolve, reject) => { const fr = new FileReader(); fr.readAsText(file); // 默认utf-8,如果出现乱码,得看导入文件是什么编码 fr.onload = function () { resolve(this.result); }; fr.onerror = function () { reject('读取文件失败,请重试。'); } }); } /* * 根据字段数组获得所要字段的值 eg: 要获取标段下的单项工程: ['标段', '单项工程']; * @param {Object}source 源数据 * {Array}fields 字段数组 * @return {String} * @example getValue(source, ['标段', '_文件类型']) * */ function getValue(source, fields) { let cur = source; for (const field of fields) { if (!cur[field]) { return ''; } cur = cur[field]; } return cur || ''; } // 获取数据类型 function _plainType(v) { return Object.prototype.toString.call(v).slice(8, -1); } /* * 获取某字段的值,强制返回数组 * @param {Object}source 数据源 * {Array}fields 取的字段 * @return {Array} * @example arrayValue(source, ['标段', '单项工程']) * */ function arrayValue(source, fields) { let target = getValue(source, fields); if (_plainType(target) === 'Object') { target = [target]; } else if (_plainType(target) !== 'Array') { target = [] } return target; } // 获取费用 function getFee(fees, fields) { if (!Array.isArray(fees) || !fees.length) { return '0'; } const feeData = fees.find(fee => fee.fieldName === fields[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) { Object.assign(sameKindFee, feeB); } else { feesA.push(feeB); } }); 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; } // 获取布尔型的数据 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(二维数组,数组里的成员由需要取的字段组成) * eg: ['组织措施分类'], ['公式计算措施项'],该层数据可能由组织措施分类 或 公式计算措施项组成 (同层不可同时存在) * {Function} 获得源数据后,需要提取的数据方法 * @return {Array} * */ function getItemsRecur(src, fields, extractFuc) { let itemsSrc = []; let curField = ['']; for (const field of fields) { itemsSrc = arrayValue(src, field); if (itemsSrc.length) { curField = field; break; } } return itemsSrc.map(itemSrc => { const obj = extractFuc(itemSrc, curField); obj.items = getItemsRecur(itemSrc, fields, extractFuc); return obj; }); } // 递归获取相关数据,与上方的方法不同的点在:同层可能出现不同节点,上方方法暂时不取消(防止bug) // fields内字段的顺序即决定了提取数据类型的顺序,如fields = [['gruop'], ['item']],则提取的数据同层中group数据在item数据之前 function extractItemsRecur(src, fields, extractFuc) { const rst = []; for (const field of fields) { const itemsSrc = arrayValue(src, field); if (itemsSrc.length) { const items = itemsSrc.map(itemSrc => { const obj = extractFuc(itemSrc, field); obj.items = extractItemsRecur(itemSrc, fields, extractFuc); return obj; }); rst.push(...items); } } return rst; } /* * 转换计算基数 * 1.有子项数据,则清空基数 * 2.行代号引用转换为ID引用 * 3.对应字典代号转换,对应字典里找不到则设置成金额 * @param {Array}billsData 清单数据 * {Object}CalcBaseMap 基数映射 * @return {void} * */ function transformCalcBase(billsData, CalcBaseMap) { //行代号 - ID映射 let rowCodeMap = {}; billsData.forEach(data => { if (data.rowCode) { rowCodeMap[data.rowCode] = data.ID; } }); for (let bills of billsData) { if (!bills.calcBase) { continue; } let sub = billsData.find(data => data.ParentID === bills.ID); //有子项数据,则清空基数,费率 if (sub) { bills.calcBase = ''; bills.feeRate = ''; continue; } //提取基数 bills.calcBase = bills.calcBase.replace(/\s/g, ''); let bases = bills.calcBase.split(/[\+\-\*\/]/g); //提取操作符 let oprs = bills.calcBase.match(/[\+\-\*\/]/g); //转换后的基数 let newBase = []; let illegal = false; //不合法 for (let base of bases) { if (rowCodeMap[base]) { //行引用 newBase.push(`@${rowCodeMap[base]}`); } else if (CalcBaseMap[base]) { //基数字典 newBase.push(CalcBaseMap[base]); } else { //都没匹配到,说明软件无法识别此基数 illegal = true; break; } }; if (illegal) { let fee = getFee(bills.fees, ['common', 'totalFee']); let 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; } } } // 获取必要的清单模板 async function getNeedfulTemplate(templateLibID) { return await ajaxPost('/template/bills/api/getNeedfulTemplate', { templateLibID }); } const UTIL = Object.freeze({ escapeXMLEntity, restoreXMLEntity, getValue, arrayValue, getFee, mergeFees, assignAttr, setTreeData, mergeDataRecur, getFlag, getBool, getItemsRecur, extractItemsRecur, readAsTextSync, }); // 获取接受上传的文件类型正则表达式 function getAcceptReg(accepts) { const acceptsExp = accepts.reduce((acc, cur, index) => { return acc + `${index ? '|' : ''}\\${cur}`; }, ''); return new RegExp(`(${acceptsExp})`, 'i'); } return { CONFIG, UTIL, getAcceptReg, } })();