importStdInterfaceBase.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /**
  2. *
  3. *
  4. * @author Zhong
  5. * @date 2019/6/28
  6. * @version
  7. */
  8. 'use strict';
  9. const importXMLBase = (() => {
  10. //人材机调整法
  11. const AdjustType = {
  12. info: 'priceInfo', //造价信息差额调整法
  13. coe: 'priceCoe' //价格指数调整法
  14. };
  15. const CONFIG = Object.freeze({
  16. AdjustType
  17. });
  18. // xml字符实体
  19. const XMLEntity = {
  20. ' ': 'escape{space}',
  21. ' ': 'escape{simpleSpace}',
  22. '	': 'escape{tab}',
  23. '	': 'escape{simpleTab}',
  24. '
': 'escape{return}',
  25. '
': 'escape{simpleReturn}',
  26. '&#000A;': 'escape{newLine}',
  27. '
': 'escape{simpleNewLine}',
  28. '<': 'escape{less}',
  29. '>': 'escape{greater}',
  30. '&': 'escape{and}',
  31. '"': 'escape{quot}',
  32. ''': 'escape{apos}'
  33. };
  34. // 避免字符实体进行转义。原文本中含有xml字符实体,转换为其他字符。
  35. function escapeXMLEntity(str) {
  36. for (const [key, value] of Object.entries(XMLEntity)) {
  37. str = str.replace(new RegExp(key, 'g'), value);
  38. }
  39. return str;
  40. }
  41. // 将文本还原为字符实体
  42. function restoreXMLEntity(str) {
  43. for (const [key, value] of Object.entries(XMLEntity)) {
  44. str = str.replace(new RegExp(value, 'g'), key);
  45. }
  46. return str;
  47. }
  48. /*
  49. * 读取文件转换为utf-8编码的字符串
  50. * @param {Blob}file
  51. * @return {Promise}
  52. * */
  53. function readAsTextSync(file) {
  54. return new Promise((resolve, reject) => {
  55. const fr = new FileReader();
  56. fr.readAsText(file); // 默认utf-8,如果出现乱码,得看导入文件是什么编码
  57. fr.onload = function () {
  58. resolve(this.result);
  59. };
  60. fr.onerror = function () {
  61. reject('读取文件失败,请重试。');
  62. }
  63. });
  64. }
  65. /*
  66. * 根据字段数组获得所要字段的值 eg: 要获取标段下的单项工程: ['标段', '单项工程'];
  67. * @param {Object}source 源数据
  68. * {Array}fields 字段数组
  69. * @return {String}
  70. * @example getValue(source, ['标段', '_文件类型'])
  71. * */
  72. function getValue(source, fields) {
  73. let cur = source;
  74. for (const field of fields) {
  75. if (!cur[field]) {
  76. return '';
  77. }
  78. cur = cur[field];
  79. }
  80. return cur || '';
  81. }
  82. // 获取数据类型
  83. function _plainType(v) {
  84. return Object.prototype.toString.call(v).slice(8, -1);
  85. }
  86. /*
  87. * 获取某字段的值,强制返回数组
  88. * @param {Object}source 数据源
  89. * {Array}fields 取的字段
  90. * @return {Array}
  91. * @example arrayValue(source, ['标段', '单项工程'])
  92. * */
  93. function arrayValue(source, fields) {
  94. let target = getValue(source, fields);
  95. if (_plainType(target) === 'Object') {
  96. target = [target];
  97. } else if (_plainType(target) !== 'Array') {
  98. target = []
  99. }
  100. return target;
  101. }
  102. // 获取费用
  103. function getFee(fees, fields) {
  104. if (!Array.isArray(fees) || !fees.length) {
  105. return '0';
  106. }
  107. const feeData = fees.find(fee => fee.fieldName === fields[0]);
  108. return feeData && feeData[fields[1]] || '0';
  109. }
  110. // 合并价格
  111. function mergeFees(feesA, feesB) {
  112. if (!feesA) {
  113. return feesB;
  114. }
  115. if (!feesB) {
  116. return [];
  117. }
  118. feesB.forEach(feeB => {
  119. const sameKindFee = feesA.find(feeA => feeA.fieldName === feeB.fieldName);
  120. if (sameKindFee) {
  121. Object.assign(sameKindFee, feeB);
  122. } else {
  123. feesA.push(feeB);
  124. }
  125. });
  126. return feesA;
  127. }
  128. // 将A对象的一部分属性赋值到B对象上
  129. function assignAttr(target, source, attrs, isExcepted = false) {
  130. if (!source || !target) {
  131. return;
  132. }
  133. const sourceAttrs = attrs
  134. ? isExcepted
  135. ? Object.keys(source).filter(attr => !attrs.includes(attr))
  136. : attrs
  137. : Object.keys(source);
  138. sourceAttrs.forEach(attr => {
  139. // 如果是价格,不能简单地覆盖,要合并两个对象的价格
  140. target[attr] = attr === 'fees' ? mergeFees(target[attr], source[attr]) : source[attr];
  141. });
  142. }
  143. // 获取固定ID
  144. function getFlag(data) {
  145. return data.flags && data.flags[0] && data.flags[0].flag || 0;
  146. }
  147. // 获取布尔型的数据
  148. function getBool(v) {
  149. return v === 'true' ? true : false;
  150. }
  151. // 设置成树结构数据
  152. function setTreeData(data, parent, next) {
  153. const defalutID = -1;
  154. data.ID = uuid.v1();
  155. data.ParentID = parent && parent.ID || defalutID;
  156. data.NextSiblingID = next && next.ID || defalutID;
  157. }
  158. // 递归设置树结构数据,并返回设置好的数据,递归items数组
  159. function mergeDataRecur(parent, items) {
  160. const rst = [];
  161. for (let i = 0; i < items.length; i++) {
  162. const cur = items[i];
  163. const next = items[i + 1];
  164. setTreeData(cur, parent, next);
  165. rst.push(cur);
  166. if (cur.items && cur.items.length) {
  167. rst.push(...mergeDataRecur(cur, cur.items));
  168. }
  169. }
  170. return rst;
  171. }
  172. /*
  173. * 递归获取相关数据,eg:获取组织措施清单下的所有子清单,该子清单们可能由分类及公式措施项各种组合组成。(参考调用处)
  174. * @param {Object}src(数据源) {Array}fields(二维数组,数组里的成员由需要取的字段组成)
  175. * eg: ['组织措施分类'], ['公式计算措施项'],该层数据可能由组织措施分类 或 公式计算措施项组成 (同层不可同时存在)
  176. * {Function} 获得源数据后,需要提取的数据方法
  177. * @return {Array}
  178. * */
  179. function getItemsRecur(src, fields, extractFuc) {
  180. let itemsSrc = [];
  181. let curField = [''];
  182. for (const field of fields) {
  183. itemsSrc = arrayValue(src, field);
  184. if (itemsSrc.length) {
  185. curField = field;
  186. break;
  187. }
  188. }
  189. return itemsSrc.map(itemSrc => {
  190. const obj = extractFuc(itemSrc, curField);
  191. obj.items = getItemsRecur(itemSrc, fields, extractFuc);
  192. return obj;
  193. });
  194. }
  195. // 递归获取相关数据,与上方的方法不同的点在:同层可能出现不同节点,上方方法暂时不取消(防止bug)
  196. // fields内字段的顺序即决定了提取数据类型的顺序,如fields = [['gruop'], ['item']],则提取的数据同层中group数据在item数据之前
  197. function extractItemsRecur(src, fields, extractFuc) {
  198. const rst = [];
  199. for (const field of fields) {
  200. const itemsSrc = arrayValue(src, field);
  201. if (itemsSrc.length) {
  202. const items = itemsSrc.map(itemSrc => {
  203. const obj = extractFuc(itemSrc, field);
  204. obj.items = extractItemsRecur(itemSrc, fields, extractFuc);
  205. return obj;
  206. });
  207. rst.push(...items);
  208. }
  209. }
  210. return rst;
  211. }
  212. /*
  213. * 转换计算基数
  214. * 1.有子项数据,则清空基数
  215. * 2.行代号引用转换为ID引用
  216. * 3.对应字典代号转换,对应字典里找不到则设置成金额
  217. * @param {Array}billsData 清单数据
  218. * {Object}CalcBaseMap 基数映射
  219. * @return {void}
  220. * */
  221. function transformCalcBase(billsData, CalcBaseMap) {
  222. //行代号 - ID映射
  223. let rowCodeMap = {};
  224. billsData.forEach(data => {
  225. if (data.rowCode) {
  226. rowCodeMap[data.rowCode] = data.ID;
  227. }
  228. });
  229. for (let bills of billsData) {
  230. if (!bills.calcBase) {
  231. continue;
  232. }
  233. let sub = billsData.find(data => data.ParentID === bills.ID);
  234. //有子项数据,则清空基数,费率
  235. if (sub) {
  236. bills.calcBase = '';
  237. bills.feeRate = '';
  238. continue;
  239. }
  240. //提取基数
  241. bills.calcBase = bills.calcBase.replace(/\s/g, '');
  242. let bases = bills.calcBase.split(/[\+\-\*\/]/g);
  243. //提取操作符
  244. let oprs = bills.calcBase.match(/[\+\-\*\/]/g);
  245. //转换后的基数
  246. let newBase = [];
  247. let illegal = false; //不合法
  248. for (let base of bases) {
  249. if (rowCodeMap[base]) { //行引用
  250. newBase.push(`@${rowCodeMap[base]}`);
  251. } else if (CalcBaseMap[base]) { //基数字典
  252. newBase.push(CalcBaseMap[base]);
  253. } else { //都没匹配到,说明软件无法识别此基数
  254. illegal = true;
  255. break;
  256. }
  257. };
  258. if (illegal) {
  259. let fee = getFee(bills.fees, ['common', 'totalFee']);
  260. let feeRate = bills.feeRate && parseFloat(bills.feeRate) !== 0 ? parseFloat(bills.feeRate) : 0;
  261. if (fee && parseFloat(fee) !== 0 && feeRate) {
  262. bills.calcBase = scMathUtil.roundForObj(parseFloat(fee) * 100 / feeRate, 2);
  263. } else {
  264. bills.calcBase = fee !== '0' ? fee : '';
  265. }
  266. } else {
  267. let newCalcBase = '';
  268. for (let i = 0; i < newBase.length; i++) {
  269. newCalcBase += newBase[i];
  270. if (oprs && oprs[i]) {
  271. newCalcBase += oprs[i];
  272. }
  273. }
  274. bills.calcBase = newCalcBase;
  275. }
  276. }
  277. }
  278. // 获取必要的清单模板
  279. async function getNeedfulTemplate(templateLibID) {
  280. return await ajaxPost('/template/bills/api/getNeedfulTemplate',
  281. { templateLibID });
  282. }
  283. /**
  284. * 根据编码内容解码数据
  285. * @param {Array} unit8ArraySouces - unit8array的数据源
  286. * @param {String} encoding - 编码方式
  287. * @return {Promise Array}
  288. */
  289. async function getDecodedData(unit8ArraySouces, encoding) {
  290. return await ajaxPost('/project/getDecodedData', { unit8ArraySouces, encoding });
  291. }
  292. const UTIL = Object.freeze({
  293. escapeXMLEntity,
  294. restoreXMLEntity,
  295. getValue,
  296. arrayValue,
  297. getFee,
  298. mergeFees,
  299. assignAttr,
  300. setTreeData,
  301. mergeDataRecur,
  302. getFlag,
  303. getBool,
  304. getItemsRecur,
  305. extractItemsRecur,
  306. readAsTextSync,
  307. getDecodedData,
  308. });
  309. // 获取接受上传的文件类型正则表达式
  310. function getAcceptReg(accepts) {
  311. const acceptsExp = accepts.reduce((acc, cur, index) => {
  312. return acc + `${index ? '|' : ''}\\${cur}`;
  313. }, '');
  314. return new RegExp(`(${acceptsExp})`, 'i');
  315. }
  316. return {
  317. CONFIG,
  318. UTIL,
  319. getAcceptReg,
  320. }
  321. })();