|
@@ -0,0 +1,550 @@
|
|
|
|
|
+'use strict';
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ *
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author Zhong
|
|
|
|
|
+ * @date 2019/6/20
|
|
|
|
|
+ * @version
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+const XML_EXPORT_BASE = (() => {
|
|
|
|
|
+
|
|
|
|
|
+ // 属性类型
|
|
|
|
|
+ const TYPE = {
|
|
|
|
|
+ DATE: 1, //日期类型YYYY-MM-DD
|
|
|
|
|
+ DATE_TIME: 2, //日期类型YYY-MM-DDTHH:mm:ss
|
|
|
|
|
+ INT: 3, //整数类型
|
|
|
|
|
+ DECIMAL: 4, //数值类型,不限制小数位数
|
|
|
|
|
+ NUM2: 5, //数值类型2:最多两位小数
|
|
|
|
|
+ BOOL: 6 //布尔型
|
|
|
|
|
+ };
|
|
|
|
|
+ // 空白字符处理
|
|
|
|
|
+ const WHITE_SPACE = {
|
|
|
|
|
+ COLLAPSE: 1 //移除所有空白字符(换行、回车、空格以及制表符会被替换为空格,开头和结尾的空格会被移除,而多个连续的空格会被缩减为一个单一的空格)
|
|
|
|
|
+ };
|
|
|
|
|
+ // 计税方法
|
|
|
|
|
+ const TAX_TYPE = {
|
|
|
|
|
+ '1': '一般计税法',
|
|
|
|
|
+ '2': '简易计税法',
|
|
|
|
|
+ };
|
|
|
|
|
+ // 承包人材料调整类型
|
|
|
|
|
+ const ADJUST_TYPE = {
|
|
|
|
|
+ info: 'priceInfo', // 造价信息差额调整法
|
|
|
|
|
+ coe: 'priceCoe' // 价格指数调整法
|
|
|
|
|
+ };
|
|
|
|
|
+ // 加载数据间隔,减少服务器压力
|
|
|
|
|
+ const TIMEOUT_TIME = 500;
|
|
|
|
|
+ // 导出粒度
|
|
|
|
|
+ const GRANULARITY = {
|
|
|
|
|
+ PROJECT: 1, //导出建设项目
|
|
|
|
|
+ ENGINEERING: 2, //导出单项工程
|
|
|
|
|
+ TENDER: 3 //导出单位工程
|
|
|
|
|
+ };
|
|
|
|
|
+ // 导出的文件类型选项
|
|
|
|
|
+ const EXPORT_KIND = {
|
|
|
|
|
+ Tender: 1, //投标
|
|
|
|
|
+ Bid: 2, //招标
|
|
|
|
|
+ Control: 3 //控制价
|
|
|
|
|
+ };
|
|
|
|
|
+ // 配置项
|
|
|
|
|
+ const CONFIG = Object.freeze({
|
|
|
|
|
+ TYPE,
|
|
|
|
|
+ WHITE_SPACE,
|
|
|
|
|
+ TAX_TYPE,
|
|
|
|
|
+ ADJUST_TYPE,
|
|
|
|
|
+ TIMEOUT_TIME,
|
|
|
|
|
+ GRANULARITY,
|
|
|
|
|
+ EXPORT_KIND
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ /*
|
|
|
|
|
+ * 定义不设置一个Node方法统一进入的原因:模板化比较直观,不分开定义节点的话,调用传参也很麻烦而且不直观。
|
|
|
|
|
+ * 一个节点对应一个构造方法,方便调整配置、方便其他版本开发、接手的人看起来更直观
|
|
|
|
|
+ * @param {String}name 节点名
|
|
|
|
|
+ * {Array}attrs 节点属性数据
|
|
|
|
|
+ * {Array}failList 失败列表
|
|
|
|
|
+ * @return {void}
|
|
|
|
|
+ * */
|
|
|
|
|
+ function Element(name, attrs, failList) {
|
|
|
|
|
+ this.name = name;
|
|
|
|
|
+ let checkData = check(name, attrs);
|
|
|
|
|
+ this.fail = checkData.failHints;
|
|
|
|
|
+ this.attrs = checkData.filterAttrs;
|
|
|
|
|
+ this.children = [];
|
|
|
|
|
+ failList.push(...this.fail);
|
|
|
|
|
+ }
|
|
|
|
|
+ /*
|
|
|
|
|
+ * 检查
|
|
|
|
|
+ * 创建节点时检查节点的数据
|
|
|
|
|
+ * 1.长度限制minLen,maxLen
|
|
|
|
|
+ * 2.值的限制,固定范围:enumeration
|
|
|
|
|
+ * @param {String}eleName 节点名称
|
|
|
|
|
+ * {Array}datas 需要检查的属性数据
|
|
|
|
|
+ * @return {Object} failHints没通过的属性提示 filterAttrs过滤后的属性数据(失败提示在属性是必须的时候才提示,如果该属性失败了,但是是非必要属性,那么该属性不显示)
|
|
|
|
|
+ * */
|
|
|
|
|
+ function check(eleName, datas) {
|
|
|
|
|
+ let rst = {failHints: [], filterAttrs: []};
|
|
|
|
|
+ let 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])))/;
|
|
|
|
|
+ for (let data of datas) {
|
|
|
|
|
+ data.value = typeof data.value === 'undefined' || data.value === null ? '' : String(data.value);
|
|
|
|
|
+ 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.value && !data.minLen && !data.enumeration) { //值为空,且没有限制最小字符数,且没有限制值,则不需判断
|
|
|
|
|
+ rst.filterAttrs.push(data);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ let isFail = false,
|
|
|
|
|
+ tempFail = '';
|
|
|
|
|
+ if (data.minLen && data.value.length < data.minLen){
|
|
|
|
|
+ isFail = true;
|
|
|
|
|
+ tempFail = data.failHint
|
|
|
|
|
+ ? `${data.failHint}字符数不可小于${data.minLen}个。`
|
|
|
|
|
+ :`${eleName}-“${data.name}”字符数不可小于${data.minLen}个。`;
|
|
|
|
|
+ } else if (data.maxLen && data.value.length > data.maxLen) {
|
|
|
|
|
+ isFail = true;
|
|
|
|
|
+ tempFail = data.failHint
|
|
|
|
|
+ ? `${data.failHint}字符数不可大于${data.maxLen}个。`
|
|
|
|
|
+ : `${eleName}-“${data.name}”字符数不可大于${data.maxLen}个。`;
|
|
|
|
|
+ } else if (data.enumeration && !data.enumeration.includes(data.value)) {
|
|
|
|
|
+ isFail = true;
|
|
|
|
|
+ let enumerationHint = data.enumerationHint
|
|
|
|
|
+ ? data.enumerationHint.join(';')
|
|
|
|
|
+ : data.enumeration.join(';');
|
|
|
|
|
+ tempFail = data.failHint
|
|
|
|
|
+ ? `${data.failHint}只能从“${enumerationHint}”中选择。`
|
|
|
|
|
+ : `${eleName}-“${data.name}”只能从“${enumerationHint}”中选择。`;
|
|
|
|
|
+ } else if (data.type && data.type === TYPE.DATE && !dateReg.test(data.value)) {
|
|
|
|
|
+ isFail = true;
|
|
|
|
|
+ tempFail = data.failHint
|
|
|
|
|
+ ? `${data.failHint}日期格式必须是YYYY-MM-DD。`
|
|
|
|
|
+ : `${eleName}-“${data.name}”日期格式必须是YYYY-MM-DD。`;
|
|
|
|
|
+ } else if (data.type && data.type === TYPE.INT && !Number.isInteger(parseFloat(data.value))) {
|
|
|
|
|
+ isFail = true;
|
|
|
|
|
+ tempFail = data.failHint
|
|
|
|
|
+ ? `${data.failHint}必须为整数。`
|
|
|
|
|
+ : `${eleName}-“${data.name}”必须为整数。`;
|
|
|
|
|
+ } else if (data.type && data.type === TYPE.DECIMAL && isNaN(parseFloat(data.value))) {
|
|
|
|
|
+ isFail = true;
|
|
|
|
|
+ tempFail = data.failHint
|
|
|
|
|
+ ? `${data.failHint}必须为数值。`
|
|
|
|
|
+ : `${eleName}-“${data.name}”必须为数值。`;
|
|
|
|
|
+ } else if (data.type && data.type === TYPE.NUM2) {
|
|
|
|
|
+ let v = parseFloat(data.value);
|
|
|
|
|
+ if (isNaN(v)) {
|
|
|
|
|
+ isFail = true;
|
|
|
|
|
+ tempFail = data.failHint
|
|
|
|
|
+ ? `${data.failHint}必须为数值。`
|
|
|
|
|
+ : `${eleName}-“${data.name}”必须为数值。`;
|
|
|
|
|
+ } else if (!data.value.length || (data.value.split('.').length > 1 && data.value.split('.')[1].length > 2)){
|
|
|
|
|
+ isFail = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (data.type && data.type === TYPE.BOOL && !['true', 'false'].includes(String(data.value))) {
|
|
|
|
|
+ isFail = true;
|
|
|
|
|
+ tempFail = data.failHint
|
|
|
|
|
+ ? `${data.failHint}必须为true或false。`
|
|
|
|
|
+ : `${eleName}-“${data.name}”必须为true或false。`;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!isFail || data.required) {
|
|
|
|
|
+ rst.filterAttrs.push(data);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isFail && data.required && tempFail) {
|
|
|
|
|
+ rst.failHints.push(tempFail);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return rst;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 等待一段时间
|
|
|
|
|
+ function setTimeoutSync(handle, time) {
|
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ if (handle && typeof handle === 'function') {
|
|
|
|
|
+ handle();
|
|
|
|
|
+ }
|
|
|
|
|
+ resolve();
|
|
|
|
|
+ }, time);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ function isDef(v) {
|
|
|
|
|
+ return typeof v !== 'undefined' && v !== null;
|
|
|
|
|
+ }
|
|
|
|
|
+ /*
|
|
|
|
|
+ * 从fees数组中获取相关费用
|
|
|
|
|
+ * @param {Array}fees 费用数组
|
|
|
|
|
+ * {String}feeFields 费用字段
|
|
|
|
|
+ * @return {Number}
|
|
|
|
|
+ * @example getFee(source.fees, 'common.totalFee')
|
|
|
|
|
+ * */
|
|
|
|
|
+ function getFee(fees, feeFields) {
|
|
|
|
|
+ if (!Array.isArray(fees)) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ let fields = feeFields.split('.');
|
|
|
|
|
+ let fee = fees.find(data => data.fieldName === fields[0]);
|
|
|
|
|
+ if (!fee) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ return fee[fields[1]] || 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ /*
|
|
|
|
|
+ * 根据key获取对应的基本信息、工程特征数据
|
|
|
|
|
+ * @param {Array}data
|
|
|
|
|
+ * {String}key
|
|
|
|
|
+ * @return {String}
|
|
|
|
|
+ * @example getValueByKey(source.basicInformation, 'projectScale')
|
|
|
|
|
+ * */
|
|
|
|
|
+ function getValueByKey(data, key) {
|
|
|
|
|
+ for (let d of data) {
|
|
|
|
|
+ if (d.key === key) {
|
|
|
|
|
+ return d.value;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (d.items && d.items.length > 0) {
|
|
|
|
|
+ let findData = d.items.find(x => x.key === key);
|
|
|
|
|
+ if (findData) {
|
|
|
|
|
+ return findData.value;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return '';
|
|
|
|
|
+ }
|
|
|
|
|
+ // 数组打平成对象
|
|
|
|
|
+ function arrayToObj(arr) {
|
|
|
|
|
+ let rst = {};
|
|
|
|
|
+ for (let data of arr) {
|
|
|
|
|
+ rst[data.key] = data.value;
|
|
|
|
|
+ }
|
|
|
|
|
+ return rst;
|
|
|
|
|
+ }
|
|
|
|
|
+ //检测层数
|
|
|
|
|
+ //@param {Number}maxDepth(最大深度) {Object}node(需要检测的清单树节点)
|
|
|
|
|
+ //@return {Boolean}
|
|
|
|
|
+ function validDepth(maxDepth, node) {
|
|
|
|
|
+ let nodeDepth = node.depth();
|
|
|
|
|
+ let allNodes = node.getPosterity();
|
|
|
|
|
+ //检测相对深度
|
|
|
|
|
+ for (let n of allNodes) {
|
|
|
|
|
+ let relativeDepth = n.depth() - nodeDepth;
|
|
|
|
|
+ if (relativeDepth > maxDepth) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ //检测唯一性
|
|
|
|
|
+ //@param {Object}constraints(约束池) {All}data(检测的数据) {String}hint(提示已存在的内容) {String}subHint(额外提示,有额外提示时,不用data提示)
|
|
|
|
|
+ //@return {void}
|
|
|
|
|
+ function checkUnique(constraints, failList, data, hint, subHint) {
|
|
|
|
|
+ if (constraints.includes(data)) {
|
|
|
|
|
+ let failHint = subHint
|
|
|
|
|
+ ? `${hint}“${subHint}”已存在`
|
|
|
|
|
+ : `${hint}“${data}”已存在`;
|
|
|
|
|
+ failList.push(failHint);
|
|
|
|
|
+ } else if (data) {
|
|
|
|
|
+ constraints.push(data);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ //根据数据的NextSiblingID进行排序,返回排序后的数组
|
|
|
|
|
+ function sortByNext(datas) {
|
|
|
|
|
+ let target = [],
|
|
|
|
|
+ temp = {};
|
|
|
|
|
+ for (let data of datas) {
|
|
|
|
|
+ temp[data.ID] = {me: data, next: null, prev: null};
|
|
|
|
|
+ }
|
|
|
|
|
+ for (let data of datas) {
|
|
|
|
|
+ let next = temp[data.NextSiblingID] || null;
|
|
|
|
|
+ temp[data.ID].next = next;
|
|
|
|
|
+ if (next) {
|
|
|
|
|
+ next.prev = temp[data.ID];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ let first = null;
|
|
|
|
|
+ for (let data of datas) {
|
|
|
|
|
+ let me = temp[data.ID];
|
|
|
|
|
+ if (!me.prev) {
|
|
|
|
|
+ first = me;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!first) {
|
|
|
|
|
+ return datas;
|
|
|
|
|
+ }
|
|
|
|
|
+ while (first) {
|
|
|
|
|
+ target.push(first.me);
|
|
|
|
|
+ first = first.next;
|
|
|
|
|
+ }
|
|
|
|
|
+ return target;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //转换基数表达式
|
|
|
|
|
+ //1.有子项,则取固定清单对应基数
|
|
|
|
|
+ //2.无子项,有基数,a.优先转换为行代号(不可自身) b.不能转换为行代号则找对应字典
|
|
|
|
|
+ //3.基数中有无法转换的,设为金额
|
|
|
|
|
+ function transformCalcBase(tenderDetail, node, {CalcBaseMap, FlagCalcBaseMap}) {
|
|
|
|
|
+ let expr = node.data.calcBase || '';
|
|
|
|
|
+ if (node.children.length) {
|
|
|
|
|
+ let flag = node.getFlag();
|
|
|
|
|
+ return FlagCalcBaseMap[flag] || '';
|
|
|
|
|
+ }
|
|
|
|
|
+ if (expr) {
|
|
|
|
|
+ let illegal = false;
|
|
|
|
|
+ let normalBase = _getNormalBase(expr),
|
|
|
|
|
+ idBase = _getIDBase(expr);
|
|
|
|
|
+ //普通基数转基数字典
|
|
|
|
|
+ normalBase.forEach(base => {
|
|
|
|
|
+ let replaceStr = CalcBaseMap[base];
|
|
|
|
|
+ //转换成行代号的优先级比较高,进行清单匹配
|
|
|
|
|
+ let flag = FlagCalcBaseMap[base];
|
|
|
|
|
+ if (flag) {
|
|
|
|
|
+ let 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 => {
|
|
|
|
|
+ let id = base.match(/[^@]+/)[0];
|
|
|
|
|
+ let theNode = tenderDetail.mainTree.getNodeByID(id),
|
|
|
|
|
+ rowCode = theNode ? `F${theNode.serialNo() + 1}` : '';
|
|
|
|
|
+ if (!rowCode) {
|
|
|
|
|
+ illegal = true;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ expr = expr.replace(new RegExp(base, 'g'), rowCode);
|
|
|
|
|
+ });
|
|
|
|
|
+ //不合法 返回金额
|
|
|
|
|
+ if (illegal) {
|
|
|
|
|
+ return getFee(node.data.fees, 'common.totalFee');
|
|
|
|
|
+ }
|
|
|
|
|
+ return expr;
|
|
|
|
|
+ }
|
|
|
|
|
+ //获取普通基数: {xxx}
|
|
|
|
|
+ function _getNormalBase(str) {
|
|
|
|
|
+ let reg = /{.+?}/g,
|
|
|
|
|
+ matchs = str.match(reg);
|
|
|
|
|
+ return matchs || [];
|
|
|
|
|
+ }
|
|
|
|
|
+ //获取id引用基数: @xxx-xxx-xx
|
|
|
|
|
+ function _getIDBase(str) {
|
|
|
|
|
+ let reg = /@.{36}/g,
|
|
|
|
|
+ matchs = str.match(reg);
|
|
|
|
|
+ return matchs || [];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ //转换基数说明,根据转换后的基数处理
|
|
|
|
|
+ //1.行引用转换为对应行的名称
|
|
|
|
|
+ //2.基数字典转换为中文
|
|
|
|
|
+ function transformCalcBaseState(tenderDetail, expr, CalcStateMap) {
|
|
|
|
|
+ if (!expr) {
|
|
|
|
|
+ return '';
|
|
|
|
|
+ }
|
|
|
|
|
+ expr = String(expr);
|
|
|
|
|
+ //提取基数
|
|
|
|
|
+ let bases = expr.split(/[\+\-\*\/]/g);
|
|
|
|
|
+ //提取操作符
|
|
|
|
|
+ let oprs = expr.match(/[\+\-\*\/]/g);
|
|
|
|
|
+ //转换后的基数
|
|
|
|
|
+ let newBase = [];
|
|
|
|
|
+ let illegal = false;
|
|
|
|
|
+ for (let base of bases) {
|
|
|
|
|
+ //行引用转换为名称
|
|
|
|
|
+ if (/F\d+/.test(base)) {
|
|
|
|
|
+ let rowCode = base.match(/\d+/)[0],
|
|
|
|
|
+ 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 getCodeSheetData(PMData) {
|
|
|
|
|
+ let curCode = '0';
|
|
|
|
|
+ let sheetData = [];
|
|
|
|
|
+ sheetData.push(getObj(PMData));
|
|
|
|
|
+ PMData.children.forEach(eng => {
|
|
|
|
|
+ sheetData.push(getObj(eng));
|
|
|
|
|
+ eng.children.forEach(tender => {
|
|
|
|
|
+ sheetData.push(getObj(tender));
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ //建设项目父ID设置为-1
|
|
|
|
|
+ if (sheetData.length) {
|
|
|
|
|
+ sheetData[0].ParentID = -1;
|
|
|
|
|
+ sheetData[0].code = '';
|
|
|
|
|
+ }
|
|
|
|
|
+ return sheetData;
|
|
|
|
|
+ function getObj(data) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ collapsed: false,
|
|
|
|
|
+ ID: data.ID,
|
|
|
|
|
+ ParentID: data.ParentID,
|
|
|
|
|
+ NextSiblingID: data.NextSiblingID,
|
|
|
|
|
+ name: data.name,
|
|
|
|
|
+ code: data.code || String(curCode++)
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 从srcNode节点中获取为target实例的节点
|
|
|
|
|
+ function _getNodeFromSrc(srcNode, target) {
|
|
|
|
|
+ if (!srcNode || !srcNode.children || !srcNode.children.length) {
|
|
|
|
|
+ return [];
|
|
|
|
|
+ }
|
|
|
|
|
+ return srcNode.children.filter(node => node instanceof target);
|
|
|
|
|
+ }
|
|
|
|
|
+ // 设置完工程编号后,更新原始数据的工程编号
|
|
|
|
|
+ function setupCode(originalDatas, codeDatas, {Tender, Engineering}) {
|
|
|
|
|
+ originalDatas.forEach(orgData => {
|
|
|
|
|
+ let curIdx = 0;
|
|
|
|
|
+ let engs = _getNodeFromSrc(orgData.data, Engineering);
|
|
|
|
|
+ engs.forEach(eng => {
|
|
|
|
|
+ eng.attrs.find(attr => attr.name === '编号').value = codeDatas[curIdx++];
|
|
|
|
|
+ let tenders = _getNodeFromSrc(eng, Tender);
|
|
|
|
|
+ tenders.forEach(tender => {
|
|
|
|
|
+ tender.attrs.find(attr => attr.name === '编号').value = codeDatas[curIdx++];
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const UTIL = Object.freeze({
|
|
|
|
|
+ isDef,
|
|
|
|
|
+ setTimeoutSync,
|
|
|
|
|
+ getFee,
|
|
|
|
|
+ getValueByKey,
|
|
|
|
|
+ arrayToObj,
|
|
|
|
|
+ validDepth,
|
|
|
|
|
+ checkUnique,
|
|
|
|
|
+ sortByNext,
|
|
|
|
|
+ transformCalcBase,
|
|
|
|
|
+ transformCalcBaseState,
|
|
|
|
|
+ getCodeSheetData,
|
|
|
|
|
+ setupCode
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 开始标签
|
|
|
|
|
+ function _startTag(ele) {
|
|
|
|
|
+ let rst = `<${ele.name}`;
|
|
|
|
|
+ for (let 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 (let 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*?</g, ">\n<");
|
|
|
|
|
+ //调整格式
|
|
|
|
|
+ let rgx = /\n(<(([^\?]).+?)(?:\s|\s*?>|\s*?(\/)>)(?:.*?(?:(?:(\/)>)|(?:<(\/)\2>)))?)/mg;
|
|
|
|
|
+ let nodeStack = [];
|
|
|
|
|
+ let output = text.replace(rgx, function($0, all, name, isBegin, isCloseFull1, isCloseFull2, isFull1, isFull2){
|
|
|
|
|
+ let 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);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ let ret = '\n' + prefix + all;
|
|
|
|
|
+ return ret;
|
|
|
|
|
+ });
|
|
|
|
|
+ let outputText = output.substring(1);
|
|
|
|
|
+ return outputText;
|
|
|
|
|
+ function getPrefix(prefixIndex) {
|
|
|
|
|
+ let span = ' ';
|
|
|
|
|
+ let output = [];
|
|
|
|
|
+ for (let i = 0 ; i < prefixIndex; ++i) {
|
|
|
|
|
+ output.push(span);
|
|
|
|
|
+ }
|
|
|
|
|
+ return output.join('');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 将数据转换为xml文件并导出
|
|
|
|
|
+ async function exportFile(originalDatas) {
|
|
|
|
|
+ let fileDatas = [];
|
|
|
|
|
+ //源数据转换成blob
|
|
|
|
|
+ for (let orgData of originalDatas) {
|
|
|
|
|
+ //转换成xml字符串
|
|
|
|
|
+ let xmlStr = _toXMLStr([orgData.data]);
|
|
|
|
|
+ //加上xml声明
|
|
|
|
|
+ xmlStr = `<?xml version="1.0" encoding="utf-8"?>${xmlStr}`;
|
|
|
|
|
+ //格式化
|
|
|
|
|
+ xmlStr = _formatXml(xmlStr);
|
|
|
|
|
+ let blob = new Blob([xmlStr], {type: 'text/plain;charset=utf-8'});
|
|
|
|
|
+ fileDatas.push({blob: blob, fileName: orgData.fileName});
|
|
|
|
|
+ }
|
|
|
|
|
+ //导出文件
|
|
|
|
|
+ if (fileDatas.length === 1) {
|
|
|
|
|
+ saveAs(fileDatas[0].blob, fileDatas[0].fileName);
|
|
|
|
|
+ } else if (fileDatas.length > 1) { //导出压缩包
|
|
|
|
|
+ let zip = new JSZip();
|
|
|
|
|
+ for (let file of fileDatas) {
|
|
|
|
|
+ zip.file(file.fileName, file.blob, {binary: true});
|
|
|
|
|
+ }
|
|
|
|
|
+ let zipFile = await zip.generateAsync({type: 'blob'});
|
|
|
|
|
+ saveAs(zipFile, '重庆标准交换数据.zip');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ CONFIG,
|
|
|
|
|
+ UTIL,
|
|
|
|
|
+ Element,
|
|
|
|
|
+ exportFile
|
|
|
|
|
+ };
|
|
|
|
|
+})();
|