|
@@ -23,6 +23,8 @@ const billType ={
|
|
|
BILL:4,//清单
|
|
|
BX:5//补项
|
|
|
};
|
|
|
+//上传的09表、广联达表
|
|
|
+const uploadType = {lj: 'lj', gld: 'gld'};
|
|
|
// 上传控件
|
|
|
const multiparty = require("multiparty");
|
|
|
const fs = require("fs");
|
|
@@ -226,6 +228,12 @@ module.exports = {
|
|
|
if (file.headers['content-type'] === undefined || allowHeader.indexOf(file.headers['content-type']) < 0) {
|
|
|
throw '不支持该类型';
|
|
|
}
|
|
|
+ //导入表类型(09表lj、广联达gld)
|
|
|
+ const fileType = fields.fileType !== undefined && fields.fileType.length > 0 ? fields.fileType[0] : uploadType.lj;
|
|
|
+ //广联达表始终插入到分部分项,将文件名重命名为分部分项触发导入到分部分项部分
|
|
|
+ if(fileType === uploadType.gld){
|
|
|
+ file.originalFilename = '广联达分部分项工程';
|
|
|
+ }
|
|
|
// 重命名文件名
|
|
|
uploadFullName = uploadOption.uploadDir + '/' + file.originalFilename;
|
|
|
fs.renameSync(file.path, uploadFullName);
|
|
@@ -234,13 +242,40 @@ module.exports = {
|
|
|
if (sheet[0] === undefined || sheet[0].data === undefined) {
|
|
|
throw 'excel没有对应数据';
|
|
|
}
|
|
|
- //导入的数据是否含有固定行(分部分项、施工技术措施项目、施工组织措施项目,通过文件名判断)
|
|
|
+ //获取表的列设置确定导入的格式是否合法(09、广联达)
|
|
|
+ //console.log(sheet[0].data);
|
|
|
+ let colMapping = getColMapping(sheet[0].data);
|
|
|
+ console.log(fileType);
|
|
|
+ console.log(`colMapping`);
|
|
|
+ console.log(colMapping);
|
|
|
+ console.log(`sheet[0].data`);
|
|
|
+ console.log(sheet[0].data);
|
|
|
+ if(!isValidSheet(colMapping, fileType)){
|
|
|
+ throw `excel数据格式错误`;
|
|
|
+ }
|
|
|
+
|
|
|
+ //导入的数据是否含有固定行(分部分项、施工技术措施项目、施工组织措施项目,通过文件名判断)、确定导入位置
|
|
|
let flag = getImportFlag(file.originalFilename);
|
|
|
if(!flag){
|
|
|
throw 'excel数据错误';
|
|
|
}
|
|
|
let fixedBill = await billsData.model.findOne({projectID: projectID, 'flags.flag': flag, deleteInfo: null});
|
|
|
let insertFixedBill = null;
|
|
|
+
|
|
|
+ let vData = getValidImportData(colMapping, sheet[0].data, fixedBill);
|
|
|
+ console.log(`vData`);
|
|
|
+ console.log(vData);
|
|
|
+ for(let rData of vData){
|
|
|
+ let t = {};
|
|
|
+ t.serialNo = rData[colMapping.serialNo];
|
|
|
+ t.code = rData[colMapping.code];
|
|
|
+ t.name = rData[colMapping.name];
|
|
|
+ t.itemCharacterText = rData[colMapping.itemCharacterText];
|
|
|
+ t.unit = rData[colMapping.unit];
|
|
|
+ t.quantity = rData[colMapping.quantity];
|
|
|
+ console.log(t);
|
|
|
+ }
|
|
|
+ // throw 'test';
|
|
|
//导入xx措施项目,若不存在此固定清单,则先插入相关固定清单
|
|
|
if(!fixedBill){
|
|
|
//分部分项工程(不可删除)应存在
|
|
@@ -277,7 +312,12 @@ module.exports = {
|
|
|
stdCharacters = await stdBillCharacterModel.find({billsLibId: billsLibId, deleted: false});
|
|
|
}
|
|
|
//将excel数据转换成清单树结构数据
|
|
|
- let insertDatas = parseToBillData(getValidImportData(sheet[0].data, fixedBill), getColMapping(sheet[0].data), fixedBill, projectID, {stdBills: stdBills, stdJobs: stdJobs, stdCharacters: stdCharacters});
|
|
|
+ let insertDatas = parseToBillData(getValidImportData(colMapping, sheet[0].data, fixedBill), colMapping, fixedBill, projectID, {stdBills: stdBills, stdJobs: stdJobs, stdCharacters: stdCharacters});
|
|
|
+ console.log(`insertDatas`);
|
|
|
+ console.log(insertDatas);
|
|
|
+ if(insertDatas.length === 0){
|
|
|
+ throw 'excel无有效数据';
|
|
|
+ }
|
|
|
//删除相关数据
|
|
|
let deleteDatas = await billsData.deepDeleteBill([fixedBill], req.session.sessionUser.id);
|
|
|
//新增清单数据
|
|
@@ -296,7 +336,8 @@ module.exports = {
|
|
|
fs.unlink(uploadFullName);
|
|
|
}
|
|
|
responseData.err = 1;
|
|
|
- responseData.msg = error;
|
|
|
+ console.log(error);
|
|
|
+ responseData.msg = typeof error === 'object' ? '上传失败' : error;
|
|
|
res.json(responseData);
|
|
|
}
|
|
|
|
|
@@ -305,30 +346,85 @@ module.exports = {
|
|
|
|
|
|
};
|
|
|
|
|
|
+//是否是有效的表头列格式,只要含有各表需要的列就行,不严格控制多少列
|
|
|
+function isValidSheet(colMapping, fileType){
|
|
|
+ //09表:序号、项目编码、项目名称、项目特征、计量单位、工程量、金额
|
|
|
+ let isValid = true;
|
|
|
+ function hasField(field, all){
|
|
|
+ for(let i of all){
|
|
|
+ if(field === i){
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ let needFields;
|
|
|
+ if(fileType === uploadType.lj){
|
|
|
+ needFields = ['serialNo', 'code', 'name', 'money'];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ needFields = ['serialNo', 'code', 'name', 'itemCharacterText', 'unit', 'quantity', 'quantityDetail', 'feeDetail'];
|
|
|
+ }
|
|
|
+ let hasFieldCount = 0;
|
|
|
+ for(let attr in colMapping){
|
|
|
+ if(hasField(attr, needFields)){
|
|
|
+ hasFieldCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return hasFieldCount === needFields.length;
|
|
|
+ //广联达表:序号、项目编码、项目名称、项目特征、计量单位、工程量、工程量明细、费用明细
|
|
|
+}
|
|
|
+
|
|
|
//提取excel表头列对应数据
|
|
|
function getColMapping(sheetData){
|
|
|
//获取表头
|
|
|
- let headRow = [];
|
|
|
- for(let rData of sheetData) {
|
|
|
- if (rData[0] && rData[0].toString().replace(/\s/g, '') === '序号') {
|
|
|
- headRow = rData;
|
|
|
- break;
|
|
|
+ function getHeadRow(sheetData){
|
|
|
+ for(let rData of sheetData) {
|
|
|
+ //寻找含有序号的行,认作表头行
|
|
|
+ for(let cData of rData){
|
|
|
+ if (cData && cData.toString().replace(/\s/g, '') === '序号') {
|
|
|
+ headRow = rData;
|
|
|
+ return rData;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+ return [];
|
|
|
}
|
|
|
- //获取表头列与列号对应关系
|
|
|
+ let headRow = getHeadRow(sheetData);
|
|
|
+ //获取需要的表头列与列号对应关系
|
|
|
let colMapping = {};
|
|
|
for(let c = 0; c < headRow.length; c++){
|
|
|
if(headRow[c]){
|
|
|
headRow[c] = headRow[c].toString().replace(/\s/g, '');
|
|
|
- switch(headRow[c]){
|
|
|
- case '序号': colMapping.serialNo = c; break;
|
|
|
- case '项目编码': colMapping.code = c; break;
|
|
|
- case '项目名称': colMapping.name = c; break;
|
|
|
- case '项目特征': colMapping.itemCharacterText = c; break;
|
|
|
- case '计量单位': colMapping.unit = c; break;
|
|
|
- case '工程量': colMapping.quantity = c; break;
|
|
|
+ //重复的,只取第一个
|
|
|
+ console.log(headRow[c]);
|
|
|
+ if(headRow[c] === '序号' && colMapping.serialNo === undefined){
|
|
|
+ colMapping.serialNo = c;
|
|
|
+ }
|
|
|
+ else if((headRow[c] === '编码' || headRow[c] === '项目编码') && colMapping.code === undefined){
|
|
|
+ colMapping.code = c;
|
|
|
+ }
|
|
|
+ else if((headRow[c] === '名称' || headRow[c] === '项目名称') && colMapping.name === undefined){
|
|
|
+ colMapping.name = c;
|
|
|
+ }
|
|
|
+ else if((headRow[c] === '特征' || headRow[c] === '项目特征') && colMapping.itemCharacterText === undefined){
|
|
|
+ colMapping.itemCharacterText = c;
|
|
|
+ }
|
|
|
+ else if((headRow[c] === '单位' || headRow[c] === '计量单位') && colMapping.unit === undefined){
|
|
|
+ colMapping.unit = c;
|
|
|
+ }
|
|
|
+ else if((headRow[c] === '工程量' || headRow[c] === '项目工程量') && colMapping.quantity === undefined){
|
|
|
+ colMapping.quantity = c;
|
|
|
+ }
|
|
|
+ else if(headRow[c].includes('金额') && colMapping.money === undefined){
|
|
|
+ colMapping.money = c;
|
|
|
+ }
|
|
|
+ else if(headRow[c] === '工程量明细' && colMapping.quantityDetail === undefined){
|
|
|
+ colMapping.quantityDetail = c;
|
|
|
+ }
|
|
|
+ else if(headRow[c] === '费用明细' && colMapping.feeDetail === undefined){
|
|
|
+ colMapping.feeDetail = c;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
}
|
|
|
return colMapping;
|
|
@@ -342,12 +438,39 @@ function rowExistData(rowData){
|
|
|
return false;
|
|
|
}
|
|
|
//提取excel表数据中的有效数据(去表头表尾,提取其中的excel数据)(根据fixedBill获取栏头占行数)
|
|
|
-function getValidImportData(sheetData, fixedBill){
|
|
|
+function getValidImportData(colMapping, sheetData, fixedBill){
|
|
|
let withingD = false;
|
|
|
let validData = [];
|
|
|
+ function isHead(rData){
|
|
|
+ return rData[colMapping.serialNo] && rData[colMapping.serialNo].toString().replace(/\s/g, '') === '序号';
|
|
|
+ }
|
|
|
+ function isTail(rData){
|
|
|
+ for(let cData of rData){
|
|
|
+ if(cData){
|
|
|
+ let trimCData = cData.toString().replace(/\s/g, '');
|
|
|
+ if(trimCData === '本页小计' || trimCData === '本页小计'){
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
for(let r = 0; r < sheetData.length; r++){
|
|
|
let rData = sheetData[r];
|
|
|
- if(rData[0]){
|
|
|
+ if(isHead(rData)){
|
|
|
+ withingD = true;
|
|
|
+ if(fixedBill.name !== '施工组织措施项目'){
|
|
|
+ r++;
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else if(isTail(rData)){
|
|
|
+ withingD = false;
|
|
|
+ }
|
|
|
+ if(withingD && rowExistData(rData)){
|
|
|
+ validData.push(rData);
|
|
|
+ }
|
|
|
+ /*if(rData[0]){
|
|
|
//首列去空格
|
|
|
rData[0] = rData[0].toString().replace(/\s/g, '');
|
|
|
//表头
|
|
@@ -365,7 +488,7 @@ function getValidImportData(sheetData, fixedBill){
|
|
|
}
|
|
|
if(withingD && rowExistData(rData)){
|
|
|
validData.push(rData);
|
|
|
- }
|
|
|
+ }*/
|
|
|
}
|
|
|
return validData;
|
|
|
}
|
|
@@ -379,6 +502,9 @@ function getImportFlag(sheetName){
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
+function isDef(data){
|
|
|
+ return typeof data !== 'undefined' && data !== null && data !== '';
|
|
|
+}
|
|
|
//excel数据转换成清单数据
|
|
|
function parseToBillData(validData, colMapping, fixedBill, projectID, stdData){
|
|
|
let rst = [];
|
|
@@ -386,17 +512,29 @@ function parseToBillData(validData, colMapping, fixedBill, projectID, stdData){
|
|
|
let preRootID = -1,
|
|
|
preLeafID = -1,
|
|
|
preID = -1;
|
|
|
- //合并了项目特征,且行有数据
|
|
|
+ //去除转义字符
|
|
|
+ function removeESC(data){
|
|
|
+ return isDef(data) ? data.toString().replace(/[\r,\n,\s,\t]/g, '') : data;
|
|
|
+ }
|
|
|
+ //父节点:1.无序号 2有编码
|
|
|
function isRoot(rData){
|
|
|
- return rData[colMapping.itemCharacterText] !== undefined && rData[colMapping.itemCharacterText] === '' && rowExistData(rData);
|
|
|
+ //序号和编码去除转义字符(有的表格单元格看起来是没数据,实际含有\r,\n等数据)
|
|
|
+ let serialNo = removeESC(rData[colMapping.serialNo]);
|
|
|
+ let code = removeESC(rData[colMapping.code]);
|
|
|
+ return !isDef(serialNo) && isDef(code);
|
|
|
}
|
|
|
- //不合并且有序号
|
|
|
+ //子节点:有序号
|
|
|
function isLeaf(rData){
|
|
|
- return (rData[colMapping] === undefined || rData[colMapping] !== '') && rData[colMapping.serialNo] && rData[colMapping.serialNo] !== '';
|
|
|
+ let serialNo = removeESC(rData[colMapping.serialNo]);
|
|
|
+ return isDef(serialNo);
|
|
|
}
|
|
|
- //续数据,上一行数据是有效节点且无序号
|
|
|
+ //续数据:1. 前数据有效 2.无序号 3.无编码 4.有名称或特征
|
|
|
function isExtend(preData, rData){
|
|
|
- return preData && (isRoot(preData) || isLeaf(preData)) && !isRoot(rData) && (rData[colMapping.serialNo] === undefined || rData[colMapping.serialNo] === '');
|
|
|
+ let serialNo = removeESC(rData[colMapping.serialNo]);
|
|
|
+ let code = removeESC(rData[colMapping.code]);
|
|
|
+ let name = rData[colMapping.name];
|
|
|
+ let itemCharacterText = rData[colMapping.itemCharacterText];
|
|
|
+ return isDef(preData) && (isRoot(preData) || isLeaf(preData)) && !isDef(serialNo) && !isDef(code) && (isDef(name) || isDef(itemCharacterText));
|
|
|
}
|
|
|
function getBillType(rData, flag){
|
|
|
if(flag === fixedFlag.CONSTRUCTION_TECH || flag === fixedFlag.CONSTRUCTION_ORGANIZATION){
|
|
@@ -443,11 +581,20 @@ function parseToBillData(validData, colMapping, fixedBill, projectID, stdData){
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- if(!isMatch && excelBill.type === billType.FX){//分项在标准清单中不匹配,则识别为补项
|
|
|
+ if(!isMatch && excelBill.type === billType.FX){//分项不为空,同时在标准清单中不匹配,则识别为补项
|
|
|
excelBill.type = billType.BX;
|
|
|
}
|
|
|
}
|
|
|
for(let r = 0; r < validData.length; r++){
|
|
|
+ /* //序号和编码去除转义字符(有的表格单元格看起来是没数据,实际含有\r,\n等数据)
|
|
|
+ let serialNo = validData[r][colMapping.serialNo];
|
|
|
+ let code = validData[r][colMapping.code];
|
|
|
+ if(isDef(serialNo)){
|
|
|
+ serialNo = removeESC(serialNo);
|
|
|
+ }
|
|
|
+ if(isDef(code)){
|
|
|
+ code = removeESC(code);
|
|
|
+ }*/
|
|
|
let preData = validData[r-1],
|
|
|
rData = validData[r];
|
|
|
if(fixedBill.flags[0].flag == fixedFlag.CONSTRUCTION_TECH && rData[colMapping.name] === '施工技术措施项目'
|
|
@@ -484,8 +631,8 @@ function parseToBillData(validData, colMapping, fixedBill, projectID, stdData){
|
|
|
//set bill data
|
|
|
billIdx[newID] = {
|
|
|
ID: newID, ParentID: pID, NextSiblingID: -1,
|
|
|
- code: rData[colMapping.code] ? rData[colMapping.code] : '',
|
|
|
- name: rData[colMapping.name] ? rData[colMapping.name] : '',
|
|
|
+ code: rData[colMapping.code] ? removeESC(rData[colMapping.code]) : '',
|
|
|
+ name: rData[colMapping.name] ? removeESC(rData[colMapping.name]) : '',
|
|
|
itemCharacterText: rData[colMapping.itemCharacterText] ? rData[colMapping.itemCharacterText] : '',
|
|
|
itemCharacter: [],
|
|
|
jobContentText: '',
|