123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068 |
- 'use strict';
- /**
- *
- *
- * @author Mai
- * @date
- * @version
- */
- const _ = require('lodash');
- const colDefineType = {
- match: 1,
- pos: 2,
- };
- const mainReg = /^(GD*)?G?(\d\d)*\d$/ig;
- const subReg = /^(GD*)?G?[A-Z]{2}(\d\d)+$/i;
- const gdXmjPartReg = /^(GD*)?G?/;
- const specCode106 = {
- code: ['102', '103', '104'],
- reg: /^(GD*)?G?106/i
- };
- const specCode109 = {
- code: ['102', '103', '104', '105', '106', '107', '108'],
- reg: /^(GD*)?G?109/i
- };
- const aeUtils = {
- toNumber: function (value) {
- let num = _.toNumber(value);
- return _.isNaN(num) ? null : num;
- },
- checkColHeader: function (row, colHeaderMatch) {
- const colsDef = {};
- for (const iCol in row) {
- const text = row[iCol];
- if (text === null) continue;
- for (const header in colHeaderMatch) {
- const match = colHeaderMatch[header];
- switch (match.type) {
- case colDefineType.match:
- if (match.value.indexOf(text) >= 0) {
- colsDef[header] = iCol;
- }
- break;
- case colDefineType.pos:
- for (const v of match.value) {
- if (text.indexOf(v) >= 0) {
- colsDef[header] = iCol;
- break;
- }
- }
- break;
- }
- }
- }
- return colsDef;
- },
- isMatch11(tempData) {
- return _.find(tempData, x => {
- return x.code.indexOf('-') > 0;
- })
- },
- check18MainCode(code) {
- const splitPos = code.indexOf('-');
- if (splitPos < 0) {
- return !!code.match(mainReg);
- } else {
- const codeArr = code.split('-');
- if (codeArr.length > 3) return false;
- for (const codePart of codeArr) {
- if (!codePart || !codePart.match(mainReg)) return false;
- }
- return true;
- }
- },
- isMatch18(tempData) {
- const checkCode = this.check18MainCode;
- return _.every(tempData, x => {
- return !x.code || checkCode(x.code);
- // return !x.code || !!x.code.match(mainReg);
- });
- }
- };
- class ImportBaseTree {
- /**
- * 构造函数
- * @param {Array} tempData - 清单模板数据
- */
- constructor (tempData, ctx, setting) {
- this.ctx = ctx;
- if (ctx.tender) {
- this.mid = ctx.tender.id;
- this.decimal = ctx.tender.info.decimal;
- this.precision = ctx.tender.info.precision;
- } else if (ctx.budget) {
- this.mid = ctx.budget.id;
- this.decimal = { up: 2, tp: 2};
- this.precision = {
- other: { value: 2 },
- };
- }
- this.setting = setting;
- // 常量
- this.splitChar = '-';
- // 索引
- // 以code为索引
- this.codeNodes = {};
- this.items = [];
- this.roots = [];
- this.pos = [];
- this.tempData = [];
- // 以id为索引
- this.nodes = {};
- // 缓存
- this.finalNode = null;
- this.finalPrecision = null;
- this.finalXmjNode = null;
- this.keyNodeId = 1;
- this._loadTemplateTree(tempData);
- }
- /**
- * 加载 清单模板
- * @param {Array} data - 模板数据
- * @private
- */
- _loadTemplateTree(data) {
- const self = this;
- let loadCodeNodes = true;
- for (const node of data) {
- node[this.setting.kid] = node.template_id;
- node[this.setting.pid] = node.pid;
- node.id = this.ctx.app.uuid.v4();
- delete node.pid;
- if (node.code && loadCodeNodes) {
- this.codeNodes[node.code] = node;
- }
- if (node.code === '3') {
- loadCodeNodes = false;
- }
- this.items.push(node);
- if (node[this.setting.pid] === -1) {
- this.roots.push(node);
- }
- if (node[this.setting.kid] >= this.keyNodeId) {
- this.keyNodeId = node[this.setting.kid] + 1;
- }
- this.tempData.push(node);
- this.nodes[node[this.setting.kid]] = node;
- }
- for (const node of this.items) {
- node[this.setting.mid] = this.mid;
- node.children = this.items.filter(function (i) {
- return i[self.setting.pid] === node[self.setting.kid];
- });
- }
- }
- /**
- * 根据 编号、名称 查找模板节点
- * @param {Object} node - 要查找的节点
- * @returns {*}
- */
- findTempData(node) {
- return this.tempData.find(function (td) {
- return td.code === node.code && td.name === node.name;
- });
- }
- /**
- * 根据 编号 查找 父项项目节
- * @param {String} code - 子项编号
- * @returns {*}
- */
- findXmjParent(code) {
- const codePath = code.split(this.splitChar);
- if (codePath.length > 1) {
- codePath.splice(codePath.length - 1, 1);
- return this.codeNodes[codePath.join(this.splitChar)];
- }
- }
- getPosterity(node) {
- let posterity = [].concat(node.children);
- for (const c of node.children) {
- posterity = posterity.concat(this.getPosterity(c));
- }
- return posterity;
- }
- findGclParent(code, xmj) {
- let parent;
- const codePath = code.split(this.splitChar);
- if (codePath.length > 1) {
- codePath.splice(codePath.length - 1, 1);
- const parentCode = codePath.join(this.splitChar);
- const posterity = this.getPosterity(xmj);
- parent = posterity.find(function (x) {
- return x.b_code === parentCode;
- })
- }
- return parent ? parent : xmj;
- }
- /**
- * 添加 树节点 并完善该节点的树结构
- * @param {Object} node - 添加节点
- * @param {Object} parent - 父项
- * @returns {*}
- */
- addNodeWithParent(node, parent) {
- node.id = this.ctx.app.uuid.v4();
- node[this.setting.kid] = this.keyNodeId;
- this.keyNodeId += 1;
- node[this.setting.pid] = parent ? parent[this.setting.kid] : -1;
- node[this.setting.level] = parent ? parent[this.setting.level] + 1 : 1;
- node[this.setting.order] = parent ? parent.children.length + 1 : this.roots.length + 1;
- node[this.setting.fullPath] = parent ? parent[this.setting.fullPath] + '-' + node[this.setting.kid] : '' + node[this.setting.kid];
- if (parent) {
- parent.children.push(node);
- } else {
- this.roots.push(node);
- }
- this.items.push(node);
- this.codeNodes[node.code] = node;
- this.defineCacheData(node);
- return node;
- }
- /**
- * 定义缓存节点(添加工程量清单、部位明细需使用缓存定位)
- * @param {Object} node - 当前添加的节点
- */
- defineCacheData(node) {
- this.nodes[node[this.setting.kid]] = node;
- this.finalNode = node;
- this.finalPrecision = this.ctx.helper.findPrecision(this.precision, node.unit);
- if (node.code) {
- this.finalXmjNode = node;
- }
- }
- _assignRelaField(temp, node) {
- if (!temp.quantity) temp.quantity = node.quantity;
- if (!temp.dgn_qty1) temp.dgn_qty1 = node.dgn_qty1;
- if (!temp.dgn_qty2) temp.dgn_qty2 = node.dgn_qty2;
- if (!temp.unit_price) temp.unit_price = node.unit_price;
- if (!temp.drawing_code) temp.drawing_code = node.drawing_code;
- if (!temp.memo) temp.memo = node.memo;
- if (!temp.features) temp.features = node.features;
- if (!temp.total_price) temp.total_price = node.total_price;
- }
- /**
- * 添加 项目节
- * @param {Object} node - 项目节
- * @returns {*}
- */
- addXmjNode(node) {
- node.id = this.ctx.app.uuid.v4();
- node[this.setting.mid] = this.mid;
- node.children = [];
- if (node.code.split(this.splitChar).length > 1) {
- const temp = this.findTempData(node);
- if (temp) {
- this.defineCacheData(temp);
- this._assignRelaField(temp, node);
- return temp;
- } else {
- const parent = this.findXmjParent(node.code);
- if (parent) {
- return this.addNodeWithParent(node, parent);
- } else {
- const newNode = this.addNodeWithParent(node, this.codeNodes['1']);
- newNode.error = 1;
- return null;
- }
- }
- } else {
- const n = this.codeNodes[node.code];
- if (!n) {
- return this.addNodeWithParent(node, null);
- } else {
- this.defineCacheData(n);
- this._assignRelaField(n, node);
- return n;
- }
- }
- }
- /**
- * 添加 工程量清单
- * @param {Object} node - 工程量清单
- */
- addGclNode(node) {
- node.id = this.ctx.app.uuid.v4();
- node[this.setting.mid] = this.mid;
- node.pos = [];
- node.children = [];
- if (this.finalXmjNode) {
- const parent = node.b_code ? this.findGclParent(node.b_code, this.finalXmjNode) : this.finalXmjNode;
- return this.addNodeWithParent(node, parent);
- }
- }
- /**
- * 添加 部位明细
- * @param {object} pos - 部位明细
- * @returns {*}
- */
- addPos (pos, strict = false){
- if (this.finalNode && this.finalNode.pos) {
- if (strict) {
- const exist = this.finalNode.pos.find(x => { return x.name === pos.name; });
- if (exist) return exist;
- }
- pos.id = this.ctx.app.uuid.v4();
- pos.lid = this.finalNode.id;
- pos.tid = this.ctx.tender.id;
- pos.add_stage = 0;
- pos.add_stage_order = 0;
- pos.add_times = 0;
- pos.in_time = new Date();
- pos.porder = this.finalNode.pos.length + 1;
- pos.add_user = this.ctx.session.sessionUser.accountId;
- this.finalNode.pos.push(pos);
- this.pos.push(pos);
- pos.sgfh_qty = this.ctx.helper.round(pos.sgfh_qty, this.finalPrecision.value);
- pos.quantity = this.ctx.helper.round(pos.quantity, this.finalPrecision.value);
- return pos;
- }
- }
- /**
- * 第一部分的子节点,顺序重排
- */
- resortFirstPartChildren () {
- const splitChar = this.splitChar;
- const firstPart = this.roots[0];
- firstPart.children.sort(function (a, b) {
- if (!a.code) {
- return 1;
- } else if (!b.code) {
- return -1;
- }
- if (a.error === b.error) {
- const codeA = a.code.split(splitChar);
- const numA = _.toNumber(codeA[codeA.length -1]);
- const codeB = b.code.split(splitChar);
- const numB = _.toNumber(codeB[codeB.length -1]);
- return numA - numB;
- } else if (a.error) {
- return 1
- } else if (b.error) {
- return -1;
- }
- });
- for (const [i, c] of firstPart.children.entries()) {
- c[this.setting.order] = i + 1;
- }
- }
- calculateLeafWithPos () {
- for (const node of this.items) {
- if (node.children && node.children.length > 0) {
- node.unit_price = null;
- node.quantity = null;
- node.total_price = null;
- }
- if (!node.pos || node.pos.length === 0) { continue; }
- node.quantity = this.ctx.helper.sum(_.map(node.pos, 'quantity'));
- if (node.quantity && node.unit_price) {
- node.total_price = this.ctx.helper.mul(node.quantity, node.unit_price, this.decimal.tp);
- } else {
- node.total_price = null;
- }
- }
- }
- }
- class ImportStd18Tree extends ImportBaseTree {
- /**
- * 检查是否是父项
- * @param parent
- * @param code
- * @returns {boolean}
- * @private
- */
- _checkParent(parent, code) {
- const codeNumberPart = code.replace(gdXmjPartReg, '');
- if (!parent.code) return false;
- const numberPart = parent.code.replace(gdXmjPartReg, '');
- if (!numberPart || !codeNumberPart || numberPart.length >= codeNumberPart.length) return false;
- if (parent === '1060501' && code == '1060501-102') console.log(numberPart);
- return code.indexOf(numberPart) === 0 ||
- code.indexOf('G' + numberPart) === 0 ||
- code.indexOf('GD' + numberPart) === 0;
- }
- /**
- * 查找主表项目节父项
- * @param code
- * @returns {*}
- */
- findMainXmjParent(code) {
- const numberPart = code.replace(gdXmjPartReg, '');
- if (numberPart.length <= 1) throw '首层项目节模板中未定义,不支持导入';
- let parent = this.cacheMainXmjNode;
- while (parent) {
- if (this._checkParent(parent, code)) return parent;
- parent = this.nodes[parent[this.setting.pid]];
- }
- return null;
- }
- /**
- * 查找分表项目节父项
- * @param code
- * @returns {*}
- */
- findSubXmjParent(code) {
- let parent = this.cacheSubXmjNode;
- while (parent && parent.is_sub && parent.code.match(subReg)) {
- if (this._checkParent(parent, code)) return parent;
- parent = this.nodes[parent[this.setting.pid]];
- }
- return this.cacheMainXmjNode;
- }
- /**
- * 根据 编号 查找 父项项目节
- * @param {String} code - 子项编号
- * @returns {*}
- */
- findXmjParent(code) {
- if (aeUtils.check18MainCode(code)) {
- //if (code.match(mainReg)) {
- if (!this.cacheMainXmjNode) throw '主表项目节找不到父项';
- return this.findMainXmjParent(code);
- } else if (code.match(subReg)) {
- if (!this.cacheMainXmjNode) throw '分表项目节找不到所属主表项目节';
- return this.findSubXmjParent(code);
- }
- }
- /**
- * 定义缓存节点(添加工程量清单、部位明细需使用缓存定位)
- * @param {Object} node - 当前添加的节点
- */
- defineCacheData(node) {
- super.defineCacheData(node);
- if (node.code) {
- if (aeUtils.check18MainCode(node.code)) {
- //if (node.code.match(mainReg)) {
- node.is_main = true;
- this.cacheMainXmjNode = node;
- this.cacheSubXmjNode = null;
- } else if (node.code.match(subReg)) {
- node.is_sub = true;
- this.cacheSubXmjNode = node;
- }
- if (node.code.match(specCode106.reg)) {
- if (this.cacheSpecMainXmj1 && this.cacheSpecMainXmj1.code.match(specCode109.reg)) {
- this.cacheSpecMainXmj2 = node;
- } else {
- this.cacheSpecMainXmj1 = node;
- }
- } else if (node.code.match(specCode109.reg)) {
- this.cacheSpecMainXmj1 = node;
- this.cacheSpecMainXmj2 = null;
- }
- }
- }
- /**
- * 添加 项目节
- * @param {Object} node - 项目节
- * @returns {*}
- */
- addXmjNode(node) {
- //if (!node.code || (!node.code.match(mainReg) && !node.code.match(subReg))) return null;
- if (!node.code || (!aeUtils.check18MainCode(node.code) && !node.code.match(subReg))) return null;
- node.id = this.ctx.app.uuid.v4();
- node.children = [];
- if ((specCode106.code.indexOf(node.code) >= 0)) {
- if (this.cacheSpecMainXmj2 && this.cacheSpecMainXmj2.code.match(specCode106.reg))
- return this.addNodeWithParent(node, this.cacheSpecMainXmj2);
- if (this.cacheSpecMainXmj1 && this.cacheSpecMainXmj1.code.match(specCode106.reg))
- return this.addNodeWithParent(node, this.cacheSpecMainXmj1);
- }
- if ((specCode109.code.indexOf(node.code) >= 0) &&
- (this.cacheSpecMainXmj1 && this.cacheSpecMainXmj1.code.match(specCode109.reg))) {
- return this.addNodeWithParent(node, this.cacheSpecMainXmj1)
- }
- const temp = this.findTempData(node);
- if (temp) {
- this.defineCacheData(temp);
- this._assignRelaField(temp, node);
- return temp;
- } else {
- const parent = this.findXmjParent(node.code);
- return this.addNodeWithParent(node, parent);
- }
- }
- }
- class AnalysisExcelTree {
- /**
- * 构造函数
- */
- constructor(ctx, setting, needCols) {
- this.ctx = ctx;
- this.setting = setting;
- if (ctx.tender) {
- this.mid = ctx.tender.id;
- this.decimal = ctx.tender.info.decimal;
- this.precision = ctx.tender.info.precision;
- } else if (ctx.budget) {
- this.mid = ctx.budget.id;
- this.decimal = ctx.budget.decimal;
- this.precision = {
- other: { value: ctx.budget.decimal.qty },
- };
- }
- this.colsDef = null;
- this.colHeaderMatch = {
- code: {value: ['项目节编号', '预算项目节'], type: colDefineType.match},
- b_code: {value: ['清单子目号', '清单编号', '子目号'], type: colDefineType.match},
- pos: {value: ['计量单元'], type: colDefineType.match},
- name: {value: ['名称'], type: colDefineType.match},
- unit: {value: ['单位'], type: colDefineType.match},
- deal_qty: {value: ['签约数量'], type: colDefineType.match},
- deal_tp: {value: ['签约金额'], type: colDefineType.match},
- quantity: {value: ['清单数量'], type: colDefineType.match},
- dgn_qty1: {value: ['设计数量1'], type: colDefineType.match},
- dgn_qty2: {value: ['设计数量2'], type: colDefineType.match},
- unit_price: {value: ['单价'], type: colDefineType.match},
- total_price: {value: ['金额', '合价'], type: colDefineType.match},
- drawing_code: {value: ['图号', '图册号'], type: colDefineType.match},
- memo: {value: ['备注'], type: colDefineType.match},
- features: {value: ['项目特征'], type: colDefineType.match},
- };
- this.needCols = needCols || ['code', 'b_code', 'pos'];
- }
- _getNewCacheTree(tempData) {
- // 模板符合11编办规则,使用11编办树
- if (aeUtils.isMatch18(tempData)) {
- return new ImportStd18Tree(tempData, this.ctx, this.setting);
- // 反之使用11编办(未校验模板是否符合,替换注释部分即可实现)
- // } else if (aeUtils.isMatch11(tempData)){
- } else {
- return new ImportBaseTree(tempData, this.ctx, this.setting);
- }
- }
- /**
- * 读取项目节节点
- * @param {Array} row - excel行数据
- * @returns {*}
- * @private
- */
- _loadXmjNode(row) {
- try {
- const node = {};
- node.code = _.trimEnd(this.ctx.helper.replaceReturn(row[this.colsDef.code]));
- node.name = this.ctx.helper.replaceReturn(row[this.colsDef.name]);
- node.unit = this.ctx.helper.replaceReturn(row[this.colsDef.unit]);
- node.dgn_qty1 = aeUtils.toNumber(row[this.colsDef.dgn_qty1]);
- node.dgn_qty2 = aeUtils.toNumber(row[this.colsDef.dgn_qty2]);
- node.deal_tp = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.deal_tp]), this.decimal.tp);
- node.total_price = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.total_price]), this.decimal.tp);
- node.drawing_code = this.ctx.helper.replaceReturn(row[this.colsDef.drawing_code]);
- node.memo = this.ctx.helper.replaceReturn(row[this.colsDef.memo]);
- node.features = row[this.colsDef.features];
- this.ctx.helper.checkDgnQtyPrecision(node);
- return this.cacheTree.addXmjNode(node);
- } catch (error) {
- if (error.stack) {
- this.ctx.logger.error(error);
- } else {
- this.ctx.getLogger('fail').info(JSON.stringify({
- error,
- project: this.ctx.session.sessionProject,
- user: this.ctx.session.sessionUser,
- body: row,
- }));
- }
- return null;
- }
- }
- /**
- * 读取工程量清单数据
- * @param {Array} row - excel行数据
- * @returns {*}
- * @private
- */
- _loadGclNode(row) {
- if (this.filter.filterGcl) return true;
- const node = {};
- node.b_code = this.ctx.helper.replaceReturn(row[this.colsDef.b_code]);
- node.name = this.ctx.helper.replaceReturn(row[this.colsDef.name]);
- node.unit = this.ctx.helper.replaceReturn(row[this.colsDef.unit]);
- const precision = this.ctx.helper.findPrecision(this.precision, node.unit);
- node.deal_qty = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.deal_qty]), precision.value);
- node.quantity = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.quantity]), precision.value);
- node.sgfh_qty = node.quantity;
- node.unit_price = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.unit_price]), this.decimal.up);
- node.drawing_code = this.ctx.helper.replaceReturn(row[this.colsDef.drawing_code]);
- node.memo = this.ctx.helper.replaceReturn(row[this.colsDef.memo]);
- node.features = row[this.colsDef.features];
- node.deal_tp = node.deal_qty && node.unit_price ? this.ctx.helper.mul(node.deal_qty, node.unit_price, this.decimal.tp) : 0;
- if (node.quantity && node.unit_price) {
- node.total_price = this.ctx.helper.mul(node.quantity, node.unit_price, this.decimal.tp);
- } else {
- node.total_price = null;
- }
- if (this.filter.filterZeroGcl && !node.quantity && !node.total_price) return true;
- return this.cacheTree.addGclNode(node);
- }
- /**
- * 读取部位明细数据
- * @param {Array} row - excel行数据
- * @returns {*}
- * @private
- */
- _loadPos(row) {
- if (this.filter.filterPos) return true;
- const pos = {};
- pos.name = this.ctx.helper.replaceReturn(row[this.colsDef.name]);
- pos.quantity = aeUtils.toNumber(row[this.colsDef.quantity]);
- pos.sgfh_qty = pos.quantity;
- pos.drawing_code = this.ctx.helper.replaceReturn(row[this.colsDef.drawing_code]);
- return this.cacheTree.addPos(pos);
- }
- /**
- * 读取数据行
- * @param {Array} row - excel数据行
- * @param {Number} index - 行索引号
- */
- loadRowData(row, index) {
- let result;
- // 含code识别为项目节,含posCode识别为部位明细,其他识别为工程量清单
- if (row[this.colsDef.code]) {
- // 第三部分(编号为'3')及之后的数据不读取
- if (row[this.colsDef.code] === '3') {
- this.loadEnd = true;
- return;
- }
- result = this._loadXmjNode(row)
- } else if (row[this.colsDef.pos]) {
- result = this._loadPos(row);
- } else {
- result = this._loadGclNode(row);
- }
- // 读取失败则写入错误数据 todo 返回前端告知用户?
- if (!result) {
- this.errorData.push({
- serialNo: index,
- data: row,
- });
- }
- }
- /**
- * 读取表头并检查
- * @param {Number} row - Excel数据行
- */
- checkColHeader(row) {
- const colsDef = aeUtils.checkColHeader(row, this.colHeaderMatch);
- let check = true;
- for (const col of this.needCols) {
- if (!colsDef[col]) check = false;
- }
- if (check) this.colsDef = colsDef;
- }
- /**
- * 将excel清单 平面数据 解析为 树结构数据
- * @param {object} sheet - excel清单数据
- * @param {Array} tempData - 新建项目使用的清单模板
- * @returns {ImportBaseTree}
- */
- analysisData(sheet, tempData, filter) {
- this.filter = filter ? filter : {};
- this.colsDef = null;
- this.cacheTree = this._getNewCacheTree(tempData);
- this.errorData = [];
- this.loadEnd = false;
- for (const iRow in sheet.rows) {
- const row = sheet.rows[iRow];
- if (this.colsDef && !this.loadEnd) {
- this.loadRowData(row, iRow);
- } else {
- this.checkColHeader(row);
- }
- }
- return this.cacheTree;
- }
- }
- class ImportGclBaseTree {
- /**
- * 构造函数
- * @param {Array} tempData - 清单模板数据
- */
- constructor (ctx, setting, parent, maxId, defaultData) {
- this.ctx = ctx;
- this.setting = setting;
- if (ctx.tender) {
- this.mid = ctx.tender.id;
- this.decimal = ctx.tender.info.decimal;
- this.precision = ctx.tender.info.precision;
- } else if (ctx.budget) {
- this.mid = ctx.budget.id;
- this.decimal = { up: 2, tp: 2};
- this.precision = {
- other: { value: 2 },
- };
- }
- this.parent = parent;
- this.defaultData = defaultData;
- // 常量
- this.splitChar = '-';
- // 索引
- // 以code为索引
- this.codeNodes = {};
- this.items = [];
- // 缓存
- this.keyNodeId = maxId ? maxId + 1 : 1;
- this.blankParent = null;
- }
- /**
- * 根据 编号 查找 父项项目节
- * @param {String} code - 子项编号
- * @returns {*}
- */
- findParent(code) {
- const codePath = code.split(this.splitChar);
- if (codePath.length > 1) {
- codePath.splice(codePath.length - 1, 1);
- return this.codeNodes[codePath.join(this.splitChar)];
- }
- }
- /**
- * 添加 树节点 并完善该节点的树结构
- * @param {Object} node - 添加节点
- * @param {Object} parent - 父项
- * @returns {*}
- */
- addNodeWithParent(node, parent) {
- parent = parent ? parent : this.parent;
- if (!parent.children) parent.children = [];
- node.id = this.ctx.app.uuid.v4();
- node[this.setting.mid] = this.mid;
- node[this.setting.kid] = this.keyNodeId;
- this.keyNodeId += 1;
- node[this.setting.pid] = parent[this.setting.kid];
- node[this.setting.level] = parent[this.setting.level] + 1;
- node[this.setting.order] = parent.children.length + 1;
- node[this.setting.fullPath] = parent[this.setting.fullPath] + '-' + node[this.setting.kid];
- parent.children.push(node);
- node.children = [];
- if (this.defaultData) _.assignIn(node, this.defaultData);
- this.items.push(node);
- if (!_.isNil(node.b_code) && node.b_code !== '') {
- this.codeNodes[node.b_code] = node;
- } else {
- this.blankParent = node;
- }
- return node;
- }
- /**
- * 添加 项目节
- * @param {Object} node - 项目节
- * @returns {*}
- */
- addNode(node) {
- if (_.isNil(node.b_code) || node.b_code === '') {
- return this.addNodeWithParent(node, null);
- } else if (node.b_code.split(this.splitChar).length > 1) {
- const parent = this.findParent(node.b_code);
- return this.addNodeWithParent(node, parent ? parent : this.blankParent);
- } else {
- return this.addNodeWithParent(node, this.blankParent);
- }
- }
- }
- class AnalysisGclExcelTree {
- /**
- * 构造函数
- */
- constructor(ctx, setting) {
- this.ctx = ctx;
- this.setting = setting;
- if (ctx.tender) {
- this.mid = ctx.tender.id;
- this.decimal = ctx.tender.info.decimal;
- this.precision = ctx.tender.info.precision;
- } else if (ctx.budget) {
- this.mid = ctx.budget.id;
- this.decimal = { up: 2, tp: 2};
- this.precision = {
- other: { value: 2 },
- };
- }
- this.colsDef = null;
- this.colHeaderMatch = {
- b_code: {value: ['编号', '清单编号', '子目号', '子目编号', '清单号'], type: colDefineType.match},
- name: {value: ['名称', '清单名称', '子目名称'], type: colDefineType.match},
- unit: {value: ['单位'], type: colDefineType.pos},
- quantity: {value: ['数量'], type: colDefineType.pos}, // 施工图复核数量
- unit_price: {value: ['单价'], type: colDefineType.pos},
- };
- }
- /**
- * 读取表头并检查
- * @param {Number} row - Excel数据行
- */
- checkColHeader(row) {
- const colsDef = aeUtils.checkColHeader(row, this.colHeaderMatch);
- if (colsDef.b_code) {
- this.colsDef = colsDef;
- }
- }
- loadRowData(row) {
- const node = {};
- node.b_code = this.ctx.helper.replaceReturn(row[this.colsDef.b_code]);
- node.name = this.ctx.helper.replaceReturn(row[this.colsDef.name]);
- if ((_.isNil(node.b_code) || node.b_code === '') && (_.isNil(node.name) || node.name === '')) return node;
- node.unit = this.ctx.helper.replaceReturn(row[this.colsDef.unit]);
- const precision = this.ctx.helper.findPrecision(this.precision, node.unit);
- node.quantity = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.quantity]), precision.value);
- node.unit_price = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.unit_price]), this.decimal.up);
- if (node.quantity && node.unit_price) {
- node.total_price = this.ctx.helper.mul(node.quantity, node.unit_price, this.decimal.tp);
- } else {
- node.total_price = null;
- }
- return this.cacheTree.addNode(node);
- }
- /**
- * 将excel清单 平面数据 解析为 树结构数据
- * @param {object} sheet - excel清单数据
- * @param {Array} parentId - 导入至的节点id
- * @returns {ImportBaseTree}
- */
- analysisData(sheet, parent, maxId, defaultData) {
- try {
- this.colsDef = null;
- this.cacheTree = new ImportGclBaseTree(this.ctx, this.setting, parent, maxId, defaultData);
- this.errorData = [];
- this.loadEnd = false;
- // 识别表头导入
- for (const iRow in sheet.rows) {
- const row = sheet.rows[iRow];
- if (this.colsDef && !this.loadEnd) {
- const result = this.loadRowData(row);
- // 读取失败则写入错误数据
- if (!result) {
- this.errorData.push({ serialNo: iRow, data: row });
- }
- } else {
- this.checkColHeader(row);
- }
- }
- // 固定列导入
- // this.colsDef = {
- // b_code: 0,
- // name: 1,
- // unit: 2,
- // quantity: 3,
- // unit_price: 4
- // };
- // for (let iRow = 1, iLen = sheet.rows.length; iRow < iLen; iRow++) {
- // const row = sheet.rows[iRow];
- // const result = this.loadRowData(row);
- // // 读取失败则写入错误数据 todo 返回前端告知用户?
- // if (!result) {
- // this.errorData.push({
- // serialNo: iRow,
- // data: row,
- // });
- // }
- // }
- return this.cacheTree;
- } catch(err) {
- this.ctx.helper.log(err);
- }
- }
- }
- class AnalysisStageExcelTree extends AnalysisExcelTree {
- /**
- * 构造函数
- */
- constructor(ctx, setting) {
- super(ctx, setting);
- this.mid = ctx.tender.id;
- this.decimal = ctx.tender.info.decimal;
- this.precision = ctx.tender.info.precision;
- this.colsDef = null;
- this.colHeaderMatch = {
- code: {value: ['项目节编号', '预算项目节'], type: colDefineType.match},
- b_code: {value: ['清单子目号', '清单编号', '子目号'], type: colDefineType.match},
- pos: {value: ['计量单元'], type: colDefineType.match},
- name: {value: ['名称'], type: colDefineType.match},
- unit: {value: ['单位'], type: colDefineType.match},
- unit_price: {value: ['单价'], type: colDefineType.match},
- contract_qty: {value: ['本期合同计量|数量'], type: colDefineType.match},
- contract_tp: {value: ['本期合同计量|金额'], type: colDefineType.match},
- deal_dgn_qty1: {value: ['合同|项目节数量1'], type: colDefineType.match},
- deal_dgn_qty2: {value: ['合同|项目节数量2'], type: colDefineType.match},
- c_dgn_qty1: {value: ['变更|项目节数量1'], type: colDefineType.match},
- c_dgn_qty2: {value: ['变更|项目节数量2'], type: colDefineType.match},
- postil: {value: ['本期批注'], type: colDefineType.match}
- };
- this.needCols = ['code', 'b_code', 'pos', 'name', 'unit', 'unit_price', 'contract_qty', 'contract_tp'];
- }
- mergeHeaderRow(iRow, row, subRow, merge) {
- const result = [];
- for (let iCol = 0, iLen = row.length; iCol < iLen; iCol++) {
- const colMerge = merge.find(x => { return x.s.c === iCol && x.s.r === iRow});
- if (colMerge && colMerge.s.c !== colMerge.e.c) {
- let iSubCol = iCol;
- while (iSubCol <= colMerge.e.c) {
- result.push(row[iCol] + '|' + subRow[iSubCol]);
- iSubCol++;
- }
- iCol = colMerge.e.c;
- } else {
- result.push(row[iCol])
- }
- }
- return result;
- }
- /**
- * 读取项目节节点
- * @param {Array} row - excel行数据
- * @returns {*}
- * @private
- */
- _loadXmjNode(row) {
- try {
- const node = {};
- node.code = this.ctx.helper.replaceReturn(this.ctx.helper._.trimEnd(row[this.colsDef.code]));
- node.name = this.ctx.helper.replaceReturn(this.ctx.helper._.trimEnd(row[this.colsDef.name]));
- node.unit = this.ctx.helper.replaceReturn(this.ctx.helper._.trimEnd(row[this.colsDef.unit]));
- const xmj = this.cacheTree.addXmjNode(node);
- if (xmj) {
- xmj.deal_dgn_qty1 = aeUtils.toNumber(row[this.colsDef.deal_dgn_qty1]);
- xmj.deal_dgn_qty2 = aeUtils.toNumber(row[this.colsDef.deal_dgn_qty2]);
- xmj.c_dgn_qty1 = aeUtils.toNumber(row[this.colsDef.c_dgn_qty1]);
- xmj.c_dgn_qty2 = aeUtils.toNumber(row[this.colsDef.c_dgn_qty2]);
- xmj.postil = this.ctx.helper.replaceReturn(row[this.colsDef.postil]);
- this.ctx.helper.checkDgnQtyPrecision(xmj);
- return xmj;
- } else {
- return null;
- }
- } catch (error) {
- if (error.stack) {
- this.ctx.logger.error(error);
- } else {
- this.ctx.getLogger('fail').info(JSON.stringify({
- error,
- project: this.ctx.session.sessionProject,
- user: this.ctx.session.sessionUser,
- body: row,
- }));
- }
- return null;
- }
- }
- /**
- * 读取工程量清单数据
- * @param {Array} row - excel行数据
- * @returns {*}
- * @private
- */
- _loadGclNode(row) {
- if (this.filter.filterGcl) return true;
- const node = {};
- node.b_code = this.ctx.helper.replaceReturn(this.ctx.helper._.trimEnd(row[this.colsDef.b_code]));
- node.name = this.ctx.helper.replaceReturn(this.ctx.helper._.trimEnd(row[this.colsDef.name]));
- node.unit = this.ctx.helper.replaceReturn(this.ctx.helper._.trimEnd(row[this.colsDef.unit]));
- node.unit_price = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.unit_price]), this.decimal.up);
- const precision = this.ctx.helper.findPrecision(this.precision, node.unit);
- node.contract_qty = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.contract_qty]), precision.value);
- if (node.contract_qty && node.unit_price) {
- node.contract_tp = this.ctx.helper.mul(node.contract_qty, node.unit_price, this.decimal.tp);
- } else {
- node.contract_tp = this.ctx.helper.round(aeUtils.toNumber(row[this.colsDef.contract_tp]), this.decimal.tp);
- }
- node.postil = this.ctx.helper.replaceReturn(row[this.colsDef.postil]);
- if (this.filter.filterZeroGcl && !node.contract_qty && !node.contract_tp) return true;
- return this.cacheTree.addGclNode(node);
- }
- /**
- * 读取部位明细数据
- * @param {Array} row - excel行数据
- * @returns {*}
- * @private
- */
- _loadPos(row) {
- if (this.filter.filterPos) return true;
- let pos = {};
- pos.name = this.ctx.helper.replaceReturn(row[this.colsDef.name]);
- pos.quantity = aeUtils.toNumber(row[this.colsDef.contract_qty]);
- pos = this.cacheTree.addPos(pos, true);
- pos.contract_qty = pos.quantity;
- pos.postil = this.ctx.helper.replaceReturn(row[this.colsDef.postil]);
- return pos;
- }
- /**
- * 将excel清单 平面数据 解析为 树结构数据
- * @param {object} sheet - excel清单数据
- * @param {Array} tempData - 新建项目使用的清单模板
- * @returns {ImportBaseTree}
- */
- analysisData(sheet, tempData, filter) {
- this.filter = filter ? filter : {};
- this.colsDef = null;
- this.cacheTree = this._getNewCacheTree(tempData);
- this.errorData = [];
- this.loadEnd = false;
- this.loadBegin = sheet.rows.length;
- for (const [iRow, row] of sheet.rows.entries()) {
- if (this.colsDef && !this.loadEnd) {
- if (iRow < this.loadBegin) continue;
- this.loadRowData(row, iRow);
- } else {
- if (iRow === sheet.rows.length - 1) continue;
- const mergeRow = this.mergeHeaderRow(iRow, row, sheet.rows[iRow + 1], sheet.merge);
- this.checkColHeader(mergeRow);
- if (this.colsDef) this.loadBegin = iRow + 2;
- }
- }
- return this.cacheTree;
- }
- }
- module.exports = { AnalysisExcelTree, AnalysisGclExcelTree, AnalysisStageExcelTree };
|