浏览代码

Merge branch 'master' of http://smartcost.f3322.net:3000/SmartCost/ConstructionCost

chenshilong 7 年之前
父节点
当前提交
44e15ee0ec

+ 176 - 29
modules/main/controllers/bills_controller.js

@@ -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: '',

文件差异内容过多而无法显示
+ 27 - 0
web/building_saas/main/html/main.html


+ 16 - 0
web/building_saas/main/js/views/project_view.js

@@ -1961,6 +1961,16 @@ function canInsertRationNode(selected) {//判断是否能插入定额、量价
     }
 }
 
+//导入类型(09表、广联达)
+const uploadType = {lj: 'lj', gld: 'gld'};
+let fileType = uploadType.lj;
+
+$('#uploadLj').click(function () {
+    fileType = uploadType.lj;
+});
+$('#uploadGld').click(function () {
+   fileType = uploadType.gld;
+});
 
 //选择要导入的excel文件
 $('#customFile').change(function () {
@@ -1968,6 +1978,7 @@ $('#customFile').change(function () {
     if(file.files.length > 0){
         $('.custom-file-label').text(`${file.files[0].name} 准备导入上传`);
         $('#uploadAlert').hide();
+        console.log(file.files);
     }
     else{
         $('.custom-file-label').text(`请选择上传文件`);
@@ -1988,6 +1999,8 @@ $('#uploadConfirm').click(function () {
         if(!projectID || projectID <= 0){
             throw '项目数据出错';
         }
+        //导入表类型(09lj、广联达gld)
+        formData.append('fileType', fileType);
         formData.append('projectID', projectID);
         //要去匹配的清单库(第一个)
         let matchBillLibId = projectInfoObj.projectInfo.engineeringInfo.bill_lib.length > 0 ? projectInfoObj.projectInfo.engineeringInfo.bill_lib[0].id : null;
@@ -2012,6 +2025,9 @@ $('#uploadConfirm').click(function () {
                     $('#import').modal("hide");
                     //更新前端
                     doAfterImport(response.data);
+                    if($.bootstrapLoading.isLoading()){
+                        $.bootstrapLoading.end();
+                    }
                 } else {
                     const message = response.msg !== undefined ? response.msg : '上传失败!';
                     $.bootstrapLoading.end();

+ 8 - 1
web/common/html/header.html

@@ -8,8 +8,15 @@
         <li class="nav-item">
             <a class="nav-link" href="#" aria-expanded="false" data-toggle="modal" data-target="#poj-set"><i class="fa fa-cube"></i> 项目属性</a>
         </li>
-        <li class="nav-item">
+        <!--<li class="nav-item">
             <a class="nav-link" href="#import" data-toggle="modal" data-target="#import"><i class="fa fa-cloud-upload"></i> 导入</a>
+        </li>-->
+        <li class="nav-item dropdown">
+            <a class="nav-link dropdown-toggle" href="javascript:void(0);" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa  fa-cloud-upload"></i> 导入</a>
+            <div class="dropdown-menu">
+                <a id="uploadLj" class="dropdown-item" href="#import" data-toggle="modal" data-target="#import">导入09表Excel清单</a>
+                <a id="uploadGld" class="dropdown-item" href="#import" data-toggle="modal" data-target="#import">导入广联达算量Excel清单</a>
+            </div>
         </li>
         <li class="nav-item dropdown">
             <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-wrench"></i> 工具</a>